svelora 3.0.14 → 3.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/dist/components/ConfirmDialog/ConfirmDialog.svelte +167 -0
  2. package/dist/components/ConfirmDialog/ConfirmDialog.svelte.d.ts +10 -0
  3. package/dist/components/ConfirmDialog/confirm.d.ts +9 -0
  4. package/dist/components/ConfirmDialog/confirm.js +24 -0
  5. package/dist/components/ConfirmDialog/confirm.types.d.ts +22 -0
  6. package/dist/components/ConfirmDialog/confirm.types.js +1 -0
  7. package/dist/components/ConfirmDialog/index.d.ts +4 -0
  8. package/dist/components/ConfirmDialog/index.js +2 -0
  9. package/dist/components/Editor/editor.schemas.js +1 -1
  10. package/dist/components/SortableList/SortableList.svelte +54 -0
  11. package/dist/components/SortableList/SortableList.svelte.d.ts +26 -0
  12. package/dist/components/SortableList/index.d.ts +2 -0
  13. package/dist/components/SortableList/index.js +1 -0
  14. package/dist/components/SortableList/sortable-list.types.d.ts +17 -0
  15. package/dist/components/SortableList/sortable-list.types.js +1 -0
  16. package/dist/components/Table/Table.svelte +57 -1
  17. package/dist/components/Table/table.types.d.ts +2 -0
  18. package/dist/components/Table/table.variants.d.ts +34 -0
  19. package/dist/components/Table/table.variants.js +13 -0
  20. package/dist/components/Toast/Toaster.svelte +194 -137
  21. package/dist/components/Toast/index.d.ts +2 -0
  22. package/dist/components/Toast/index.js +1 -0
  23. package/dist/components/Toast/internal/AnimatedIcon.svelte +446 -0
  24. package/dist/components/Toast/internal/AnimatedIcon.svelte.d.ts +15 -0
  25. package/dist/components/Toast/internal/animated-icon.types.d.ts +1 -0
  26. package/dist/components/Toast/internal/animated-icon.types.js +1 -0
  27. package/dist/components/Toast/internal/french-toast/LICENSE.md +21 -0
  28. package/dist/components/Toast/internal/french-toast/components/CheckmarkIcon.svelte +64 -0
  29. package/dist/components/Toast/internal/french-toast/components/CheckmarkIcon.svelte.d.ts +8 -0
  30. package/dist/components/Toast/internal/french-toast/components/ErrorIcon.svelte +74 -0
  31. package/dist/components/Toast/internal/french-toast/components/ErrorIcon.svelte.d.ts +8 -0
  32. package/dist/components/Toast/internal/french-toast/components/LoaderIcon.svelte +28 -0
  33. package/dist/components/Toast/internal/french-toast/components/LoaderIcon.svelte.d.ts +8 -0
  34. package/dist/components/Toast/internal/french-toast/components/ToastBar.svelte +169 -0
  35. package/dist/components/Toast/internal/french-toast/components/ToastBar.svelte.d.ts +20 -0
  36. package/dist/components/Toast/internal/french-toast/components/ToastIcon.svelte +81 -0
  37. package/dist/components/Toast/internal/french-toast/components/ToastIcon.svelte.d.ts +7 -0
  38. package/dist/components/Toast/internal/french-toast/components/ToastMessage.svelte +13 -0
  39. package/dist/components/Toast/internal/french-toast/components/ToastMessage.svelte.d.ts +8 -0
  40. package/dist/components/Toast/internal/french-toast/components/ToastWrapper.svelte +57 -0
  41. package/dist/components/Toast/internal/french-toast/components/ToastWrapper.svelte.d.ts +12 -0
  42. package/dist/components/Toast/internal/french-toast/components/Toaster.svelte +73 -0
  43. package/dist/components/Toast/internal/french-toast/components/Toaster.svelte.d.ts +23 -0
  44. package/dist/components/Toast/internal/french-toast/core/store.svelte.d.ts +15 -0
  45. package/dist/components/Toast/internal/french-toast/core/store.svelte.js +92 -0
  46. package/dist/components/Toast/internal/french-toast/core/toast.d.ts +21 -0
  47. package/dist/components/Toast/internal/french-toast/core/toast.js +67 -0
  48. package/dist/components/Toast/internal/french-toast/core/types.d.ts +58 -0
  49. package/dist/components/Toast/internal/french-toast/core/types.js +4 -0
  50. package/dist/components/Toast/internal/french-toast/core/use-toaster.svelte.d.ts +16 -0
  51. package/dist/components/Toast/internal/french-toast/core/use-toaster.svelte.js +71 -0
  52. package/dist/components/Toast/internal/french-toast/core/utils.d.ts +6 -0
  53. package/dist/components/Toast/internal/french-toast/core/utils.js +25 -0
  54. package/dist/components/Toast/internal/french-toast/toast-context.d.ts +18 -0
  55. package/dist/components/Toast/internal/french-toast/toast-context.js +10 -0
  56. package/dist/components/Toast/internal/notify.d.ts +31 -0
  57. package/dist/components/Toast/internal/notify.js +100 -0
  58. package/dist/components/Toast/internal/toast-icons/ToastError.svelte +8 -0
  59. package/dist/components/Toast/internal/toast-icons/ToastError.svelte.d.ts +4 -0
  60. package/dist/components/Toast/internal/toast-icons/ToastInfo.svelte +8 -0
  61. package/dist/components/Toast/internal/toast-icons/ToastInfo.svelte.d.ts +4 -0
  62. package/dist/components/Toast/internal/toast-icons/ToastLoading.svelte +8 -0
  63. package/dist/components/Toast/internal/toast-icons/ToastLoading.svelte.d.ts +4 -0
  64. package/dist/components/Toast/internal/toast-icons/ToastProcessing.svelte +8 -0
  65. package/dist/components/Toast/internal/toast-icons/ToastProcessing.svelte.d.ts +4 -0
  66. package/dist/components/Toast/internal/toast-icons/ToastSuccess.svelte +8 -0
  67. package/dist/components/Toast/internal/toast-icons/ToastSuccess.svelte.d.ts +4 -0
  68. package/dist/components/Toast/internal/toast-icons/ToastWarning.svelte +8 -0
  69. package/dist/components/Toast/internal/toast-icons/ToastWarning.svelte.d.ts +4 -0
  70. package/dist/components/Toast/internal/toast-icons/index.d.ts +6 -0
  71. package/dist/components/Toast/internal/toast-icons/index.js +6 -0
  72. package/dist/components/Toast/internal/toast-icons/toast-icon.types.d.ts +4 -0
  73. package/dist/components/Toast/internal/toast-icons/toast-icon.types.js +1 -0
  74. package/dist/components/Toast/toast.d.ts +31 -16
  75. package/dist/components/Toast/toast.js +45 -20
  76. package/dist/components/Toast/toast.types.d.ts +20 -7
  77. package/dist/hooks/index.d.ts +2 -0
  78. package/dist/hooks/index.js +2 -0
  79. package/dist/hooks/internal/DragDropProviderScope.svelte +29 -0
  80. package/dist/hooks/internal/DragDropProviderScope.svelte.d.ts +7 -0
  81. package/dist/hooks/internal/SortableProvider.svelte +30 -0
  82. package/dist/hooks/internal/SortableProvider.svelte.d.ts +7 -0
  83. package/dist/hooks/internal/drag-drop-context.d.ts +12 -0
  84. package/dist/hooks/internal/drag-drop-context.js +1 -0
  85. package/dist/hooks/internal/sortable-context.d.ts +12 -0
  86. package/dist/hooks/internal/sortable-context.js +1 -0
  87. package/dist/hooks/useDragDrop/index.d.ts +1 -0
  88. package/dist/hooks/useDragDrop/index.js +1 -0
  89. package/dist/hooks/useDragDrop/useDragDrop.svelte.d.ts +40 -0
  90. package/dist/hooks/useDragDrop/useDragDrop.svelte.js +128 -0
  91. package/dist/hooks/useSortable/index.d.ts +2 -0
  92. package/dist/hooks/useSortable/index.js +2 -0
  93. package/dist/hooks/useSortable/sortable-utils.d.ts +1 -0
  94. package/dist/hooks/useSortable/sortable-utils.js +13 -0
  95. package/dist/hooks/useSortable/useSortable.svelte.d.ts +52 -0
  96. package/dist/hooks/useSortable/useSortable.svelte.js +120 -0
  97. package/dist/index.d.ts +2 -0
  98. package/dist/index.js +2 -0
  99. package/dist/mcp/svelora-docs.data.json +14 -6
  100. package/package.json +5 -3
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "version": 1,
3
3
  "packageName": "svelora",
4
- "packageVersion": "3.0.14",
5
- "generatedAt": "2026-06-27T06:43:34.953Z",
4
+ "packageVersion": "3.0.16",
5
+ "generatedAt": "2026-06-28T17:57:55.685Z",
6
6
  "slugs": {
7
7
  "components": [
8
8
  "button",
@@ -48,6 +48,7 @@
48
48
  "select",
49
49
  "select-menu",
50
50
  "slider",
51
+ "sortable-list",
51
52
  "switch",
52
53
  "textarea",
53
54
  "password-input",
@@ -72,6 +73,7 @@
72
73
  "collapsible",
73
74
  "command",
74
75
  "context-menu",
76
+ "confirm-dialog",
75
77
  "drawer",
76
78
  "dropdown-menu",
77
79
  "popover",
@@ -86,6 +88,8 @@
86
88
  "use-clipboard",
87
89
  "use-form-field",
88
90
  "use-click-outside",
91
+ "use-sortable",
92
+ "use-drag-drop",
89
93
  "use-infinite-scroll",
90
94
  "use-escape-keydown",
91
95
  "use-debounce",
@@ -133,21 +137,22 @@
133
137
  "skeleton": "<script lang=\"ts\">\n import { Skeleton, Card, Avatar, Button, Separator } from '$lib/index.js'\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">Skeleton</h1>\n <p class=\"text-on-surface-variant\">\n Display placeholder content while data is loading to improve perceived performance.\n </p>\n </div>\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <div class=\"space-y-3 rounded-lg bg-surface-container-high p-4\">\n <Skeleton class=\"h-4 w-3/4\" />\n <Skeleton class=\"h-4 w-1/2\" />\n <Skeleton class=\"h-4 w-2/3\" />\n </div>\n </section>\n\n <!-- Shapes -->\n <section class=\"space-y-3\">\n <h2 id=\"Shapes\" class=\"text-lg font-semibold\">\n<a href=\"#Shapes\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Shapes\n </a>\n</h2>\n <div class=\"grid gap-6 sm:grid-cols-2 lg:grid-cols-4\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Text Lines</p>\n <div class=\"space-y-2 rounded-lg bg-surface-container-high p-4\">\n <Skeleton class=\"h-4 w-48\" />\n <Skeleton class=\"h-4 w-40\" />\n <Skeleton class=\"h-4 w-44\" />\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Circle (Avatar)</p>\n <div\n class=\"flex items-center justify-center rounded-lg bg-surface-container-high p-4\"\n >\n <Skeleton class=\"size-12 rounded-full\" />\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Square</p>\n <div\n class=\"flex items-center justify-center rounded-lg bg-surface-container-high p-4\"\n >\n <Skeleton class=\"size-24 rounded-lg\" />\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Rectangle (Image)</p>\n <div\n class=\"flex items-center justify-center rounded-lg bg-surface-container-high p-4\"\n >\n <Skeleton class=\"h-24 w-full rounded-lg\" />\n </div>\n </div>\n </div>\n </section>\n\n <!-- As Different Element -->\n <section class=\"space-y-3\">\n <h2 id=\"As-Different-Element\" class=\"text-lg font-semibold\">\n<a href=\"#As-Different-Element\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n As Different Element\n </a>\n</h2>\n <div class=\"space-y-2 rounded-lg bg-surface-container-high p-4\">\n <Skeleton as=\"span\" class=\"inline-block h-4 w-32\" />\n <Skeleton as=\"div\" class=\"h-4 w-48\" />\n <Skeleton as=\"p\" class=\"h-4 w-40\" />\n </div>\n </section>\n\n <!-- UI Slot Overrides -->\n <section class=\"space-y-3\">\n <h2 id=\"UI-Slot-Overrides\" class=\"text-lg font-semibold\">\n<a href=\"#UI-Slot-Overrides\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n UI Slot Overrides\n </a>\n</h2>\n <div class=\"grid gap-4 lg:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Custom background</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Skeleton class=\"h-16 w-full\" ui={{ root: 'bg-primary/15' }} />\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Custom border radius</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Skeleton class=\"h-16 w-full\" ui={{ root: 'rounded-2xl' }} />\n </div>\n </div>\n </div>\n </section>\n\n <Separator />\n\n <!-- Real World Examples -->\n <section class=\"space-y-6\">\n <h2 id=\"Real-World-Examples\" class=\"text-lg font-semibold\">\n<a href=\"#Real-World-Examples\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World Examples\n </a>\n</h2>\n\n <!-- Loading Card Comparison -->\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium\">Card Loading vs Loaded</p>\n <div class=\"grid gap-4 md:grid-cols-2\">\n <Card>\n <div class=\"space-y-3\">\n <div class=\"flex items-center gap-3\">\n <Skeleton class=\"size-12 rounded-full\" />\n <div class=\"flex-1 space-y-2\">\n <Skeleton class=\"h-4 w-1/3\" />\n <Skeleton class=\"h-3 w-1/2\" />\n </div>\n </div>\n <Skeleton class=\"h-4 w-full\" />\n <Skeleton class=\"h-4 w-5/6\" />\n <Skeleton class=\"h-4 w-4/6\" />\n <div class=\"flex gap-2 pt-2\">\n <Skeleton class=\"h-9 w-24\" />\n <Skeleton class=\"h-9 w-20\" />\n </div>\n </div>\n </Card>\n <Card>\n <div class=\"space-y-3\">\n <div class=\"flex items-center gap-3\">\n <Avatar\n src=\"https://i.pravatar.cc/150?img=3\"\n alt=\"Jane Smith\"\n size=\"md\"\n />\n <div>\n <h3 class=\"font-semibold\">Jane Smith</h3>\n <p class=\"text-sm text-on-surface-variant\">Product Designer</p>\n </div>\n </div>\n <p class=\"text-on-surface-variant\">\n Creating beautiful and functional user interfaces with attention to\n detail and user experience.\n </p>\n <div class=\"flex gap-2\">\n <Button variant=\"solid\" color=\"primary\" label=\"Follow\" size=\"sm\" />\n <Button variant=\"ghost\" color=\"secondary\" label=\"Message\" size=\"sm\" />\n </div>\n </div>\n </Card>\n </div>\n </div>\n\n <!-- Loading List -->\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium\">User List</p>\n <Card>\n <div class=\"space-y-4\">\n {#each Array.from({ length: 4 }, (_, i) => i) as i (i)}\n <div\n class=\"flex items-center gap-3 border-b border-outline-variant pb-4 last:border-0 last:pb-0\"\n >\n <Skeleton class=\"size-10 rounded-full\" />\n <div class=\"flex-1 space-y-2\">\n <Skeleton class=\"h-4 w-1/4\" />\n <Skeleton class=\"h-3 w-3/4\" />\n </div>\n </div>\n {/each}\n </div>\n </Card>\n </div>\n\n <!-- Loading Article -->\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium\">Article</p>\n <Card as=\"article\">\n <div class=\"space-y-4\">\n <div class=\"space-y-3\">\n <Skeleton class=\"h-8 w-3/4\" />\n <div class=\"flex items-center gap-3\">\n <Skeleton class=\"size-8 rounded-full\" />\n <div class=\"space-y-2\">\n <Skeleton class=\"h-3 w-24\" />\n <Skeleton class=\"h-3 w-32\" />\n </div>\n </div>\n </div>\n <Skeleton class=\"h-48 w-full rounded-lg\" />\n <div class=\"space-y-2\">\n <Skeleton class=\"h-4 w-full\" />\n <Skeleton class=\"h-4 w-full\" />\n <Skeleton class=\"h-4 w-5/6\" />\n <Skeleton class=\"h-4 w-4/6\" />\n </div>\n </div>\n </Card>\n </div>\n\n <!-- Dashboard Widgets -->\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium\">Dashboard Widgets</p>\n <div class=\"grid gap-4 md:grid-cols-3\">\n {#each Array.from({ length: 3 }, (_, i) => i) as i (i)}\n <Card>\n <div class=\"space-y-3\">\n <div class=\"flex items-center justify-between\">\n <Skeleton class=\"h-4 w-24\" />\n <Skeleton class=\"size-6 rounded\" />\n </div>\n <Skeleton class=\"h-10 w-20\" />\n <Skeleton class=\"h-20 w-full rounded\" />\n </div>\n </Card>\n {/each}\n </div>\n </div>\n\n <!-- Media Grid -->\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium\">Media Grid</p>\n <div class=\"grid grid-cols-2 gap-4 md:grid-cols-4\">\n {#each Array.from({ length: 4 }, (_, i) => i) as i (i)}\n <div class=\"space-y-2\">\n <Skeleton class=\"aspect-square w-full rounded-lg\" />\n <Skeleton class=\"h-3 w-3/4\" />\n <Skeleton class=\"h-3 w-1/2\" />\n </div>\n {/each}\n </div>\n </div>\n </section>\n</div>\n",
134
138
  "timeline": "<script lang=\"ts\">\n import { Timeline, Button, Separator } from '$lib/index.js'\n import type { TimelineItem } from '$lib/index.js'\n\n const colors = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'success',\n 'warning',\n 'error',\n 'info',\n 'surface'\n ] as const\n const sizes = ['3xs', '2xs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const\n\n // Basic items\n const basicItems: TimelineItem[] = [\n { value: 1, title: 'Step 1', description: 'First step', date: 'Jan 1' },\n { value: 2, title: 'Step 2', description: 'Second step', date: 'Jan 5' },\n { value: 3, title: 'Step 3', description: 'Third step', date: 'Jan 10' }\n ]\n\n // Order tracking\n let orderStep = $state(2)\n const orderItems: TimelineItem[] = [\n {\n value: 1,\n icon: 'lucide:shopping-cart',\n title: 'Order Placed',\n description: 'Your order has been confirmed',\n date: 'Dec 20'\n },\n {\n value: 2,\n icon: 'lucide:package',\n title: 'Processing',\n description: 'Preparing your items',\n date: 'Dec 21'\n },\n {\n value: 3,\n icon: 'lucide:truck',\n title: 'Shipped',\n description: 'On the way to you',\n date: 'Dec 22'\n },\n {\n value: 4,\n icon: 'lucide:home',\n title: 'Delivered',\n description: 'Package delivered',\n date: 'Dec 24'\n }\n ]\n\n // Checkout stepper\n let checkoutStep = $state(2)\n const checkoutItems: TimelineItem[] = [\n { value: 1, icon: 'lucide:shopping-bag', title: 'Cart' },\n { value: 2, icon: 'lucide:map-pin', title: 'Address' },\n { value: 3, icon: 'lucide:credit-card', title: 'Payment' },\n { value: 4, icon: 'lucide:check', title: 'Confirm' }\n ]\n\n // Activity feed\n const activityItems: TimelineItem[] = [\n {\n avatar: { src: 'https://i.pravatar.cc/150?u=john', alt: 'John' },\n title: 'John merged PR #142',\n description: 'feat: add Timeline component',\n date: '5 min ago'\n },\n {\n avatar: { src: 'https://i.pravatar.cc/150?u=sarah', alt: 'Sarah' },\n title: 'Sarah commented',\n description: 'Looks great! Ready for review.',\n date: '15 min ago'\n },\n {\n avatar: { src: 'https://i.pravatar.cc/150?u=mike', alt: 'Mike' },\n title: 'Mike pushed 3 commits',\n description: 'fix: resolve styling issues',\n date: '1 hour ago'\n },\n {\n avatar: { src: 'https://i.pravatar.cc/150?u=emma', alt: 'Emma' },\n title: 'Emma created issue #38',\n description: 'Bug: Timeline not rendering correctly',\n date: '2 hours ago'\n }\n ]\n\n // Per-item ui demo\n const perItemUiItems: TimelineItem[] = [\n {\n value: 1,\n icon: 'lucide:star',\n title: 'Highlighted Step',\n date: 'Special',\n ui: {\n indicator: 'ring-2 ring-warning ring-offset-2 ring-offset-surface-container-high',\n title: 'text-warning font-bold'\n }\n },\n {\n value: 2,\n icon: 'lucide:zap',\n title: 'Normal Step',\n date: 'Default'\n },\n {\n value: 3,\n icon: 'lucide:heart',\n title: 'Custom Separator',\n date: 'Styled',\n ui: { separator: 'bg-gradient-to-b from-error to-primary' }\n }\n ]\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">Timeline</h1>\n <p class=\"text-on-surface-variant\">\n Display a sequence of events with dates, titles, icons or avatars.\n </p>\n </div>\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <div class=\"grid gap-6 lg:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Without active state</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Timeline\n items={[\n { title: 'Event 1', date: 'Jan 1' },\n { title: 'Event 2', date: 'Jan 5' },\n { title: 'Event 3', date: 'Jan 10' }\n ]}\n />\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">With active state</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Timeline items={basicItems} value={2} />\n </div>\n </div>\n </div>\n </section>\n\n <!-- Icons & Avatars -->\n <section class=\"space-y-3\">\n <h2 id=\"Icons--Avatars\" class=\"text-lg font-semibold\">\n<a href=\"#Icons--Avatars\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Icons & Avatars\n </a>\n</h2>\n <div class=\"grid gap-6 lg:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">With Icons</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Timeline\n items={[\n { value: 1, icon: 'lucide:file-plus', title: 'Created', date: 'Dec 1' },\n { value: 2, icon: 'lucide:edit', title: 'Edited', date: 'Dec 5' },\n {\n value: 3,\n icon: 'lucide:check-circle',\n title: 'Approved',\n date: 'Dec 8'\n }\n ]}\n value={2}\n color=\"success\"\n />\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">With Avatars</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Timeline\n items={[\n {\n avatar: { src: 'https://i.pravatar.cc/150?u=a', alt: 'Alice' },\n title: 'Alice started',\n date: '2h ago'\n },\n {\n avatar: { src: 'https://i.pravatar.cc/150?u=b', alt: 'Bob' },\n title: 'Bob reviewed',\n date: '1h ago'\n },\n { avatar: { alt: 'You' }, title: 'You approved', date: 'Just now' }\n ]}\n />\n </div>\n </div>\n </div>\n </section>\n\n <!-- Colors -->\n <section class=\"space-y-3\">\n <h2 id=\"Colors\" class=\"text-lg font-semibold\">\n<a href=\"#Colors\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Colors\n </a>\n</h2>\n <div class=\"grid gap-3 sm:grid-cols-2 lg:grid-cols-4\">\n {#each colors as color (color)}\n <div class=\"rounded-lg bg-surface-container-high p-3\">\n <p class=\"mb-2 text-xs font-medium text-on-surface-variant capitalize\">\n {color}\n </p>\n <Timeline\n items={[\n { value: 1, title: 'Done' },\n { value: 2, title: 'Active' },\n { value: 3, title: 'Next' }\n ]}\n value={2}\n {color}\n size=\"sm\"\n />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Sizes -->\n <section class=\"space-y-3\">\n <h2 id=\"Sizes\" class=\"text-lg font-semibold\">\n<a href=\"#Sizes\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Sizes\n </a>\n</h2>\n <div class=\"overflow-x-auto\">\n <div\n class=\"flex gap-6 rounded-lg bg-surface-container-high p-4\"\n style=\"min-width: max-content;\"\n >\n {#each sizes as size (size)}\n <div class=\"flex flex-col items-center gap-2\">\n <span class=\"text-xs font-medium text-on-surface-variant\">{size}</span>\n <Timeline\n items={[\n { value: 1, icon: 'lucide:check' },\n { value: 2, icon: 'lucide:circle' }\n ]}\n value={1}\n {size}\n />\n </div>\n {/each}\n </div>\n </div>\n </section>\n\n <!-- Orientation -->\n <section class=\"space-y-3\">\n <h2 id=\"Orientation\" class=\"text-lg font-semibold\">\n<a href=\"#Orientation\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Orientation\n </a>\n</h2>\n <div class=\"space-y-4\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Horizontal</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Timeline items={basicItems} value={2} orientation=\"horizontal\" />\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Horizontal Reversed</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Timeline items={basicItems} value={2} orientation=\"horizontal\" reverse />\n </div>\n </div>\n </div>\n </section>\n\n <!-- Per-item UI Overrides -->\n <section class=\"space-y-3\">\n <h2 id=\"Per-item-UI-Overrides\" class=\"text-lg font-semibold\">\n<a href=\"#Per-item-UI-Overrides\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Per-item UI Overrides\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Each item can have its own <code class=\"rounded bg-surface-container-highest px-1\"\n >ui</code\n > prop to override slot classes.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Timeline items={perItemUiItems} value={2} />\n </div>\n </section>\n\n <!-- Custom Slots -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-Slots\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-Slots\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Slots\n </a>\n</h2>\n <div class=\"grid gap-6 lg:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Custom Indicator</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Timeline\n items={[\n { value: 1, title: 'Phase 1', date: 'Q1' },\n { value: 2, title: 'Phase 2', date: 'Q2' },\n { value: 3, title: 'Phase 3', date: 'Q3' }\n ]}\n value={2}\n >\n {#snippet indicator({ state, index })}\n <div\n class=\"flex size-8 items-center justify-center rounded-full text-sm font-bold transition-all\"\n class:bg-primary={state !== 'pending'}\n class:text-on-primary={state !== 'pending'}\n class:bg-surface-container-highest={state === 'pending'}\n class:text-on-surface-variant={state === 'pending'}\n class:scale-110={state === 'active'}\n >\n {index + 1}\n </div>\n {/snippet}\n </Timeline>\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Custom Content</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Timeline\n items={[\n {\n value: 'v1',\n icon: 'lucide:package',\n title: 'v1.0.0',\n date: 'Jan 2024'\n },\n {\n value: 'v2',\n icon: 'lucide:sparkles',\n title: 'v2.0.0',\n date: 'Jun 2024'\n },\n {\n value: 'v3',\n icon: 'lucide:rocket',\n title: 'v3.0.0',\n date: 'Coming Soon'\n }\n ]}\n value=\"v2\"\n color=\"info\"\n >\n {#snippet content({ item, state })}\n <div\n class=\"mt-2 rounded-lg border border-outline-variant bg-surface-container p-3\"\n >\n {#if item.value === 'v1'}\n <ul class=\"space-y-1 text-sm text-on-surface-variant\">\n <li>Initial release</li>\n <li>Core components</li>\n </ul>\n {:else if item.value === 'v2'}\n <ul class=\"space-y-1 text-sm text-on-surface-variant\">\n <li>Timeline component</li>\n <li>Performance improvements</li>\n </ul>\n {:else}\n <p class=\"text-sm text-on-surface-variant\">\n {state === 'pending'\n ? 'Exciting features coming!'\n : 'Released!'}\n </p>\n {/if}\n </div>\n {/snippet}\n </Timeline>\n </div>\n </div>\n </div>\n </section>\n\n <!-- UI Slot Overrides -->\n <section class=\"space-y-3\">\n <h2 id=\"UI-Slot-Overrides\" class=\"text-lg font-semibold\">\n<a href=\"#UI-Slot-Overrides\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n UI Slot Overrides\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Timeline\n items={[\n { value: 1, icon: 'lucide:star', title: 'Custom Ring', date: 'Styled' },\n { value: 2, icon: 'lucide:zap', title: 'Gradient Line', date: 'Colorful' },\n { value: 3, icon: 'lucide:heart', title: 'Bold Title', date: 'Italic' }\n ]}\n value={2}\n ui={{\n indicator:\n 'ring-2 ring-primary ring-offset-2 ring-offset-surface-container-high',\n separator: 'bg-gradient-to-b from-primary to-tertiary',\n title: 'text-primary font-bold',\n date: 'italic'\n }}\n />\n </div>\n </section>\n\n <Separator />\n\n <!-- Real World Examples -->\n <section class=\"space-y-6\">\n <h2 id=\"Real-World-Examples\" class=\"text-lg font-semibold\">\n<a href=\"#Real-World-Examples\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World Examples\n </a>\n</h2>\n\n <!-- Order Tracking -->\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium\">Order Tracking</p>\n <div class=\"rounded-lg border border-outline-variant bg-surface-container p-4\">\n <Timeline items={orderItems} value={orderStep} color=\"success\" />\n <div class=\"mt-4 flex items-center justify-center gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n onclick={() => (orderStep = Math.max(1, orderStep - 1))}\n disabled={orderStep === 1}\n >\n Previous\n </Button>\n <span class=\"px-3 text-sm text-on-surface-variant\">Step {orderStep} of 4</span>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onclick={() => (orderStep = Math.min(4, orderStep + 1))}\n disabled={orderStep === 4}\n >\n Next\n </Button>\n </div>\n </div>\n </div>\n\n <!-- Checkout Stepper -->\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium\">Checkout Stepper (Horizontal)</p>\n <div class=\"rounded-lg border border-outline-variant bg-surface-container p-4\">\n <Timeline\n items={checkoutItems}\n value={checkoutStep}\n orientation=\"horizontal\"\n size=\"lg\"\n />\n <div class=\"mt-4 flex items-center justify-between\">\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onclick={() => (checkoutStep = Math.max(1, checkoutStep - 1))}\n disabled={checkoutStep === 1}\n >\n Back\n </Button>\n <Button\n size=\"sm\"\n onclick={() => (checkoutStep = Math.min(4, checkoutStep + 1))}\n disabled={checkoutStep === 4}\n >\n {checkoutStep === 3 ? 'Place Order' : 'Continue'}\n </Button>\n </div>\n </div>\n </div>\n\n <!-- Activity Feed -->\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium\">Activity Feed</p>\n <div class=\"rounded-lg border border-outline-variant bg-surface-container p-4\">\n <Timeline items={activityItems} size=\"lg\" />\n </div>\n </div>\n </section>\n</div>\n",
135
139
  "user": "<script lang=\"ts\">\n import { User, Separator } from '$lib/index.js'\n\n const sizes = ['3xs', '2xs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">User</h1>\n <p class=\"text-on-surface-variant\">\n Displays user information with avatar, name, and description.\n </p>\n </div>\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <div class=\"grid gap-6 lg:grid-cols-3\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">With avatar & description</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <User\n name=\"John Doe\"\n description=\"Software Engineer\"\n avatar={{ src: 'https://i.pravatar.cc/128?u=john' }}\n />\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Avatar with initials</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <User name=\"Alex Chen\" description=\"Product Manager\" avatar={{ alt: 'AC' }} />\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Name only</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <User name=\"Jane Smith\" avatar={{ src: 'https://i.pravatar.cc/128?u=jane' }} />\n </div>\n </div>\n </div>\n </section>\n\n <!-- Sizes -->\n <section class=\"space-y-3\">\n <h2 id=\"Sizes\" class=\"text-lg font-semibold\">\n<a href=\"#Sizes\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Sizes\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-4\">\n {#each sizes as size (size)}\n <div class=\"flex items-center gap-4\">\n <span class=\"w-10 text-sm font-medium text-on-surface-variant\">{size}</span>\n <User\n name=\"John Doe\"\n description=\"Software Engineer\"\n avatar={{ src: 'https://i.pravatar.cc/128?u=size-{size}' }}\n {size}\n />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Orientation -->\n <section class=\"space-y-3\">\n <h2 id=\"Orientation\" class=\"text-lg font-semibold\">\n<a href=\"#Orientation\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Orientation\n </a>\n</h2>\n <div class=\"grid gap-6 lg:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Horizontal (default)</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <User\n name=\"John Doe\"\n description=\"Software Engineer\"\n avatar={{ src: 'https://i.pravatar.cc/128?u=h' }}\n orientation=\"horizontal\"\n />\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Vertical</p>\n <div class=\"flex justify-center rounded-lg bg-surface-container-high p-4\">\n <User\n name=\"John Doe\"\n description=\"Software Engineer\"\n avatar={{ src: 'https://i.pravatar.cc/128?u=v' }}\n orientation=\"vertical\"\n />\n </div>\n </div>\n </div>\n </section>\n\n <!-- With Chip -->\n <section class=\"space-y-3\">\n <h2 id=\"With-Chip\" class=\"text-lg font-semibold\">\n<a href=\"#With-Chip\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n With Chip\n </a>\n</h2>\n <div class=\"grid gap-6 sm:grid-cols-2 lg:grid-cols-4\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Online</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <User\n name=\"Alice\"\n description=\"Available\"\n avatar={{ src: 'https://i.pravatar.cc/128?u=online' }}\n chip={{ color: 'success' }}\n />\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Busy</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <User\n name=\"Bob\"\n description=\"In a meeting\"\n avatar={{ src: 'https://i.pravatar.cc/128?u=busy' }}\n chip={{ color: 'error' }}\n />\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Away</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <User\n name=\"Carol\"\n description=\"Be right back\"\n avatar={{ src: 'https://i.pravatar.cc/128?u=away' }}\n chip={{ color: 'warning' }}\n />\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Default chip</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <User\n name=\"Dave\"\n description=\"Offline\"\n avatar={{ src: 'https://i.pravatar.cc/128?u=offline' }}\n chip={true}\n />\n </div>\n </div>\n </div>\n </section>\n\n <!-- As Link -->\n <section class=\"space-y-3\">\n <h2 id=\"As-Link\" class=\"text-lg font-semibold\">\n<a href=\"#As-Link\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n As Link\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <User\n name=\"John Doe\"\n description=\"View profile →\"\n avatar={{ src: 'https://i.pravatar.cc/128?u=link' }}\n href=\"/user\"\n />\n </div>\n </section>\n\n <!-- Clickable -->\n <section class=\"space-y-3\">\n <h2 id=\"Clickable\" class=\"text-lg font-semibold\">\n<a href=\"#Clickable\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Clickable\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <User\n name=\"Click me\"\n description=\"Has onclick handler\"\n avatar={{ src: 'https://i.pravatar.cc/128?u=click' }}\n onclick={() => alert('Clicked!')}\n />\n </div>\n </section>\n\n <!-- Custom Slots -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-Slots\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-Slots\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Slots\n </a>\n</h2>\n <div class=\"grid gap-6 lg:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Custom avatar slot</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <User name=\"Custom Avatar\" description=\"With emoji avatar\">\n {#snippet avatarSlot()}\n <div\n class=\"flex size-10 items-center justify-center rounded-full bg-primary text-lg\"\n >\n 🎨\n </div>\n {/snippet}\n </User>\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Custom name & description</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <User avatar={{ src: 'https://i.pravatar.cc/128?u=slot' }}>\n {#snippet nameSlot()}\n <p class=\"text-sm font-bold text-primary\">Premium User</p>\n {/snippet}\n {#snippet descriptionSlot()}\n <div class=\"flex items-center gap-1\">\n <span class=\"inline-block size-2 rounded-full bg-success\"></span>\n <span class=\"text-xs text-on-surface-variant\">Active now</span>\n </div>\n {/snippet}\n </User>\n </div>\n </div>\n </div>\n </section>\n\n <!-- UI Slot Overrides -->\n <section class=\"space-y-3\">\n <h2 id=\"UI-Slot-Overrides\" class=\"text-lg font-semibold\">\n<a href=\"#UI-Slot-Overrides\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n UI Slot Overrides\n </a>\n</h2>\n <div class=\"grid gap-6 lg:grid-cols-3\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Custom text styles</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <User\n name=\"Styled Name\"\n description=\"Styled description\"\n avatar={{ src: 'https://i.pravatar.cc/128?u=ui1' }}\n ui={{\n name: 'text-primary font-bold',\n description: 'text-tertiary italic'\n }}\n />\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Bordered root</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <User\n name=\"Bordered\"\n description=\"With root override\"\n avatar={{ src: 'https://i.pravatar.cc/128?u=ui2' }}\n ui={{\n root: 'border border-outline-variant rounded-xl p-3'\n }}\n />\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Custom avatar class</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <User\n name=\"Ring Avatar\"\n description=\"Avatar with ring\"\n avatar={{ src: 'https://i.pravatar.cc/128?u=ui3' }}\n ui={{\n avatar: 'ring-2 ring-primary ring-offset-2 ring-offset-surface-container-high'\n }}\n />\n </div>\n </div>\n </div>\n </section>\n\n <Separator />\n\n <!-- Real World Examples -->\n <section class=\"space-y-6\">\n <h2 id=\"Real-World-Examples\" class=\"text-lg font-semibold\">\n<a href=\"#Real-World-Examples\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World Examples\n </a>\n</h2>\n\n <!-- User List -->\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium\">User List</p>\n <div\n class=\"max-w-sm space-y-1 rounded-lg border border-outline-variant bg-surface-container p-2\"\n >\n {#each [{ name: 'Alice Johnson', desc: 'Admin', u: 'alice' }, { name: 'Bob Williams', desc: 'Editor', u: 'bob' }, { name: 'Carol Davis', desc: 'Viewer', u: 'carol' }] as item (item.u)}\n <User\n name={item.name}\n description={item.desc}\n avatar={{\n src: `https://i.pravatar.cc/128?u=${item.u}`,\n alt: item.name\n }}\n onclick={() => alert(item.name)}\n size=\"sm\"\n />\n {/each}\n </div>\n </div>\n\n <!-- Comment Header -->\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium\">Comment Header</p>\n <div class=\"max-w-md rounded-lg border border-outline-variant bg-surface-container p-4\">\n <User\n name=\"Sarah Chen\"\n description=\"2 hours ago\"\n avatar={{ src: 'https://i.pravatar.cc/128?u=sarah' }}\n size=\"sm\"\n />\n <p class=\"mt-2 text-sm text-on-surface-variant\">\n This looks great! I love the new design direction. The component feels much more\n polished now.\n </p>\n </div>\n </div>\n\n <!-- Profile Card -->\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium\">Profile Card</p>\n <div class=\"max-w-xs rounded-lg border border-outline-variant bg-surface-container p-6\">\n <User\n name=\"Emily Parker\"\n description=\"Senior Developer at Acme Corp\"\n avatar={{ src: 'https://i.pravatar.cc/128?u=emily' }}\n orientation=\"vertical\"\n size=\"xl\"\n chip={{ color: 'success' }}\n />\n </div>\n </div>\n\n <!-- Team Members -->\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium\">Team Members</p>\n <div class=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {#each [{ name: 'Mike Ross', role: 'Frontend', u: 'mike', color: 'primary' as const }, { name: 'Rachel Zane', role: 'Backend', u: 'rachel', color: 'secondary' as const }, { name: 'Harvey Specter', role: 'Lead', u: 'harvey', color: 'tertiary' as const }] as member (member.u)}\n <div class=\"rounded-lg border border-outline-variant bg-surface-container p-4\">\n <User\n name={member.name}\n description={member.role}\n avatar={{ src: `https://i.pravatar.cc/128?u=${member.u}` }}\n chip={{ color: member.color }}\n href=\"/user\"\n />\n </div>\n {/each}\n </div>\n </div>\n </section>\n</div>\n",
136
- "table": "<script lang=\"ts\">\n import {\n Table,\n Badge,\n Avatar,\n Button,\n Input,\n Icon,\n Pagination,\n DropdownMenu,\n TableBulkActionBar,\n type TableColumn,\n type SortState,\n type TableCellSlotProps,\n type TableFooterSlotProps,\n type DropdownMenuItem\n } from '$lib/index.js'\n\n // ==================== Types ====================\n\n interface User {\n id: string\n name: string\n email: string\n role: string\n status: 'active' | 'inactive' | 'pending'\n avatar: string\n }\n\n interface Product {\n name: string\n category: string\n price: number\n stock: number\n }\n\n interface Department {\n id: string\n name: string\n manager: string\n employees: number\n budget: string\n children?: Department[]\n }\n\n // ==================== Data ====================\n\n const users: User[] = [\n {\n id: '1',\n name: 'Alice Johnson',\n email: 'alice@example.com',\n role: 'Admin',\n status: 'active',\n avatar: 'https://i.pravatar.cc/150?u=alice'\n },\n {\n id: '2',\n name: 'Bob Smith',\n email: 'bob@example.com',\n role: 'User',\n status: 'inactive',\n avatar: 'https://i.pravatar.cc/150?u=bob'\n },\n {\n id: '3',\n name: 'Charlie Brown',\n email: 'charlie@example.com',\n role: 'Editor',\n status: 'active',\n avatar: 'https://i.pravatar.cc/150?u=charlie'\n },\n {\n id: '4',\n name: 'Diana Prince',\n email: 'diana@example.com',\n role: 'Admin',\n status: 'pending',\n avatar: 'https://i.pravatar.cc/150?u=diana'\n },\n {\n id: '5',\n name: 'Eve Wilson',\n email: 'eve@example.com',\n role: 'User',\n status: 'active',\n avatar: 'https://i.pravatar.cc/150?u=eve'\n }\n ]\n\n const manyUsers: User[] = Array.from({ length: 50 }, (_, i) => ({\n id: String(i + 1),\n name: `User ${i + 1}`,\n email: `user${i + 1}@example.com`,\n role: ['Admin', 'User', 'Editor'][i % 3],\n status: (['active', 'inactive', 'pending'] as const)[i % 3],\n avatar: `https://i.pravatar.cc/150?u=user${i + 1}`\n }))\n\n const products: Product[] = [\n { name: 'MacBook Pro', category: 'Electronics', price: 2499, stock: 12 },\n { name: 'iPhone 16', category: 'Electronics', price: 999, stock: 45 },\n { name: 'AirPods Pro', category: 'Accessories', price: 249, stock: 100 },\n { name: 'Magic Keyboard', category: 'Accessories', price: 299, stock: 30 },\n { name: 'Studio Display', category: 'Electronics', price: 1599, stock: 8 }\n ]\n\n const departments: Department[] = [\n {\n id: 'eng',\n name: 'Engineering',\n manager: 'Alice Johnson',\n employees: 42,\n budget: '$2.1M',\n children: [\n {\n id: 'eng-fe',\n name: 'Frontend',\n manager: 'Bob Smith',\n employees: 15,\n budget: '$750K'\n },\n {\n id: 'eng-be',\n name: 'Backend',\n manager: 'Charlie Brown',\n employees: 18,\n budget: '$900K'\n },\n {\n id: 'eng-infra',\n name: 'Infrastructure',\n manager: 'Diana Prince',\n employees: 9,\n budget: '$450K'\n }\n ]\n },\n {\n id: 'design',\n name: 'Design',\n manager: 'Eve Wilson',\n employees: 12,\n budget: '$600K',\n children: [\n {\n id: 'design-ux',\n name: 'UX Research',\n manager: 'Frank Lee',\n employees: 5,\n budget: '$250K'\n },\n {\n id: 'design-ui',\n name: 'UI Design',\n manager: 'Grace Kim',\n employees: 7,\n budget: '$350K'\n }\n ]\n },\n {\n id: 'marketing',\n name: 'Marketing',\n manager: 'Henry Zhang',\n employees: 8,\n budget: '$400K'\n }\n ]\n\n // Flatten departments with parent-child for expandable\n const flatDepartments = $derived.by(() => {\n const result: (Department & { level: number; parentId?: string })[] = []\n for (const dept of departments) {\n result.push({ ...dept, level: 0 })\n }\n return result\n })\n\n const statusColor: Record<string, 'success' | 'error' | 'warning'> = {\n active: 'success',\n inactive: 'error',\n pending: 'warning'\n }\n\n const roleColor: Record<string, 'primary' | 'secondary' | 'tertiary'> = {\n Admin: 'primary',\n User: 'secondary',\n Editor: 'tertiary'\n }\n\n // ==================== Rich Columns ====================\n const richColumns: TableColumn<User>[] = [\n { key: 'id', label: '#' },\n { key: 'name', label: 'Name', cell: userCell },\n { key: 'email', label: 'Email' },\n { key: 'role', label: 'Role', cell: roleCell },\n { key: 'status', label: 'Status', cell: statusCell }\n ]\n\n // ==================== Sorting ====================\n let sorting: SortState = $state([{ key: 'name', direction: 'asc' }])\n\n const sortColumns: TableColumn<User>[] = [\n { key: 'id', label: '#' },\n { key: 'name', label: 'Name', sortable: true, cell: userCell },\n { key: 'email', label: 'Email' },\n { key: 'role', label: 'Role', sortable: true, cell: roleCell },\n { key: 'status', label: 'Status', sortable: true, cell: statusCell }\n ]\n\n // ==================== Row Actions ====================\n function getRowActions(row: User): DropdownMenuItem[] {\n return [\n {\n label: 'View profile',\n icon: 'lucide:user',\n onSelect: () => alert(`View ${row.name}`)\n },\n {\n label: 'Send email',\n icon: 'lucide:mail',\n onSelect: () => alert(`Email ${row.email}`)\n },\n { type: 'separator' },\n { label: 'Edit', icon: 'lucide:pencil', onSelect: () => alert(`Edit ${row.name}`) },\n {\n label: 'Delete',\n icon: 'lucide:trash-2',\n color: 'error',\n onSelect: () => alert(`Delete ${row.name}`)\n }\n ]\n }\n\n const actionColumns: TableColumn<User>[] = [\n { key: 'id', label: '#' },\n { key: 'name', label: 'Name', cell: userCell },\n { key: 'email', label: 'Email' },\n { key: 'role', label: 'Role', cell: roleCell },\n { key: 'status', label: 'Status', cell: statusCell },\n { key: 'avatar', label: '', cell: actionsCell, align: 'right', width: 60 }\n ]\n\n // ==================== Column Pinning ====================\n const pinColumns: TableColumn<User>[] = [\n { key: 'id', label: '#', width: 60 },\n { key: 'name', label: 'Name', cell: userCell, width: 200 },\n { key: 'email', label: 'Email', width: 220 },\n { key: 'role', label: 'Role', cell: roleCell, width: 120 },\n { key: 'status', label: 'Status', cell: statusCell, width: 120 },\n { key: 'avatar', label: '', cell: actionsCell, align: 'right', width: 80 }\n ]\n\n // ==================== Row Pinning ====================\n let pinnedRows: (string | number)[] = $state(['1', '3'])\n\n const rowPinColumns: TableColumn<User>[] = [\n { key: 'id', label: '#' },\n { key: 'name', label: 'Name', cell: userCell },\n { key: 'email', label: 'Email' },\n { key: 'role', label: 'Role', cell: roleCell },\n { key: 'status', label: 'Status', cell: statusCell },\n { key: 'avatar', label: '', cell: pinActionCell, align: 'right', width: 60 }\n ]\n\n // ==================== Expandable Rows ====================\n let expandedRows: (string | number)[] = $state([])\n let expandedDepts: (string | number)[] = $state([])\n\n const deptColumns: TableColumn<Department>[] = [\n { key: 'id', label: '', cell: deptExpandCell, width: 40 },\n { key: 'name', label: 'Department', cell: deptNameCell },\n { key: 'manager', label: 'Manager' },\n { key: 'employees', label: 'Employees', align: 'right' },\n { key: 'budget', label: 'Budget', align: 'right' }\n ]\n\n // ==================== Row Selection ====================\n let selectedRows: User[] = $state([])\n let bulkSelectedRows: User[] = $state([])\n\n // ==================== Column Visibility ====================\n let columnVisibility: Record<string, boolean> = $state({})\n\n const visibilityItems = [\n { value: 'id', label: '#' },\n { value: 'name', label: 'Name' },\n { value: 'email', label: 'Email' },\n { value: 'role', label: 'Role' },\n { value: 'status', label: 'Status' }\n ]\n\n function toggleColumnVisibility(col: string) {\n columnVisibility = {\n ...columnVisibility,\n [col]: columnVisibility[col] === false\n }\n }\n\n // ==================== Pagination ====================\n let paginationPage = $state(1)\n const pageSize = 10\n const tablePage = $derived(paginationPage - 1)\n\n // ==================== Row Pinning + Pagination ====================\n let pinPagPage = $state(1)\n const pinPagTablePage = $derived(pinPagPage - 1)\n let pinPagPinnedRows: (string | number)[] = $state(['1', '5', '12'])\n\n const rowPinPagColumns: TableColumn<User>[] = [\n { key: 'id', label: '#' },\n { key: 'name', label: 'Name', cell: userCell },\n { key: 'email', label: 'Email' },\n { key: 'role', label: 'Role', cell: roleCell },\n { key: 'status', label: 'Status', cell: statusCell },\n { key: 'avatar', label: '', cell: pinPagActionCell, align: 'right', width: 60 }\n ]\n\n // ==================== Selection + Pagination ====================\n let selPagPage = $state(1)\n const selPagTablePage = $derived(selPagPage - 1)\n let selPagSelected: User[] = $state([])\n\n // ==================== Global Filter ====================\n let globalFilter = $state('')\n\n // ==================== Loading (Replace data) ====================\n let isLoadingReplace = $state(false)\n function simulateLoadingReplace() {\n isLoadingReplace = true\n setTimeout(() => (isLoadingReplace = false), 2000)\n }\n\n // ==================== Loading (Overlay) ====================\n let isLoadingOverlay = $state(false)\n function simulateLoadingOverlay() {\n isLoadingOverlay = true\n setTimeout(() => (isLoadingOverlay = false), 2000)\n }\n\n // ==================== Row Click ====================\n let selectedUser: User | undefined = $state(undefined)\n\n // ==================== Column Resizing ====================\n let columnSizing: Record<string, number> = $state({})\n\n const resizableColumns: TableColumn<User>[] = [\n { key: 'id', label: '#', width: 60 },\n { key: 'name', label: 'Name', cell: userCell, resizable: true, width: 200, minWidth: 120 },\n { key: 'email', label: 'Email', resizable: true, width: 220, minWidth: 150 },\n { key: 'role', label: 'Role', cell: roleCell, resizable: true, width: 120, minWidth: 80 },\n { key: 'status', label: 'Status', cell: statusCell, width: 120 }\n ]\n\n // ==================== Hover / Contextmenu ====================\n let hoveredUser: User | null = $state(null)\n let contextUser: { user: User; x: number; y: number } | null = $state(null)\n\n // ==================== Product columns ====================\n const productColumns: TableColumn<Product>[] = [\n { key: 'name', label: 'Product' },\n { key: 'category', label: 'Category', cell: categoryCell },\n { key: 'price', label: 'Price', cell: priceCell, align: 'right' },\n { key: 'stock', label: 'Stock', cell: stockCell, align: 'right' }\n ]\n</script>\n\n<div class=\"space-y-16\">\n <div>\n <h1 class=\"text-3xl font-bold text-on-surface\">Table</h1>\n <p class=\"mt-2 text-on-surface-variant\">Custom implementation — no external dependency.</p>\n </div>\n\n <!-- ==================== BASIC ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Basic\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Auto-generates columns from data keys.</p>\n <Table\n data={[\n { name: 'Alice', email: 'alice@example.com', role: 'Admin' },\n { name: 'Bob', email: 'bob@example.com', role: 'User' },\n { name: 'Charlie', email: 'charlie@example.com', role: 'Editor' }\n ]}\n />\n </section>\n\n <!-- ==================== RICH COLUMNS ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Rich-Columns\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Rich-Columns\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Rich Columns\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Custom cell snippets with Avatar, Badge components.\n </p>\n <Table data={users} columns={richColumns} rowKey=\"id\" />\n </section>\n\n <!-- ==================== SORTING (default asc on name) ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Sorting\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Sorting\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Sorting\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Default sort on <strong>Name (asc)</strong>. Click headers to toggle.\n </p>\n <Table data={users} columns={sortColumns} bind:sorting rowKey=\"id\" />\n {#if sorting.length > 0}\n <p class=\"text-xs text-on-surface-variant\">\n Sorting: <strong>{sorting[0].key}</strong> ({sorting[0].direction})\n </p>\n {/if}\n </section>\n\n <!-- ==================== GLOBAL FILTER ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Global-Filter\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Global-Filter\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Global Filter\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Filter across all columns.</p>\n <Input\n placeholder=\"Search users...\"\n bind:value={globalFilter}\n leadingIcon=\"lucide:search\"\n variant=\"outline\"\n class=\"max-w-sm\"\n />\n <Table data={users} columns={richColumns} bind:globalFilter rowKey=\"id\" />\n </section>\n\n <!-- ==================== ROW ACTIONS ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Row-Actions\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Row-Actions\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Row Actions\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n DropdownMenu in the last column for per-row actions.\n </p>\n <Table data={users} columns={actionColumns} rowKey=\"id\" />\n </section>\n\n <!-- ==================== COLUMN PINNING ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Column-Pinning\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Column-Pinning\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Column Pinning\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n \"#\" pinned left, \"Actions\" pinned right. Scroll horizontally.\n </p>\n <div class=\"max-w-lg\">\n <Table\n data={users}\n columns={pinColumns}\n columnPinning={{ left: ['id'], right: ['avatar'] }}\n rowKey=\"id\"\n />\n </div>\n </section>\n\n <!-- ==================== ROW PINNING ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Row-Pinning\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Row-Pinning\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Row Pinning\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Pin rows to the top. Alice and Charlie are pinned by default. Click the pin icon to\n toggle.\n </p>\n <Table data={users} columns={rowPinColumns} bind:pinnedRows rowKey=\"id\" />\n {#if pinnedRows.length > 0}\n <p class=\"text-xs text-on-surface-variant\">\n Pinned: <strong>{pinnedRows.join(', ')}</strong>\n </p>\n {/if}\n </section>\n\n <!-- ==================== ROW PINNING + PAGINATION ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Row-Pinning--Pagination\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Row-Pinning--Pagination\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Row Pinning + Pagination\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Pinned rows (#1, #5, #12) stay at the top regardless of the current page.\n </p>\n <Table\n data={manyUsers}\n columns={rowPinPagColumns}\n bind:pinnedRows={pinPagPinnedRows}\n page={pinPagTablePage}\n {pageSize}\n rowKey=\"id\"\n />\n <div class=\"flex items-center justify-between\">\n {#if pinPagPinnedRows.length > 0}\n <Badge label=\"{pinPagPinnedRows.length} pinned\" variant=\"soft\" color=\"primary\" />\n {:else}\n <span class=\"text-sm text-on-surface-variant/50\">No pinned rows</span>\n {/if}\n <Pagination\n bind:page={pinPagPage}\n total={manyUsers.length}\n itemsPerPage={pageSize}\n showEdges\n size=\"sm\"\n />\n </div>\n </section>\n\n <!-- ==================== EXPANDABLE ROWS ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Expandable-Rows\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Expandable-Rows\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Expandable Rows\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Click the chevron to expand row details.</p>\n <Table\n data={users}\n columns={[\n { key: 'id', label: '', cell: expandCell, width: 40 },\n ...richColumns.slice(1)\n ]}\n bind:expandedRows\n rowKey=\"id\"\n >\n {#snippet expandedSlot({ row })}\n <div class=\"flex items-start gap-4 p-3\">\n <Avatar src={row.avatar} alt={row.name} size=\"lg\" />\n <div class=\"space-y-2\">\n <div>\n <p class=\"font-semibold text-on-surface\">{row.name}</p>\n <p class=\"text-sm text-on-surface-variant\">{row.email}</p>\n </div>\n <div class=\"flex gap-2\">\n <Badge\n label={row.role}\n variant=\"soft\"\n color={roleColor[row.role] ?? 'secondary'}\n />\n <Badge\n label={row.status}\n variant=\"subtle\"\n color={statusColor[row.status] ?? 'surface'}\n />\n </div>\n </div>\n </div>\n {/snippet}\n </Table>\n </section>\n\n <!-- ==================== EXPANDABLE: PARENT-CHILD ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Parent-Child-Rows\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Parent-Child-Rows\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Parent-Child Rows\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Expand departments to see sub-departments as a nested table.\n </p>\n <Table\n data={flatDepartments}\n columns={deptColumns}\n bind:expandedRows={expandedDepts}\n rowKey=\"id\"\n >\n {#snippet expandedSlot({ row })}\n {#if row.children && row.children.length > 0}\n <Table\n data={row.children}\n columns={[\n { key: 'name', label: 'Sub-department' },\n { key: 'manager', label: 'Manager' },\n { key: 'employees', label: 'Employees', align: 'right' },\n { key: 'budget', label: 'Budget', align: 'right' }\n ]}\n rowKey=\"id\"\n ui={{ root: 'border-0 rounded-none shadow-none' }}\n />\n {:else}\n <p class=\"py-2 text-sm text-on-surface-variant\">No sub-departments</p>\n {/if}\n {/snippet}\n </Table>\n </section>\n\n <!-- ==================== ROW SELECTION ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Row-Selection\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Row-Selection\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Row Selection\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Built-in checkbox column with select-all support.\n </p>\n <Table\n data={users}\n columns={richColumns}\n selection=\"multiple\"\n bind:selectedRows\n rowKey=\"id\"\n />\n {#if selectedRows.length > 0}\n <div class=\"flex items-center gap-2\">\n <Badge label=\"{selectedRows.length} selected\" variant=\"soft\" color=\"primary\" />\n <span class=\"text-sm text-on-surface-variant\"\n >{selectedRows.map((u) => u.name).join(', ')}</span\n >\n </div>\n {:else}\n <p class=\"text-sm text-on-surface-variant/50\">No rows selected</p>\n {/if}\n </section>\n\n <!-- ==================== SELECTION + PAGINATION ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Selection--Pagination\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Selection--Pagination\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Selection + Pagination\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Selections persist across pages. Select some rows, then navigate.\n </p>\n <Table\n data={manyUsers}\n columns={richColumns}\n selection=\"multiple\"\n bind:selectedRows={selPagSelected}\n page={selPagTablePage}\n {pageSize}\n rowKey=\"id\"\n />\n <div class=\"flex items-center justify-between\">\n {#if selPagSelected.length > 0}\n <Badge\n label=\"{selPagSelected.length} selected across pages\"\n variant=\"soft\"\n color=\"primary\"\n />\n {:else}\n <span class=\"text-sm text-on-surface-variant/50\">No rows selected</span>\n {/if}\n <Pagination\n bind:page={selPagPage}\n total={manyUsers.length}\n itemsPerPage={pageSize}\n showEdges\n size=\"sm\"\n />\n </div>\n </section>\n\n <!-- ==================== BULK ACTIONS ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Bulk-Actions\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Bulk-Actions\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Bulk Actions\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n A real-world example of using <code>TableBulkActionBar</code> when items are selected. Try selecting some rows below.\n </p>\n <div class=\"relative\">\n <Table\n data={users}\n columns={richColumns}\n selection=\"multiple\"\n bind:selectedRows={bulkSelectedRows}\n rowKey=\"id\"\n />\n </div>\n {#if bulkSelectedRows.length > 0}\n <TableBulkActionBar count={bulkSelectedRows.length} onClear={() => (bulkSelectedRows = [])}>\n <Button color=\"error\" size=\"sm\" icon=\"lucide:trash-2\" onclick={() => alert(`Deleted ${bulkSelectedRows.length} users`)}>\n Delete\n </Button>\n <Button variant=\"outline\" color=\"surface\" size=\"sm\" icon=\"lucide:download\" onclick={() => alert(`Exported CSV for ${bulkSelectedRows.length} users`)}>\n Export\n </Button>\n </TableBulkActionBar>\n {/if}\n </section>\n\n <!-- ==================== COLUMN VISIBILITY ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Column-Visibility\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Column-Visibility\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Column Visibility\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Toggle columns on/off.</p>\n <div class=\"flex flex-wrap gap-1.5\">\n {#each visibilityItems as item (item.value)}\n <button\n type=\"button\"\n class=\"rounded-full px-2.5 py-1 text-xs transition-colors {columnVisibility[\n item.value\n ] === false\n ? 'bg-surface-container text-on-surface-variant line-through'\n : 'bg-primary/10 text-primary'}\"\n onclick={() => toggleColumnVisibility(item.value)}\n >\n {item.label}\n </button>\n {/each}\n </div>\n <Table data={users} columns={richColumns} bind:columnVisibility rowKey=\"id\" />\n </section>\n\n <!-- ==================== PAGINATION ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Pagination\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Pagination\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Pagination\n </a>\n</h2>\n <Table data={manyUsers} columns={richColumns} page={tablePage} {pageSize} rowKey=\"id\" />\n <div class=\"flex items-center justify-between\">\n <p class=\"text-sm text-on-surface-variant\">\n {tablePage * pageSize + 1}–{Math.min((tablePage + 1) * pageSize, manyUsers.length)} of\n {manyUsers.length}\n </p>\n <Pagination\n bind:page={paginationPage}\n total={manyUsers.length}\n itemsPerPage={pageSize}\n showEdges\n size=\"sm\"\n />\n </div>\n </section>\n\n <!-- ==================== ROW CLICK ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Row-Click\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Row-Click\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Row Click\n </a>\n</h2>\n <Table\n data={users}\n columns={richColumns}\n onRowClick={(row) => (selectedUser = row)}\n rowKey=\"id\"\n />\n {#if selectedUser}\n <div\n class=\"flex items-center gap-3 rounded-xl border border-primary/20 bg-primary-container/15 p-3\"\n >\n <Avatar src={selectedUser.avatar} alt={selectedUser.name} size=\"sm\" />\n <div>\n <p class=\"text-sm font-medium text-on-surface\">{selectedUser.name}</p>\n <p class=\"text-xs text-on-surface-variant\">{selectedUser.email}</p>\n </div>\n <Badge\n label={selectedUser.role}\n variant=\"soft\"\n color={roleColor[selectedUser.role] ?? 'secondary'}\n class=\"ml-auto\"\n />\n </div>\n {/if}\n </section>\n\n <!-- ==================== ROW HOVER ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Row-Hover\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Row-Hover\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Row Hover\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Hover over rows to see the callback in action.\n </p>\n <Table\n data={users}\n columns={richColumns}\n rowKey=\"id\"\n onRowHover={(row) => (hoveredUser = row)}\n />\n {#if hoveredUser}\n <div\n class=\"flex items-center gap-3 rounded-xl border border-outline-variant/30 bg-surface-container p-3\"\n >\n <Avatar src={hoveredUser.avatar} alt={hoveredUser.name} size=\"xs\" />\n <span class=\"text-sm text-on-surface\"\n >Hovering: <strong>{hoveredUser.name}</strong></span\n >\n <Badge\n label={hoveredUser.status}\n variant=\"subtle\"\n color={statusColor[hoveredUser.status] ?? 'surface'}\n />\n </div>\n {:else}\n <p class=\"text-sm text-on-surface-variant/50\">Hover over a row</p>\n {/if}\n </section>\n\n <!-- ==================== CONTEXT MENU ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Context-Menu-Right-Click\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Context-Menu-Right-Click\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Context Menu (Right-Click)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Right-click a row to trigger the callback.</p>\n <Table\n data={users}\n columns={richColumns}\n rowKey=\"id\"\n onRowContextmenu={(row, _index, e) => {\n e.preventDefault()\n contextUser = { user: row, x: e.clientX, y: e.clientY }\n setTimeout(() => (contextUser = null), 2000)\n }}\n />\n {#if contextUser}\n <div\n class=\"flex items-center gap-3 rounded-xl border border-outline-variant/30 bg-surface-container p-3\"\n >\n <Icon name=\"lucide:mouse-pointer\" class=\"size-4 text-on-surface-variant\" />\n <span class=\"text-sm text-on-surface\">\n Right-clicked: <strong>{contextUser.user.name}</strong>\n at ({contextUser.x}, {contextUser.y})\n </span>\n </div>\n {/if}\n </section>\n\n <!-- ==================== BODY SLOTS ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Body-Top--Bottom-Slots\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Body-Top--Bottom-Slots\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Body Top / Bottom Slots\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Insert custom rows before and after data rows.\n </p>\n <Table data={products} columns={productColumns}>\n {#snippet bodyTopSlot()}\n <tr>\n <td\n colspan={4}\n class=\"border-b border-primary/10 bg-primary/5 px-4 py-2 text-xs font-medium text-primary\"\n >\n <Icon name=\"lucide:info\" class=\"-mt-0.5 mr-1 inline-block size-3.5\" />\n Prices updated March 2026. All items in stock.\n </td>\n </tr>\n {/snippet}\n {#snippet bodyBottomSlot()}\n <tr>\n <td\n colspan={4}\n class=\"border-t border-outline-variant/30 bg-surface-container-lowest px-4 py-2 text-xs text-on-surface-variant\"\n >\n <Icon\n name=\"lucide:arrow-right\"\n class=\"-mt-0.5 mr-1 inline-block size-3.5\"\n />\n Showing {products.length} of {products.length} products\n </td>\n </tr>\n {/snippet}\n </Table>\n </section>\n\n <!-- ==================== POLYMORPHIC (as prop) ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Polymorphic-Root-as-prop\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Polymorphic-Root-as-prop\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Polymorphic Root (as prop)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Render as <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\"\n >&lt;section&gt;</code\n >\n instead of\n <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\"\n >&lt;div&gt;</code\n >.\n </p>\n <Table\n as=\"section\"\n data={products.slice(0, 3)}\n columns={productColumns}\n caption=\"Q1 2026 Product Summary\"\n />\n </section>\n\n <!-- ==================== STRIPED ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Striped\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Striped\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Striped\n </a>\n</h2>\n <Table data={products} columns={productColumns} striped />\n </section>\n\n <!-- ==================== COLUMN RESIZING ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Column-Resizing\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Column-Resizing\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Column Resizing\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Drag the right edge of Name, Email, or Role headers to resize.\n </p>\n <Table data={users} columns={resizableColumns} bind:columnSizing rowKey=\"id\" />\n {#if Object.keys(columnSizing).length > 0}\n <p class=\"text-xs text-on-surface-variant\">\n Sizes: {Object.entries(columnSizing)\n .map(([k, v]) => `${k}: ${Math.round(v)}px`)\n .join(', ')}\n </p>\n {/if}\n </section>\n\n <!-- ==================== STICKY HEADER ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Sticky-Header\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Sticky-Header\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Sticky Header\n </a>\n</h2>\n <Table\n data={[...users, ...users, ...users]}\n columns={richColumns}\n sticky=\"header\"\n class=\"max-h-72\"\n />\n </section>\n\n <!-- ==================== LOADING (REPLACE DATA) ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Loading--Replace-Data\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Loading--Replace-Data\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Loading — Replace Data\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Data is hidden during loading.</p>\n <Button onclick={simulateLoadingReplace} size=\"sm\" variant=\"outline\" color=\"surface\">\n Simulate Loading (2s)\n </Button>\n <Table\n data={isLoadingReplace ? [] : users}\n columns={richColumns}\n loading={isLoadingReplace}\n rowKey=\"id\"\n >\n {#snippet loadingSlot()}\n <div class=\"flex items-center justify-center gap-2\">\n <Icon name=\"lucide:loader-2\" class=\"size-5 animate-spin text-primary\" />\n <span class=\"text-sm text-on-surface-variant\">Loading data...</span>\n </div>\n {/snippet}\n </Table>\n </section>\n\n <!-- ==================== LOADING (OVERLAY) ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Loading--Overlay\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Loading--Overlay\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Loading — Overlay\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Data stays visible with a frosted overlay.</p>\n <Button onclick={simulateLoadingOverlay} size=\"sm\" variant=\"outline\" color=\"surface\">\n Simulate Loading (2s)\n </Button>\n <Table data={users} columns={richColumns} loading={isLoadingOverlay} rowKey=\"id\" />\n </section>\n\n <!-- ==================== LOADING ANIMATIONS ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Loading-Animations\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Loading-Animations\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Loading Animations\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-6 md:grid-cols-2\">\n {#each ['carousel', 'carousel-inverse', 'swing', 'elastic'] as anim (anim)}\n <div class=\"space-y-1\">\n <p class=\"text-xs font-medium text-on-surface-variant\">{anim}</p>\n <Table\n data={products.slice(0, 2)}\n columns={productColumns}\n loading\n loadingAnimation={anim as\n | 'carousel'\n | 'carousel-inverse'\n | 'swing'\n | 'elastic'}\n />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- ==================== EMPTY ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Empty-State\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Empty-State\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Empty State\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-6 md:grid-cols-2\">\n <div class=\"space-y-1\">\n <p class=\"text-xs font-medium text-on-surface-variant\">Default</p>\n <Table data={[]} columns={richColumns} />\n </div>\n <div class=\"space-y-1\">\n <p class=\"text-xs font-medium text-on-surface-variant\">Custom</p>\n <Table data={[]} columns={richColumns}>\n {#snippet emptySlot()}\n <div class=\"flex flex-col items-center gap-2\">\n <Icon name=\"lucide:inbox\" class=\"size-8 text-on-surface-variant/40\" />\n <p class=\"text-sm text-on-surface-variant\">No users found</p>\n </div>\n {/snippet}\n </Table>\n </div>\n </div>\n </section>\n\n <!-- ==================== CUSTOM UI OVERRIDES ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Custom-UI-Overrides\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Custom-UI-Overrides\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom UI Overrides\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use the <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\"\n >ui</code\n > prop to override slot classes.\n </p>\n <div class=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <div class=\"space-y-1\">\n <p class=\"text-xs font-medium text-on-surface-variant\">\n Bordered rows + colored header\n </p>\n <Table\n data={products}\n columns={productColumns}\n ui={{\n root: 'border-2 border-primary/20',\n thead: 'bg-primary/5',\n th: 'text-primary',\n tr: 'border-b border-primary/10'\n }}\n />\n </div>\n <div class=\"space-y-1\">\n <p class=\"text-xs font-medium text-on-surface-variant\">Compact + no border</p>\n <Table\n data={products}\n columns={productColumns}\n ui={{\n root: 'border-0 rounded-none',\n th: 'py-2 px-3 text-[10px]',\n td: 'py-2 px-3 text-xs'\n }}\n />\n </div>\n </div>\n </section>\n\n <!-- ==================== CUSTOM HEADER SLOT ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Custom-Header-Slot\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Custom-Header-Slot\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Header Slot\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use the global <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\"\n >headerSlot</code\n > to customize all column headers.\n </p>\n <Table data={users.slice(0, 3)} columns={richColumns} rowKey=\"id\">\n {#snippet headerSlot({ column })}\n <div class=\"flex items-center gap-1.5\">\n <Icon\n name={column.key === 'name'\n ? 'lucide:user'\n : column.key === 'email'\n ? 'lucide:mail'\n : column.key === 'role'\n ? 'lucide:shield'\n : column.key === 'status'\n ? 'lucide:activity'\n : 'lucide:hash'}\n class=\"size-3.5 text-primary\"\n />\n <span>{column.label ?? column.key}</span>\n </div>\n {/snippet}\n </Table>\n </section>\n\n <!-- ==================== CUSTOM CELL SLOT ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Custom-Cell-Slot\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Custom-Cell-Slot\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Cell Slot\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use the global <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\"\n >cellSlot</code\n > to customize all cells at once.\n </p>\n <Table\n data={[\n { name: 'Alice', score: 95, grade: 'A' },\n { name: 'Bob', score: 72, grade: 'B' },\n { name: 'Charlie', score: 58, grade: 'C' }\n ]}\n >\n {#snippet cellSlot({ column, value })}\n {#if column.key === 'score'}\n <div class=\"flex items-center gap-2\">\n <div\n class=\"h-1.5 w-20 overflow-hidden rounded-full bg-surface-container-highest\"\n >\n <div\n class=\"h-full rounded-full transition-all {Number(value) >= 80\n ? 'bg-success'\n : Number(value) >= 60\n ? 'bg-warning'\n : 'bg-error'}\"\n style=\"width: {value}%\"\n ></div>\n </div>\n <span class=\"text-xs font-medium\">{value}</span>\n </div>\n {:else if column.key === 'grade'}\n <Badge\n label={String(value)}\n variant=\"soft\"\n color={value === 'A' ? 'success' : value === 'B' ? 'primary' : 'warning'}\n />\n {:else}\n <span class=\"font-medium\">{value}</span>\n {/if}\n {/snippet}\n </Table>\n </section>\n\n <!-- ==================== FOOTER SLOT ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Footer-Aggregation\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Footer-Aggregation\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Footer (Aggregation)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use per-column <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\"\n >footer</code\n > snippet for totals/aggregation.\n </p>\n <Table\n data={products}\n columns={[\n { key: 'name', label: 'Product', footer: footerLabel },\n { key: 'category', label: 'Category', cell: categoryCell },\n {\n key: 'price',\n label: 'Price',\n cell: priceCell,\n align: 'right',\n footer: footerTotal\n },\n { key: 'stock', label: 'Stock', cell: stockCell, align: 'right', footer: footerSum }\n ]}\n />\n </section>\n\n <!-- ==================== CAPTION ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Caption-Accessibility\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Caption-Accessibility\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Caption (Accessibility)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n The <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\"\n >caption</code\n >\n prop renders a sr-only caption for screen readers. Or use\n <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\"\n >captionSlot</code\n > for visible captions.\n </p>\n <Table\n data={products.slice(0, 3)}\n columns={productColumns}\n caption=\"Product inventory as of March 2026\"\n >\n {#snippet captionSlot()}\n <div\n class=\"not-sr-only border-b border-outline-variant/30 px-4 py-3 text-left text-sm font-medium text-on-surface-variant\"\n >\n <Icon name=\"lucide:table-2\" class=\"-mt-0.5 mr-1.5 inline-block size-4\" />\n Product inventory — March 2026\n </div>\n {/snippet}\n </Table>\n </section>\n</div>\n\n<!-- ==================== SNIPPET DEFINITIONS ==================== -->\n\n{#snippet userCell(props: TableCellSlotProps<User>)}\n <div class=\"flex items-center gap-3\">\n <Avatar src={props.row.avatar} alt={props.row.name} size=\"xs\" />\n <span class=\"font-medium\">{props.row.name}</span>\n </div>\n{/snippet}\n\n{#snippet roleCell(props: TableCellSlotProps<User>)}\n <Badge label={props.row.role} variant=\"soft\" color={roleColor[props.row.role] ?? 'secondary'} />\n{/snippet}\n\n{#snippet statusCell(props: TableCellSlotProps<User>)}\n <Badge\n label={props.row.status}\n variant=\"subtle\"\n color={statusColor[props.row.status] ?? 'surface'}\n />\n{/snippet}\n\n{#snippet categoryCell(props: TableCellSlotProps<Product>)}\n <Badge\n label={props.row.category}\n variant=\"soft\"\n color={props.row.category === 'Electronics' ? 'primary' : 'tertiary'}\n />\n{/snippet}\n\n{#snippet priceCell(props: TableCellSlotProps<Product>)}\n <span class=\"font-medium\">${props.row.price.toLocaleString()}</span>\n{/snippet}\n\n{#snippet stockCell(props: TableCellSlotProps<Product>)}\n <Badge\n label={String(props.row.stock)}\n variant=\"subtle\"\n color={props.row.stock < 15 ? 'error' : props.row.stock < 50 ? 'warning' : 'success'}\n />\n{/snippet}\n\n{#snippet actionsCell(props: TableCellSlotProps<User>)}\n <DropdownMenu items={getRowActions(props.row)}>\n <Button\n variant=\"ghost\"\n color=\"surface\"\n size=\"xs\"\n icon=\"lucide:ellipsis\"\n aria-label=\"Row actions\"\n />\n </DropdownMenu>\n{/snippet}\n\n{#snippet expandCell(props: TableCellSlotProps<User>)}\n {@const rowId = props.row.id}\n {@const isExpanded = expandedRows.includes(rowId)}\n <Button\n variant=\"ghost\"\n color=\"surface\"\n size=\"xs\"\n icon=\"lucide:chevron-right\"\n class=\"transition-transform duration-200 {isExpanded ? 'rotate-90' : ''}\"\n aria-label={isExpanded ? 'Collapse row' : 'Expand row'}\n aria-expanded={isExpanded}\n onclick={() => {\n if (isExpanded) {\n expandedRows = expandedRows.filter((k) => k !== rowId)\n } else {\n expandedRows = [...expandedRows, rowId]\n }\n }}\n />\n{/snippet}\n\n{#snippet deptExpandCell(props: TableCellSlotProps<Department>)}\n {@const deptId = props.row.id}\n {@const hasChildren = props.row.children && props.row.children.length > 0}\n {@const isExpanded = expandedDepts.includes(deptId)}\n {#if hasChildren}\n <Button\n variant=\"ghost\"\n color=\"surface\"\n size=\"xs\"\n icon=\"lucide:chevron-right\"\n class=\"transition-transform duration-200 {isExpanded ? 'rotate-90' : ''}\"\n aria-label={isExpanded ? 'Collapse' : 'Expand'}\n aria-expanded={isExpanded}\n onclick={() => {\n if (isExpanded) {\n expandedDepts = expandedDepts.filter((k) => k !== deptId)\n } else {\n expandedDepts = [...expandedDepts, deptId]\n }\n }}\n />\n {/if}\n{/snippet}\n\n{#snippet deptNameCell(props: TableCellSlotProps<Department>)}\n <div class=\"flex items-center gap-2\">\n <Icon name=\"lucide:building-2\" class=\"size-4 text-on-surface-variant\" />\n <span class=\"font-medium\">{props.row.name}</span>\n {#if props.row.children}\n <Badge\n label=\"{props.row.children.length} teams\"\n variant=\"soft\"\n color=\"tertiary\"\n size=\"xs\"\n />\n {/if}\n </div>\n{/snippet}\n\n{#snippet footerLabel(props: TableFooterSlotProps<Product>)}\n <span class=\"font-semibold text-on-surface\">Total ({props.rows.length} items)</span>\n{/snippet}\n\n{#snippet footerTotal(props: TableFooterSlotProps<Product>)}\n <span class=\"font-semibold text-on-surface\"\n >${props.rows.reduce((sum, r) => sum + r.price, 0).toLocaleString()}</span\n >\n{/snippet}\n\n{#snippet footerSum(props: TableFooterSlotProps<Product>)}\n <span class=\"font-semibold text-on-surface\"\n >{props.rows.reduce((sum, r) => sum + r.stock, 0)}</span\n >\n{/snippet}\n\n{#snippet pinActionCell(props: TableCellSlotProps<User>)}\n {@const isPinned = pinnedRows.includes(props.row.id)}\n <Button\n variant=\"ghost\"\n color={isPinned ? 'primary' : 'surface'}\n size=\"xs\"\n icon={isPinned ? 'lucide:pin-off' : 'lucide:pin'}\n aria-label={isPinned ? 'Unpin row' : 'Pin row'}\n onclick={() => {\n if (isPinned) {\n pinnedRows = pinnedRows.filter((k) => k !== props.row.id)\n } else {\n pinnedRows = [...pinnedRows, props.row.id]\n }\n }}\n />\n{/snippet}\n\n{#snippet pinPagActionCell(props: TableCellSlotProps<User>)}\n {@const isPinned = pinPagPinnedRows.includes(props.row.id)}\n <Button\n variant=\"ghost\"\n color={isPinned ? 'primary' : 'surface'}\n size=\"xs\"\n icon={isPinned ? 'lucide:pin-off' : 'lucide:pin'}\n aria-label={isPinned ? 'Unpin row' : 'Pin row'}\n onclick={() => {\n if (isPinned) {\n pinPagPinnedRows = pinPagPinnedRows.filter((k) => k !== props.row.id)\n } else {\n pinPagPinnedRows = [...pinPagPinnedRows, props.row.id]\n }\n }}\n />\n{/snippet}\n",
140
+ "table": "<script lang=\"ts\">\n import {\n Table,\n Badge,\n Avatar,\n Button,\n Input,\n Icon,\n Pagination,\n DropdownMenu,\n TableBulkActionBar,\n type TableColumn,\n type SortState,\n type TableCellSlotProps,\n type TableFooterSlotProps,\n type DropdownMenuItem\n } from '$lib/index.js'\n\n // ==================== Types ====================\n\n interface User {\n id: string\n name: string\n email: string\n role: string\n status: 'active' | 'inactive' | 'pending'\n avatar: string\n }\n\n interface Product {\n name: string\n category: string\n price: number\n stock: number\n }\n\n interface Department {\n id: string\n name: string\n manager: string\n employees: number\n budget: string\n children?: Department[]\n }\n\n // ==================== Data ====================\n\n const users: User[] = [\n {\n id: '1',\n name: 'Alice Johnson',\n email: 'alice@example.com',\n role: 'Admin',\n status: 'active',\n avatar: 'https://i.pravatar.cc/150?u=alice'\n },\n {\n id: '2',\n name: 'Bob Smith',\n email: 'bob@example.com',\n role: 'User',\n status: 'inactive',\n avatar: 'https://i.pravatar.cc/150?u=bob'\n },\n {\n id: '3',\n name: 'Charlie Brown',\n email: 'charlie@example.com',\n role: 'Editor',\n status: 'active',\n avatar: 'https://i.pravatar.cc/150?u=charlie'\n },\n {\n id: '4',\n name: 'Diana Prince',\n email: 'diana@example.com',\n role: 'Admin',\n status: 'pending',\n avatar: 'https://i.pravatar.cc/150?u=diana'\n },\n {\n id: '5',\n name: 'Eve Wilson',\n email: 'eve@example.com',\n role: 'User',\n status: 'active',\n avatar: 'https://i.pravatar.cc/150?u=eve'\n }\n ]\n\n const manyUsers: User[] = Array.from({ length: 50 }, (_, i) => ({\n id: String(i + 1),\n name: `User ${i + 1}`,\n email: `user${i + 1}@example.com`,\n role: ['Admin', 'User', 'Editor'][i % 3],\n status: (['active', 'inactive', 'pending'] as const)[i % 3],\n avatar: `https://i.pravatar.cc/150?u=user${i + 1}`\n }))\n\n const products: Product[] = [\n { name: 'MacBook Pro', category: 'Electronics', price: 2499, stock: 12 },\n { name: 'iPhone 16', category: 'Electronics', price: 999, stock: 45 },\n { name: 'AirPods Pro', category: 'Accessories', price: 249, stock: 100 },\n { name: 'Magic Keyboard', category: 'Accessories', price: 299, stock: 30 },\n { name: 'Studio Display', category: 'Electronics', price: 1599, stock: 8 }\n ]\n\n const departments: Department[] = [\n {\n id: 'eng',\n name: 'Engineering',\n manager: 'Alice Johnson',\n employees: 42,\n budget: '$2.1M',\n children: [\n {\n id: 'eng-fe',\n name: 'Frontend',\n manager: 'Bob Smith',\n employees: 15,\n budget: '$750K'\n },\n {\n id: 'eng-be',\n name: 'Backend',\n manager: 'Charlie Brown',\n employees: 18,\n budget: '$900K'\n },\n {\n id: 'eng-infra',\n name: 'Infrastructure',\n manager: 'Diana Prince',\n employees: 9,\n budget: '$450K'\n }\n ]\n },\n {\n id: 'design',\n name: 'Design',\n manager: 'Eve Wilson',\n employees: 12,\n budget: '$600K',\n children: [\n {\n id: 'design-ux',\n name: 'UX Research',\n manager: 'Frank Lee',\n employees: 5,\n budget: '$250K'\n },\n {\n id: 'design-ui',\n name: 'UI Design',\n manager: 'Grace Kim',\n employees: 7,\n budget: '$350K'\n }\n ]\n },\n {\n id: 'marketing',\n name: 'Marketing',\n manager: 'Henry Zhang',\n employees: 8,\n budget: '$400K'\n }\n ]\n\n // Flatten departments with parent-child for expandable\n const flatDepartments = $derived.by(() => {\n const result: (Department & { level: number; parentId?: string })[] = []\n for (const dept of departments) {\n result.push({ ...dept, level: 0 })\n }\n return result\n })\n\n const statusColor: Record<string, 'success' | 'error' | 'warning'> = {\n active: 'success',\n inactive: 'error',\n pending: 'warning'\n }\n\n const roleColor: Record<string, 'primary' | 'secondary' | 'tertiary'> = {\n Admin: 'primary',\n User: 'secondary',\n Editor: 'tertiary'\n }\n\n // ==================== Rich Columns ====================\n const richColumns: TableColumn<User>[] = [\n { key: 'id', label: '#' },\n { key: 'name', label: 'Name', cell: userCell },\n { key: 'email', label: 'Email' },\n { key: 'role', label: 'Role', cell: roleCell },\n { key: 'status', label: 'Status', cell: statusCell }\n ]\n\n // ==================== Sorting ====================\n let sorting: SortState = $state([{ key: 'name', direction: 'asc' }])\n\n const sortColumns: TableColumn<User>[] = [\n { key: 'id', label: '#' },\n { key: 'name', label: 'Name', sortable: true, cell: userCell },\n { key: 'email', label: 'Email' },\n { key: 'role', label: 'Role', sortable: true, cell: roleCell },\n { key: 'status', label: 'Status', sortable: true, cell: statusCell }\n ]\n\n // ==================== Row Actions ====================\n function getRowActions(row: User): DropdownMenuItem[] {\n return [\n {\n label: 'View profile',\n icon: 'lucide:user',\n onSelect: () => alert(`View ${row.name}`)\n },\n {\n label: 'Send email',\n icon: 'lucide:mail',\n onSelect: () => alert(`Email ${row.email}`)\n },\n { type: 'separator' },\n { label: 'Edit', icon: 'lucide:pencil', onSelect: () => alert(`Edit ${row.name}`) },\n {\n label: 'Delete',\n icon: 'lucide:trash-2',\n color: 'error',\n onSelect: () => alert(`Delete ${row.name}`)\n }\n ]\n }\n\n const actionColumns: TableColumn<User>[] = [\n { key: 'id', label: '#' },\n { key: 'name', label: 'Name', cell: userCell },\n { key: 'email', label: 'Email' },\n { key: 'role', label: 'Role', cell: roleCell },\n { key: 'status', label: 'Status', cell: statusCell },\n { key: 'avatar', label: '', cell: actionsCell, align: 'right', width: 60 }\n ]\n\n // ==================== Column Pinning ====================\n const pinColumns: TableColumn<User>[] = [\n { key: 'id', label: '#', width: 60 },\n { key: 'name', label: 'Name', cell: userCell, width: 200 },\n { key: 'email', label: 'Email', width: 220 },\n { key: 'role', label: 'Role', cell: roleCell, width: 120 },\n { key: 'status', label: 'Status', cell: statusCell, width: 120 },\n { key: 'avatar', label: '', cell: actionsCell, align: 'right', width: 80 }\n ]\n\n // ==================== Row Pinning ====================\n let pinnedRows: (string | number)[] = $state(['1', '3'])\n\n const rowPinColumns: TableColumn<User>[] = [\n { key: 'id', label: '#' },\n { key: 'name', label: 'Name', cell: userCell },\n { key: 'email', label: 'Email' },\n { key: 'role', label: 'Role', cell: roleCell },\n { key: 'status', label: 'Status', cell: statusCell },\n { key: 'avatar', label: '', cell: pinActionCell, align: 'right', width: 60 }\n ]\n\n // ==================== Expandable Rows ====================\n let expandedRows: (string | number)[] = $state([])\n let expandedDepts: (string | number)[] = $state([])\n\n const deptColumns: TableColumn<Department>[] = [\n { key: 'id', label: '', cell: deptExpandCell, width: 40 },\n { key: 'name', label: 'Department', cell: deptNameCell },\n { key: 'manager', label: 'Manager' },\n { key: 'employees', label: 'Employees', align: 'right' },\n { key: 'budget', label: 'Budget', align: 'right' }\n ]\n\n // ==================== Row Selection ====================\n let selectedRows: User[] = $state([])\n let bulkSelectedRows: User[] = $state([])\n\n // ==================== Column Visibility ====================\n let columnVisibility: Record<string, boolean> = $state({})\n\n const visibilityItems = [\n { value: 'id', label: '#' },\n { value: 'name', label: 'Name' },\n { value: 'email', label: 'Email' },\n { value: 'role', label: 'Role' },\n { value: 'status', label: 'Status' }\n ]\n\n function toggleColumnVisibility(col: string) {\n columnVisibility = {\n ...columnVisibility,\n [col]: columnVisibility[col] === false\n }\n }\n\n // ==================== Pagination ====================\n let paginationPage = $state(1)\n const pageSize = 10\n const tablePage = $derived(paginationPage - 1)\n\n // ==================== Row Pinning + Pagination ====================\n let pinPagPage = $state(1)\n const pinPagTablePage = $derived(pinPagPage - 1)\n let pinPagPinnedRows: (string | number)[] = $state(['1', '5', '12'])\n\n const rowPinPagColumns: TableColumn<User>[] = [\n { key: 'id', label: '#' },\n { key: 'name', label: 'Name', cell: userCell },\n { key: 'email', label: 'Email' },\n { key: 'role', label: 'Role', cell: roleCell },\n { key: 'status', label: 'Status', cell: statusCell },\n { key: 'avatar', label: '', cell: pinPagActionCell, align: 'right', width: 60 }\n ]\n\n // ==================== Selection + Pagination ====================\n let selPagPage = $state(1)\n const selPagTablePage = $derived(selPagPage - 1)\n let selPagSelected: User[] = $state([])\n\n // ==================== Global Filter ====================\n let globalFilter = $state('')\n\n // ==================== Column Filters ====================\n let columnFilterValues: Record<string, string> = $state({})\n\n const filterableColumns: TableColumn<User>[] = [\n { key: 'id', label: '#' },\n { key: 'name', label: 'Name', cell: userCell, filterable: true },\n { key: 'email', label: 'Email', filterable: true },\n { key: 'role', label: 'Role', cell: roleCell, filterable: true },\n { key: 'status', label: 'Status', cell: statusCell, filterable: true }\n ]\n\n // ==================== Full-Featured ====================\n let fullFeatSearch = $state('')\n let fullFeatSort: SortState = $state([])\n let fullFeatPage = $state(1)\n const fullFeatTablePage = $derived(fullFeatPage - 1)\n const fullFeatPageSize = 5\n\n const fullFeatColumns: TableColumn<User>[] = [\n { key: 'id', label: '#', sortable: true },\n { key: 'name', label: 'Name', cell: userCell, sortable: true, filterable: true },\n { key: 'email', label: 'Email', filterable: true },\n { key: 'role', label: 'Role', cell: roleCell, sortable: true, filterable: true },\n { key: 'status', label: 'Status', cell: statusCell, filterable: true }\n ]\n\n // ==================== Loading (Replace data) ====================\n let isLoadingReplace = $state(false)\n function simulateLoadingReplace() {\n isLoadingReplace = true\n setTimeout(() => (isLoadingReplace = false), 2000)\n }\n\n // ==================== Loading (Overlay) ====================\n let isLoadingOverlay = $state(false)\n function simulateLoadingOverlay() {\n isLoadingOverlay = true\n setTimeout(() => (isLoadingOverlay = false), 2000)\n }\n\n // ==================== Row Click ====================\n let selectedUser: User | undefined = $state(undefined)\n\n // ==================== Column Resizing ====================\n let columnSizing: Record<string, number> = $state({})\n\n const resizableColumns: TableColumn<User>[] = [\n { key: 'id', label: '#', width: 60 },\n { key: 'name', label: 'Name', cell: userCell, resizable: true, width: 200, minWidth: 120 },\n { key: 'email', label: 'Email', resizable: true, width: 220, minWidth: 150 },\n { key: 'role', label: 'Role', cell: roleCell, resizable: true, width: 120, minWidth: 80 },\n { key: 'status', label: 'Status', cell: statusCell, width: 120 }\n ]\n\n // ==================== Hover / Contextmenu ====================\n let hoveredUser: User | null = $state(null)\n let contextUser: { user: User; x: number; y: number } | null = $state(null)\n\n // ==================== Product columns ====================\n const productColumns: TableColumn<Product>[] = [\n { key: 'name', label: 'Product' },\n { key: 'category', label: 'Category', cell: categoryCell },\n { key: 'price', label: 'Price', cell: priceCell, align: 'right' },\n { key: 'stock', label: 'Stock', cell: stockCell, align: 'right' }\n ]\n</script>\n\n<div class=\"space-y-16\">\n <div>\n <h1 class=\"text-3xl font-bold text-on-surface\">Table</h1>\n <p class=\"mt-2 text-on-surface-variant\">Custom implementation — no external dependency.</p>\n </div>\n\n <!-- ==================== BASIC ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Basic\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Auto-generates columns from data keys.</p>\n <Table\n data={[\n { name: 'Alice', email: 'alice@example.com', role: 'Admin' },\n { name: 'Bob', email: 'bob@example.com', role: 'User' },\n { name: 'Charlie', email: 'charlie@example.com', role: 'Editor' }\n ]}\n />\n </section>\n\n <!-- ==================== RICH COLUMNS ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Rich-Columns\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Rich-Columns\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Rich Columns\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Custom cell snippets with Avatar, Badge components.\n </p>\n <Table data={users} columns={richColumns} rowKey=\"id\" />\n </section>\n\n <!-- ==================== SORTING (default asc on name) ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Sorting\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Sorting\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Sorting\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Default sort on <strong>Name (asc)</strong>. Click headers to toggle.\n </p>\n <Table data={users} columns={sortColumns} bind:sorting rowKey=\"id\" />\n {#if sorting.length > 0}\n <p class=\"text-xs text-on-surface-variant\">\n Sorting: <strong>{sorting[0].key}</strong> ({sorting[0].direction})\n </p>\n {/if}\n </section>\n\n <!-- ==================== GLOBAL FILTER ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Global-Filter\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Global-Filter\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Global Filter\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Filter across all columns.</p>\n <Input\n placeholder=\"Search users...\"\n bind:value={globalFilter}\n leadingIcon=\"lucide:search\"\n variant=\"outline\"\n class=\"max-w-sm\"\n />\n <Table data={users} columns={richColumns} bind:globalFilter rowKey=\"id\" />\n </section>\n\n <!-- ==================== COLUMN FILTERS ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Column-Filters\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Column-Filters\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Column Filters\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Add <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\">filterable: true</code> to a column definition to show an inline filter input below its header.\n Supports <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\">filterFn</code> for custom matching logic.\n </p>\n <Table\n data={manyUsers}\n columns={filterableColumns}\n bind:columnFilters={columnFilterValues}\n rowKey=\"id\"\n />\n {#if Object.keys(columnFilterValues).length > 0}\n <div class=\"flex flex-wrap items-center gap-1.5\">\n <span class=\"text-xs text-on-surface-variant\">Active filters:</span>\n {#each Object.entries(columnFilterValues) as [key, val] (key)}\n <Badge label=\"{key}: {val}\" variant=\"soft\" color=\"primary\" size=\"xs\" />\n {/each}\n <Button\n variant=\"ghost\"\n color=\"surface\"\n size=\"xs\"\n icon=\"lucide:x\"\n onclick={() => (columnFilterValues = {})}\n aria-label=\"Clear all column filters\"\n />\n </div>\n {/if}\n </section>\n\n <!-- ==================== FULL-FEATURED ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Full-Featured\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Full-Featured\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Full-Featured (Search + Filter + Sort + Pagination)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Compose <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\">&lt;Input&gt;</code>,\n <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\">&lt;Table&gt;</code>, and\n <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\">&lt;Pagination&gt;</code>\n together using <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\">bind:</code> for a full data-grid experience with no coupling.\n </p>\n <Input\n placeholder=\"Search across all columns…\"\n bind:value={fullFeatSearch}\n leadingIcon=\"lucide:search\"\n variant=\"outline\"\n class=\"max-w-sm\"\n />\n <Table\n data={manyUsers}\n columns={fullFeatColumns}\n bind:globalFilter={fullFeatSearch}\n bind:sorting={fullFeatSort}\n page={fullFeatTablePage}\n pageSize={fullFeatPageSize}\n rowKey=\"id\"\n multiSort\n />\n <div class=\"flex items-center justify-between\">\n <p class=\"text-sm text-on-surface-variant\">\n {#if fullFeatSearch}\n Showing results for <strong>&ldquo;{fullFeatSearch}&rdquo;</strong>\n {:else}\n {manyUsers.length} users total\n {/if}\n </p>\n <Pagination\n bind:page={fullFeatPage}\n total={manyUsers.length}\n itemsPerPage={fullFeatPageSize}\n showEdges\n size=\"sm\"\n />\n </div>\n </section>\n\n <!-- ==================== ROW ACTIONS ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Row-Actions\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Row-Actions\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Row Actions\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n DropdownMenu in the last column for per-row actions.\n </p>\n <Table data={users} columns={actionColumns} rowKey=\"id\" />\n </section>\n\n <!-- ==================== COLUMN PINNING ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Column-Pinning\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Column-Pinning\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Column Pinning\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n \"#\" pinned left, \"Actions\" pinned right. Scroll horizontally.\n </p>\n <div class=\"max-w-lg\">\n <Table\n data={users}\n columns={pinColumns}\n columnPinning={{ left: ['id'], right: ['avatar'] }}\n rowKey=\"id\"\n />\n </div>\n </section>\n\n <!-- ==================== ROW PINNING ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Row-Pinning\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Row-Pinning\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Row Pinning\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Pin rows to the top. Alice and Charlie are pinned by default. Click the pin icon to\n toggle.\n </p>\n <Table data={users} columns={rowPinColumns} bind:pinnedRows rowKey=\"id\" />\n {#if pinnedRows.length > 0}\n <p class=\"text-xs text-on-surface-variant\">\n Pinned: <strong>{pinnedRows.join(', ')}</strong>\n </p>\n {/if}\n </section>\n\n <!-- ==================== ROW PINNING + PAGINATION ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Row-Pinning--Pagination\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Row-Pinning--Pagination\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Row Pinning + Pagination\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Pinned rows (#1, #5, #12) stay at the top regardless of the current page.\n </p>\n <Table\n data={manyUsers}\n columns={rowPinPagColumns}\n bind:pinnedRows={pinPagPinnedRows}\n page={pinPagTablePage}\n {pageSize}\n rowKey=\"id\"\n />\n <div class=\"flex items-center justify-between\">\n {#if pinPagPinnedRows.length > 0}\n <Badge label=\"{pinPagPinnedRows.length} pinned\" variant=\"soft\" color=\"primary\" />\n {:else}\n <span class=\"text-sm text-on-surface-variant/50\">No pinned rows</span>\n {/if}\n <Pagination\n bind:page={pinPagPage}\n total={manyUsers.length}\n itemsPerPage={pageSize}\n showEdges\n size=\"sm\"\n />\n </div>\n </section>\n\n <!-- ==================== EXPANDABLE ROWS ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Expandable-Rows\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Expandable-Rows\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Expandable Rows\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Click the chevron to expand row details.</p>\n <Table\n data={users}\n columns={[\n { key: 'id', label: '', cell: expandCell, width: 40 },\n ...richColumns.slice(1)\n ]}\n bind:expandedRows\n rowKey=\"id\"\n >\n {#snippet expandedSlot({ row })}\n <div class=\"flex items-start gap-4 p-3\">\n <Avatar src={row.avatar} alt={row.name} size=\"lg\" />\n <div class=\"space-y-2\">\n <div>\n <p class=\"font-semibold text-on-surface\">{row.name}</p>\n <p class=\"text-sm text-on-surface-variant\">{row.email}</p>\n </div>\n <div class=\"flex gap-2\">\n <Badge\n label={row.role}\n variant=\"soft\"\n color={roleColor[row.role] ?? 'secondary'}\n />\n <Badge\n label={row.status}\n variant=\"subtle\"\n color={statusColor[row.status] ?? 'surface'}\n />\n </div>\n </div>\n </div>\n {/snippet}\n </Table>\n </section>\n\n <!-- ==================== EXPANDABLE: PARENT-CHILD ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Parent-Child-Rows\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Parent-Child-Rows\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Parent-Child Rows\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Expand departments to see sub-departments as a nested table.\n </p>\n <Table\n data={flatDepartments}\n columns={deptColumns}\n bind:expandedRows={expandedDepts}\n rowKey=\"id\"\n >\n {#snippet expandedSlot({ row })}\n {#if row.children && row.children.length > 0}\n <Table\n data={row.children}\n columns={[\n { key: 'name', label: 'Sub-department' },\n { key: 'manager', label: 'Manager' },\n { key: 'employees', label: 'Employees', align: 'right' },\n { key: 'budget', label: 'Budget', align: 'right' }\n ]}\n rowKey=\"id\"\n ui={{ root: 'border-0 rounded-none shadow-none' }}\n />\n {:else}\n <p class=\"py-2 text-sm text-on-surface-variant\">No sub-departments</p>\n {/if}\n {/snippet}\n </Table>\n </section>\n\n <!-- ==================== ROW SELECTION ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Row-Selection\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Row-Selection\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Row Selection\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Built-in checkbox column with select-all support.\n </p>\n <Table\n data={users}\n columns={richColumns}\n selection=\"multiple\"\n bind:selectedRows\n rowKey=\"id\"\n />\n {#if selectedRows.length > 0}\n <div class=\"flex items-center gap-2\">\n <Badge label=\"{selectedRows.length} selected\" variant=\"soft\" color=\"primary\" />\n <span class=\"text-sm text-on-surface-variant\"\n >{selectedRows.map((u) => u.name).join(', ')}</span\n >\n </div>\n {:else}\n <p class=\"text-sm text-on-surface-variant/50\">No rows selected</p>\n {/if}\n </section>\n\n <!-- ==================== SELECTION + PAGINATION ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Selection--Pagination\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Selection--Pagination\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Selection + Pagination\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Selections persist across pages. Select some rows, then navigate.\n </p>\n <Table\n data={manyUsers}\n columns={richColumns}\n selection=\"multiple\"\n bind:selectedRows={selPagSelected}\n page={selPagTablePage}\n {pageSize}\n rowKey=\"id\"\n />\n <div class=\"flex items-center justify-between\">\n {#if selPagSelected.length > 0}\n <Badge\n label=\"{selPagSelected.length} selected across pages\"\n variant=\"soft\"\n color=\"primary\"\n />\n {:else}\n <span class=\"text-sm text-on-surface-variant/50\">No rows selected</span>\n {/if}\n <Pagination\n bind:page={selPagPage}\n total={manyUsers.length}\n itemsPerPage={pageSize}\n showEdges\n size=\"sm\"\n />\n </div>\n </section>\n\n <!-- ==================== BULK ACTIONS ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Bulk-Actions\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Bulk-Actions\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Bulk Actions\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n A real-world example of using <code>TableBulkActionBar</code> when items are selected. Try selecting some rows below.\n </p>\n <div class=\"relative\">\n <Table\n data={users}\n columns={richColumns}\n selection=\"multiple\"\n bind:selectedRows={bulkSelectedRows}\n rowKey=\"id\"\n />\n </div>\n {#if bulkSelectedRows.length > 0}\n <TableBulkActionBar count={bulkSelectedRows.length} onClear={() => (bulkSelectedRows = [])}>\n <Button color=\"error\" size=\"sm\" icon=\"lucide:trash-2\" onclick={() => alert(`Deleted ${bulkSelectedRows.length} users`)}>\n Delete\n </Button>\n <Button variant=\"outline\" color=\"surface\" size=\"sm\" icon=\"lucide:download\" onclick={() => alert(`Exported CSV for ${bulkSelectedRows.length} users`)}>\n Export\n </Button>\n </TableBulkActionBar>\n {/if}\n </section>\n\n <!-- ==================== COLUMN VISIBILITY ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Column-Visibility\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Column-Visibility\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Column Visibility\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Toggle columns on/off.</p>\n <div class=\"flex flex-wrap gap-1.5\">\n {#each visibilityItems as item (item.value)}\n <button\n type=\"button\"\n class=\"rounded-full px-2.5 py-1 text-xs transition-colors {columnVisibility[\n item.value\n ] === false\n ? 'bg-surface-container text-on-surface-variant line-through'\n : 'bg-primary/10 text-primary'}\"\n onclick={() => toggleColumnVisibility(item.value)}\n >\n {item.label}\n </button>\n {/each}\n </div>\n <Table data={users} columns={richColumns} bind:columnVisibility rowKey=\"id\" />\n </section>\n\n <!-- ==================== PAGINATION ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Pagination\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Pagination\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Pagination\n </a>\n</h2>\n <Table data={manyUsers} columns={richColumns} page={tablePage} {pageSize} rowKey=\"id\" />\n <div class=\"flex items-center justify-between\">\n <p class=\"text-sm text-on-surface-variant\">\n {tablePage * pageSize + 1}–{Math.min((tablePage + 1) * pageSize, manyUsers.length)} of\n {manyUsers.length}\n </p>\n <Pagination\n bind:page={paginationPage}\n total={manyUsers.length}\n itemsPerPage={pageSize}\n showEdges\n size=\"sm\"\n />\n </div>\n </section>\n\n <!-- ==================== ROW CLICK ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Row-Click\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Row-Click\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Row Click\n </a>\n</h2>\n <Table\n data={users}\n columns={richColumns}\n onRowClick={(row) => (selectedUser = row)}\n rowKey=\"id\"\n />\n {#if selectedUser}\n <div\n class=\"flex items-center gap-3 rounded-xl border border-primary/20 bg-primary-container/15 p-3\"\n >\n <Avatar src={selectedUser.avatar} alt={selectedUser.name} size=\"sm\" />\n <div>\n <p class=\"text-sm font-medium text-on-surface\">{selectedUser.name}</p>\n <p class=\"text-xs text-on-surface-variant\">{selectedUser.email}</p>\n </div>\n <Badge\n label={selectedUser.role}\n variant=\"soft\"\n color={roleColor[selectedUser.role] ?? 'secondary'}\n class=\"ml-auto\"\n />\n </div>\n {/if}\n </section>\n\n <!-- ==================== ROW HOVER ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Row-Hover\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Row-Hover\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Row Hover\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Hover over rows to see the callback in action.\n </p>\n <Table\n data={users}\n columns={richColumns}\n rowKey=\"id\"\n onRowHover={(row) => (hoveredUser = row)}\n />\n {#if hoveredUser}\n <div\n class=\"flex items-center gap-3 rounded-xl border border-outline-variant/30 bg-surface-container p-3\"\n >\n <Avatar src={hoveredUser.avatar} alt={hoveredUser.name} size=\"xs\" />\n <span class=\"text-sm text-on-surface\"\n >Hovering: <strong>{hoveredUser.name}</strong></span\n >\n <Badge\n label={hoveredUser.status}\n variant=\"subtle\"\n color={statusColor[hoveredUser.status] ?? 'surface'}\n />\n </div>\n {:else}\n <p class=\"text-sm text-on-surface-variant/50\">Hover over a row</p>\n {/if}\n </section>\n\n <!-- ==================== CONTEXT MENU ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Context-Menu-Right-Click\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Context-Menu-Right-Click\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Context Menu (Right-Click)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Right-click a row to trigger the callback.</p>\n <Table\n data={users}\n columns={richColumns}\n rowKey=\"id\"\n onRowContextmenu={(row, _index, e) => {\n e.preventDefault()\n contextUser = { user: row, x: e.clientX, y: e.clientY }\n setTimeout(() => (contextUser = null), 2000)\n }}\n />\n {#if contextUser}\n <div\n class=\"flex items-center gap-3 rounded-xl border border-outline-variant/30 bg-surface-container p-3\"\n >\n <Icon name=\"lucide:mouse-pointer\" class=\"size-4 text-on-surface-variant\" />\n <span class=\"text-sm text-on-surface\">\n Right-clicked: <strong>{contextUser.user.name}</strong>\n at ({contextUser.x}, {contextUser.y})\n </span>\n </div>\n {/if}\n </section>\n\n <!-- ==================== BODY SLOTS ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Body-Top--Bottom-Slots\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Body-Top--Bottom-Slots\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Body Top / Bottom Slots\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Insert custom rows before and after data rows.\n </p>\n <Table data={products} columns={productColumns}>\n {#snippet bodyTopSlot()}\n <tr>\n <td\n colspan={4}\n class=\"border-b border-primary/10 bg-primary/5 px-4 py-2 text-xs font-medium text-primary\"\n >\n <Icon name=\"lucide:info\" class=\"-mt-0.5 mr-1 inline-block size-3.5\" />\n Prices updated March 2026. All items in stock.\n </td>\n </tr>\n {/snippet}\n {#snippet bodyBottomSlot()}\n <tr>\n <td\n colspan={4}\n class=\"border-t border-outline-variant/30 bg-surface-container-lowest px-4 py-2 text-xs text-on-surface-variant\"\n >\n <Icon\n name=\"lucide:arrow-right\"\n class=\"-mt-0.5 mr-1 inline-block size-3.5\"\n />\n Showing {products.length} of {products.length} products\n </td>\n </tr>\n {/snippet}\n </Table>\n </section>\n\n <!-- ==================== POLYMORPHIC (as prop) ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Polymorphic-Root-as-prop\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Polymorphic-Root-as-prop\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Polymorphic Root (as prop)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Render as <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\"\n >&lt;section&gt;</code\n >\n instead of\n <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\"\n >&lt;div&gt;</code\n >.\n </p>\n <Table\n as=\"section\"\n data={products.slice(0, 3)}\n columns={productColumns}\n caption=\"Q1 2026 Product Summary\"\n />\n </section>\n\n <!-- ==================== STRIPED ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Striped\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Striped\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Striped\n </a>\n</h2>\n <Table data={products} columns={productColumns} striped />\n </section>\n\n <!-- ==================== COLUMN RESIZING ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Column-Resizing\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Column-Resizing\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Column Resizing\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Drag the right edge of Name, Email, or Role headers to resize.\n </p>\n <Table data={users} columns={resizableColumns} bind:columnSizing rowKey=\"id\" />\n {#if Object.keys(columnSizing).length > 0}\n <p class=\"text-xs text-on-surface-variant\">\n Sizes: {Object.entries(columnSizing)\n .map(([k, v]) => `${k}: ${Math.round(v)}px`)\n .join(', ')}\n </p>\n {/if}\n </section>\n\n <!-- ==================== STICKY HEADER ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Sticky-Header\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Sticky-Header\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Sticky Header\n </a>\n</h2>\n <Table\n data={[...users, ...users, ...users]}\n columns={richColumns}\n sticky=\"header\"\n class=\"max-h-72\"\n />\n </section>\n\n <!-- ==================== LOADING (REPLACE DATA) ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Loading--Replace-Data\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Loading--Replace-Data\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Loading — Replace Data\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Data is hidden during loading.</p>\n <Button onclick={simulateLoadingReplace} size=\"sm\" variant=\"outline\" color=\"surface\">\n Simulate Loading (2s)\n </Button>\n <Table\n data={isLoadingReplace ? [] : users}\n columns={richColumns}\n loading={isLoadingReplace}\n rowKey=\"id\"\n >\n {#snippet loadingSlot()}\n <div class=\"flex items-center justify-center gap-2\">\n <Icon name=\"lucide:loader-2\" class=\"size-5 animate-spin text-primary\" />\n <span class=\"text-sm text-on-surface-variant\">Loading data...</span>\n </div>\n {/snippet}\n </Table>\n </section>\n\n <!-- ==================== LOADING (OVERLAY) ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Loading--Overlay\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Loading--Overlay\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Loading — Overlay\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Data stays visible with a frosted overlay.</p>\n <Button onclick={simulateLoadingOverlay} size=\"sm\" variant=\"outline\" color=\"surface\">\n Simulate Loading (2s)\n </Button>\n <Table data={users} columns={richColumns} loading={isLoadingOverlay} rowKey=\"id\" />\n </section>\n\n <!-- ==================== LOADING ANIMATIONS ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Loading-Animations\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Loading-Animations\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Loading Animations\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-6 md:grid-cols-2\">\n {#each ['carousel', 'carousel-inverse', 'swing', 'elastic'] as anim (anim)}\n <div class=\"space-y-1\">\n <p class=\"text-xs font-medium text-on-surface-variant\">{anim}</p>\n <Table\n data={products.slice(0, 2)}\n columns={productColumns}\n loading\n loadingAnimation={anim as\n | 'carousel'\n | 'carousel-inverse'\n | 'swing'\n | 'elastic'}\n />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- ==================== EMPTY ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Empty-State\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Empty-State\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Empty State\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-6 md:grid-cols-2\">\n <div class=\"space-y-1\">\n <p class=\"text-xs font-medium text-on-surface-variant\">Default</p>\n <Table data={[]} columns={richColumns} />\n </div>\n <div class=\"space-y-1\">\n <p class=\"text-xs font-medium text-on-surface-variant\">Custom</p>\n <Table data={[]} columns={richColumns}>\n {#snippet emptySlot()}\n <div class=\"flex flex-col items-center gap-2\">\n <Icon name=\"lucide:inbox\" class=\"size-8 text-on-surface-variant/40\" />\n <p class=\"text-sm text-on-surface-variant\">No users found</p>\n </div>\n {/snippet}\n </Table>\n </div>\n </div>\n </section>\n\n <!-- ==================== SIZE ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Size\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Size\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Size\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use the <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\"\n >size</code\n > prop to switch between compact, default, and spacious density presets.\n </p>\n <div class=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <div class=\"space-y-1\">\n <p class=\"text-xs font-medium text-on-surface-variant\">sm</p>\n <Table data={products} columns={productColumns} size=\"sm\" />\n </div>\n <div class=\"space-y-1\">\n <p class=\"text-xs font-medium text-on-surface-variant\">md</p>\n <Table data={products} columns={productColumns} size=\"md\" />\n </div>\n <div class=\"space-y-1\">\n <p class=\"text-xs font-medium text-on-surface-variant\">lg</p>\n <Table data={products} columns={productColumns} size=\"lg\" />\n </div>\n </div>\n </section>\n\n <!-- ==================== CUSTOM UI OVERRIDES ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Custom-UI-Overrides\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Custom-UI-Overrides\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom UI Overrides\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use the <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\"\n >ui</code\n > prop to override slot classes.\n </p>\n <div class=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <div class=\"space-y-1\">\n <p class=\"text-xs font-medium text-on-surface-variant\">\n Bordered rows + colored header\n </p>\n <Table\n data={products}\n columns={productColumns}\n ui={{\n root: 'border-2 border-primary/20',\n thead: 'bg-primary/5',\n th: 'text-primary',\n tr: 'border-b border-primary/10'\n }}\n />\n </div>\n <div class=\"space-y-1\">\n <p class=\"text-xs font-medium text-on-surface-variant\">Compact + no border</p>\n <Table\n data={products}\n columns={productColumns}\n size=\"sm\"\n ui={{\n root: 'border-0 rounded-none',\n th: 'text-[10px]'\n }}\n />\n </div>\n </div>\n </section>\n\n <!-- ==================== CUSTOM HEADER SLOT ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Custom-Header-Slot\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Custom-Header-Slot\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Header Slot\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use the global <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\"\n >headerSlot</code\n > to customize all column headers.\n </p>\n <Table data={users.slice(0, 3)} columns={richColumns} rowKey=\"id\">\n {#snippet headerSlot({ column })}\n <div class=\"flex items-center gap-1.5\">\n <Icon\n name={column.key === 'name'\n ? 'lucide:user'\n : column.key === 'email'\n ? 'lucide:mail'\n : column.key === 'role'\n ? 'lucide:shield'\n : column.key === 'status'\n ? 'lucide:activity'\n : 'lucide:hash'}\n class=\"size-3.5 text-primary\"\n />\n <span>{column.label ?? column.key}</span>\n </div>\n {/snippet}\n </Table>\n </section>\n\n <!-- ==================== CUSTOM CELL SLOT ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Custom-Cell-Slot\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Custom-Cell-Slot\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Cell Slot\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use the global <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\"\n >cellSlot</code\n > to customize all cells at once.\n </p>\n <Table\n data={[\n { name: 'Alice', score: 95, grade: 'A' },\n { name: 'Bob', score: 72, grade: 'B' },\n { name: 'Charlie', score: 58, grade: 'C' }\n ]}\n >\n {#snippet cellSlot({ column, value })}\n {#if column.key === 'score'}\n <div class=\"flex items-center gap-2\">\n <div\n class=\"h-1.5 w-20 overflow-hidden rounded-full bg-surface-container-highest\"\n >\n <div\n class=\"h-full rounded-full transition-all {Number(value) >= 80\n ? 'bg-success'\n : Number(value) >= 60\n ? 'bg-warning'\n : 'bg-error'}\"\n style=\"width: {value}%\"\n ></div>\n </div>\n <span class=\"text-xs font-medium\">{value}</span>\n </div>\n {:else if column.key === 'grade'}\n <Badge\n label={String(value)}\n variant=\"soft\"\n color={value === 'A' ? 'success' : value === 'B' ? 'primary' : 'warning'}\n />\n {:else}\n <span class=\"font-medium\">{value}</span>\n {/if}\n {/snippet}\n </Table>\n </section>\n\n <!-- ==================== FOOTER SLOT ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Footer-Aggregation\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Footer-Aggregation\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Footer (Aggregation)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use per-column <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\"\n >footer</code\n > snippet for totals/aggregation.\n </p>\n <Table\n data={products}\n columns={[\n { key: 'name', label: 'Product', footer: footerLabel },\n { key: 'category', label: 'Category', cell: categoryCell },\n {\n key: 'price',\n label: 'Price',\n cell: priceCell,\n align: 'right',\n footer: footerTotal\n },\n { key: 'stock', label: 'Stock', cell: stockCell, align: 'right', footer: footerSum }\n ]}\n />\n </section>\n\n <!-- ==================== CAPTION ==================== -->\n <section class=\"space-y-4\">\n <h2 id=\"Caption-Accessibility\" class=\"text-xl font-semibold text-on-surface\">\n<a href=\"#Caption-Accessibility\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Caption (Accessibility)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n The <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\"\n >caption</code\n >\n prop renders a sr-only caption for screen readers. Or use\n <code class=\"rounded-md bg-surface-container-high px-1.5 py-0.5 text-xs\"\n >captionSlot</code\n > for visible captions.\n </p>\n <Table\n data={products.slice(0, 3)}\n columns={productColumns}\n caption=\"Product inventory as of March 2026\"\n >\n {#snippet captionSlot()}\n <div\n class=\"not-sr-only border-b border-outline-variant/30 px-4 py-3 text-left text-sm font-medium text-on-surface-variant\"\n >\n <Icon name=\"lucide:table-2\" class=\"-mt-0.5 mr-1.5 inline-block size-4\" />\n Product inventory — March 2026\n </div>\n {/snippet}\n </Table>\n </section>\n</div>\n\n<!-- ==================== SNIPPET DEFINITIONS ==================== -->\n\n{#snippet userCell(props: TableCellSlotProps<User>)}\n <div class=\"flex items-center gap-3\">\n <Avatar src={props.row.avatar} alt={props.row.name} size=\"xs\" />\n <span class=\"font-medium\">{props.row.name}</span>\n </div>\n{/snippet}\n\n{#snippet roleCell(props: TableCellSlotProps<User>)}\n <Badge label={props.row.role} variant=\"soft\" color={roleColor[props.row.role] ?? 'secondary'} />\n{/snippet}\n\n{#snippet statusCell(props: TableCellSlotProps<User>)}\n <Badge\n label={props.row.status}\n variant=\"subtle\"\n color={statusColor[props.row.status] ?? 'surface'}\n />\n{/snippet}\n\n{#snippet categoryCell(props: TableCellSlotProps<Product>)}\n <Badge\n label={props.row.category}\n variant=\"soft\"\n color={props.row.category === 'Electronics' ? 'primary' : 'tertiary'}\n />\n{/snippet}\n\n{#snippet priceCell(props: TableCellSlotProps<Product>)}\n <span class=\"font-medium\">${props.row.price.toLocaleString()}</span>\n{/snippet}\n\n{#snippet stockCell(props: TableCellSlotProps<Product>)}\n <Badge\n label={String(props.row.stock)}\n variant=\"subtle\"\n color={props.row.stock < 15 ? 'error' : props.row.stock < 50 ? 'warning' : 'success'}\n />\n{/snippet}\n\n{#snippet actionsCell(props: TableCellSlotProps<User>)}\n <DropdownMenu items={getRowActions(props.row)}>\n <Button\n variant=\"ghost\"\n color=\"surface\"\n size=\"xs\"\n icon=\"lucide:ellipsis\"\n aria-label=\"Row actions\"\n />\n </DropdownMenu>\n{/snippet}\n\n{#snippet expandCell(props: TableCellSlotProps<User>)}\n {@const rowId = props.row.id}\n {@const isExpanded = expandedRows.includes(rowId)}\n <Button\n variant=\"ghost\"\n color=\"surface\"\n size=\"xs\"\n icon=\"lucide:chevron-right\"\n class=\"transition-transform duration-200 {isExpanded ? 'rotate-90' : ''}\"\n aria-label={isExpanded ? 'Collapse row' : 'Expand row'}\n aria-expanded={isExpanded}\n onclick={() => {\n if (isExpanded) {\n expandedRows = expandedRows.filter((k) => k !== rowId)\n } else {\n expandedRows = [...expandedRows, rowId]\n }\n }}\n />\n{/snippet}\n\n{#snippet deptExpandCell(props: TableCellSlotProps<Department>)}\n {@const deptId = props.row.id}\n {@const hasChildren = props.row.children && props.row.children.length > 0}\n {@const isExpanded = expandedDepts.includes(deptId)}\n {#if hasChildren}\n <Button\n variant=\"ghost\"\n color=\"surface\"\n size=\"xs\"\n icon=\"lucide:chevron-right\"\n class=\"transition-transform duration-200 {isExpanded ? 'rotate-90' : ''}\"\n aria-label={isExpanded ? 'Collapse' : 'Expand'}\n aria-expanded={isExpanded}\n onclick={() => {\n if (isExpanded) {\n expandedDepts = expandedDepts.filter((k) => k !== deptId)\n } else {\n expandedDepts = [...expandedDepts, deptId]\n }\n }}\n />\n {/if}\n{/snippet}\n\n{#snippet deptNameCell(props: TableCellSlotProps<Department>)}\n <div class=\"flex items-center gap-2\">\n <Icon name=\"lucide:building-2\" class=\"size-4 text-on-surface-variant\" />\n <span class=\"font-medium\">{props.row.name}</span>\n {#if props.row.children}\n <Badge\n label=\"{props.row.children.length} teams\"\n variant=\"soft\"\n color=\"tertiary\"\n size=\"xs\"\n />\n {/if}\n </div>\n{/snippet}\n\n{#snippet footerLabel(props: TableFooterSlotProps<Product>)}\n <span class=\"font-semibold text-on-surface\">Total ({props.rows.length} items)</span>\n{/snippet}\n\n{#snippet footerTotal(props: TableFooterSlotProps<Product>)}\n <span class=\"font-semibold text-on-surface\"\n >${props.rows.reduce((sum, r) => sum + r.price, 0).toLocaleString()}</span\n >\n{/snippet}\n\n{#snippet footerSum(props: TableFooterSlotProps<Product>)}\n <span class=\"font-semibold text-on-surface\"\n >{props.rows.reduce((sum, r) => sum + r.stock, 0)}</span\n >\n{/snippet}\n\n{#snippet pinActionCell(props: TableCellSlotProps<User>)}\n {@const isPinned = pinnedRows.includes(props.row.id)}\n <Button\n variant=\"ghost\"\n color={isPinned ? 'primary' : 'surface'}\n size=\"xs\"\n icon={isPinned ? 'lucide:pin-off' : 'lucide:pin'}\n aria-label={isPinned ? 'Unpin row' : 'Pin row'}\n onclick={() => {\n if (isPinned) {\n pinnedRows = pinnedRows.filter((k) => k !== props.row.id)\n } else {\n pinnedRows = [...pinnedRows, props.row.id]\n }\n }}\n />\n{/snippet}\n\n{#snippet pinPagActionCell(props: TableCellSlotProps<User>)}\n {@const isPinned = pinPagPinnedRows.includes(props.row.id)}\n <Button\n variant=\"ghost\"\n color={isPinned ? 'primary' : 'surface'}\n size=\"xs\"\n icon={isPinned ? 'lucide:pin-off' : 'lucide:pin'}\n aria-label={isPinned ? 'Unpin row' : 'Pin row'}\n onclick={() => {\n if (isPinned) {\n pinPagPinnedRows = pinPagPinnedRows.filter((k) => k !== props.row.id)\n } else {\n pinPagPinnedRows = [...pinPagPinnedRows, props.row.id]\n }\n }}\n />\n{/snippet}\n",
137
141
  "tree-view": "<script lang=\"ts\">\n import { TreeView } from '$lib/index.js'\n import type { TreeItem } from '$lib/index.js'\n \n const treeData: TreeItem[] = [\n {\n id: '1',\n label: 'src',\n icon: 'lucide:folder',\n children: [\n {\n id: '1-1',\n label: 'lib',\n icon: 'lucide:folder',\n children: [\n { id: '1-1-1', label: 'index.ts', icon: 'lucide:file-code' },\n { id: '1-1-2', label: 'utils.ts', icon: 'lucide:file-code' }\n ]\n },\n {\n id: '1-2',\n label: 'routes',\n icon: 'lucide:folder',\n children: [\n { id: '1-2-1', label: '+page.svelte', icon: 'lucide:file' },\n { id: '1-2-2', label: '+layout.svelte', icon: 'lucide:file' }\n ]\n }\n ]\n },\n {\n id: '2',\n label: 'package.json',\n icon: 'lucide:file-json'\n }\n ]\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">TreeView</h1>\n <p class=\"text-on-surface-variant\">\n A component for displaying hierarchical data in a collapsible tree structure.\n </p>\n </div>\n\n <!-- Usage -->\n <section class=\"space-y-3\">\n <h2 id=\"Usage\" class=\"text-lg font-semibold\">\n<a href=\"#Usage\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Usage\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Pass an array of <code class=\"rounded bg-surface-container-highest px-1\">TreeItem</code> objects to the <code class=\"rounded bg-surface-container-highest px-1\">items</code> prop. Each item must have an <code class=\"rounded bg-surface-container-highest px-1\">id</code> and <code class=\"rounded bg-surface-container-highest px-1\">label</code>.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4 flex justify-center\">\n <div class=\"w-full max-w-sm p-4 bg-surface rounded-lg border border-outline-variant\">\n <TreeView items={treeData} />\n </div>\n </div>\n </section>\n</div>\n",
138
142
  "checkbox": "<script lang=\"ts\">\n import { Checkbox, FormField, Separator } from '$lib/index.js'\n\n const colors = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'success',\n 'warning',\n 'error',\n 'info',\n 'surface'\n ] as const\n const sizes = ['xs', 'sm', 'md', 'lg', 'xl'] as const\n\n let bindChecked = $state(false)\n let indeterminate = $state(true)\n\n let allChecked = $state(false)\n let items = $state([\n { label: 'Emails', checked: false },\n { label: 'Push notifications', checked: false },\n { label: 'SMS alerts', checked: false }\n ])\n\n function syncSelectAll() {\n const all = items.every((i) => i.checked)\n const none = items.every((i) => !i.checked)\n allChecked = all\n indeterminate = !all && !none\n }\n\n function toggleAll(checked: boolean) {\n allChecked = checked\n indeterminate = false\n items = items.map((i) => ({ ...i, checked }))\n }\n</script>\n\n<div class=\"space-y-8\">\n <h1 class=\"text-2xl font-bold text-on-surface\">Checkbox</h1>\n\n <!-- Basic Usage -->\n <section class=\"space-y-4\">\n <h2 id=\"Basic-Usage\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Basic-Usage\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic Usage\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n A checkbox component for boolean and indeterminate states.\n </p>\n <div class=\"flex flex-wrap gap-6\">\n <Checkbox />\n <Checkbox checked={true} />\n </div>\n </section>\n\n <!-- Two-way Binding -->\n <section class=\"space-y-4\">\n <h2 id=\"Two-way-Binding\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Two-way-Binding\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Two-way Binding\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >bind:checked</code\n > for reactive two-way data binding.\n </p>\n <div class=\"flex flex-wrap items-center gap-6\">\n <Checkbox bind:checked={bindChecked} label=\"Toggle me\" />\n <p class=\"text-sm text-on-surface-variant\">\n Checked: <span class=\"font-mono text-on-surface\">{bindChecked}</span>\n </p>\n </div>\n </section>\n\n <!-- Label & Description -->\n <section class=\"space-y-4\">\n <h2 id=\"Label-amp-Description\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Label-amp-Description\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Label &amp; Description\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">label</code\n >\n and\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >description</code\n > to add text next to the checkbox.\n </p>\n <div class=\"flex flex-col gap-4\">\n <Checkbox label=\"Accept terms\" />\n <Checkbox\n label=\"Marketing emails\"\n description=\"Receive emails about new products and features.\"\n />\n <Checkbox\n label=\"Push notifications\"\n description=\"Get notified when someone mentions you.\"\n />\n </div>\n </section>\n\n <!-- Indeterminate -->\n <section class=\"space-y-4\">\n <h2 id=\"Indeterminate\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Indeterminate\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Indeterminate\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >bind:indeterminate</code\n > for tri-state behavior, useful for \"select all\" patterns.\n </p>\n <div class=\"flex flex-col gap-3\">\n <Checkbox\n checked={allChecked}\n {indeterminate}\n label=\"Select all\"\n onCheckedChange={toggleAll}\n />\n <div class=\"ms-6 flex flex-col gap-2\">\n {#each items as item, i (item.label)}\n <Checkbox\n bind:checked={items[i].checked}\n label={item.label}\n onCheckedChange={() => syncSelectAll()}\n />\n {/each}\n </div>\n </div>\n </section>\n\n <!-- Colors -->\n <section class=\"space-y-4\">\n <h2 id=\"Colors\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Colors\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Colors\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">color</code\n > to control the checked background color.\n </p>\n <div class=\"flex flex-wrap gap-6\">\n {#each colors as color (color)}\n <Checkbox {color} checked={true} label={color} />\n {/each}\n </div>\n </section>\n\n <!-- Sizes -->\n <section class=\"space-y-4\">\n <h2 id=\"Sizes\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Sizes\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Sizes\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">size</code> to\n control the dimensions.\n </p>\n <div class=\"flex flex-wrap items-center gap-6\">\n {#each sizes as size (size)}\n <Checkbox {size} checked={true} label={size} />\n {/each}\n </div>\n </section>\n\n <!-- Variant -->\n <section class=\"space-y-4\">\n <h2 id=\"Variant\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Variant\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Variant\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >variant=\"card\"</code\n > to display the checkbox inside a bordered card. The border highlights when checked.\n </p>\n <div class=\"flex max-w-sm flex-col gap-3\">\n <Checkbox\n variant=\"card\"\n label=\"Email notifications\"\n description=\"Receive alerts in your inbox.\"\n />\n <Checkbox\n variant=\"card\"\n checked={true}\n label=\"Push notifications\"\n description=\"Alerts on your device.\"\n color=\"primary\"\n />\n <Checkbox\n variant=\"card\"\n checked={true}\n label=\"SMS alerts\"\n description=\"Text messages for critical updates.\"\n color=\"success\"\n />\n <Checkbox\n variant=\"card\"\n disabled\n label=\"Fax (unavailable)\"\n description=\"This option is currently disabled.\"\n />\n </div>\n </section>\n\n <!-- Indicator -->\n <section class=\"space-y-4\">\n <h2 id=\"Indicator-Position\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Indicator-Position\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Indicator Position\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >indicator</code\n >\n to control where the checkbox appears:\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">start</code>,\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">end</code>, or\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">hidden</code>.\n </p>\n <div class=\"flex max-w-sm flex-col gap-4\">\n <Checkbox\n indicator=\"start\"\n label=\"Start (default)\"\n description=\"Checkbox on the left.\"\n />\n <Checkbox\n indicator=\"end\"\n label=\"End\"\n description=\"Checkbox on the right.\"\n checked={true}\n />\n <Checkbox\n indicator=\"hidden\"\n label=\"Hidden\"\n description=\"Checkbox hidden, label only.\"\n checked={true}\n color=\"secondary\"\n />\n </div>\n <div class=\"flex max-w-sm flex-col gap-3\">\n <p class=\"text-xs text-on-surface-variant\">Combined with card variant</p>\n <Checkbox\n variant=\"card\"\n indicator=\"end\"\n label=\"Opt in to beta features\"\n description=\"Get early access to new features.\"\n checked={true}\n color=\"tertiary\"\n />\n </div>\n </section>\n\n <!-- Icons -->\n <section class=\"space-y-4\">\n <h2 id=\"Icons\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Icons\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Icons\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">icon</code>\n and\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >indeterminateIcon</code\n > to customize the icons.\n </p>\n <div class=\"flex flex-col gap-4\">\n <Checkbox icon=\"lucide:heart\" checked={true} label=\"Favorite\" color=\"error\" />\n <Checkbox icon=\"lucide:star\" checked={true} label=\"Starred\" color=\"warning\" />\n <Checkbox icon=\"lucide:thumbs-up\" checked={true} label=\"Liked\" color=\"success\" />\n </div>\n </section>\n\n <!-- Loading -->\n <section class=\"space-y-4\">\n <h2 id=\"Loading\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Loading\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Loading\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >loading</code\n > to show a spinner and disable interaction.\n </p>\n <div class=\"flex flex-col gap-4\">\n <Checkbox loading label=\"Syncing...\" />\n <Checkbox loading checked={true} label=\"Saving preferences...\" color=\"success\" />\n </div>\n </section>\n\n <!-- Disabled -->\n <section class=\"space-y-4\">\n <h2 id=\"Disabled\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Disabled\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Disabled\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >disabled</code\n > to prevent interaction.\n </p>\n <div class=\"flex flex-wrap gap-6\">\n <Checkbox disabled label=\"Disabled (off)\" />\n <Checkbox disabled checked={true} label=\"Disabled (on)\" />\n </div>\n </section>\n\n <!-- Required -->\n <section class=\"space-y-4\">\n <h2 id=\"Required\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Required\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Required\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >required</code\n > to show an asterisk next to the label.\n </p>\n <Checkbox required label=\"Accept terms and conditions\" />\n </section>\n\n <!-- Custom Slots -->\n <section class=\"space-y-4\">\n <h2 id=\"Custom-Slots\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Custom-Slots\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Slots\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >labelSlot</code\n >\n and\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >descriptionSlot</code\n > for fully custom label content.\n </p>\n <div class=\"flex flex-col gap-4\">\n <Checkbox checked={true} color=\"success\">\n {#snippet labelSlot()}\n <span class=\"flex items-center gap-1.5 text-sm font-medium text-on-surface\">\n <span class=\"inline-block size-2 rounded-full bg-success\"></span>\n System online\n </span>\n {/snippet}\n {#snippet descriptionSlot()}\n <span class=\"text-xs text-on-surface-variant\">\n All services are running normally. Last checked <strong>just now</strong>.\n </span>\n {/snippet}\n </Checkbox>\n\n <Checkbox>\n {#snippet labelSlot()}\n <span class=\"flex items-center gap-2 text-sm font-medium text-on-surface\">\n Beta features\n <span\n class=\"rounded bg-tertiary-container px-1.5 py-0.5 text-[10px] font-bold text-on-tertiary-container\"\n >BETA</span\n >\n </span>\n {/snippet}\n </Checkbox>\n </div>\n </section>\n\n <!-- FormField Integration -->\n <section class=\"space-y-4\">\n <h2 id=\"FormField-Integration\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#FormField-Integration\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n FormField Integration\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n When used inside a <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">FormField</code\n >, the Checkbox automatically inherits size and error state.\n </p>\n <div class=\"max-w-sm space-y-4\">\n <FormField label=\"Preferences\" description=\"Choose your notification preferences.\">\n <Checkbox label=\"Email notifications\" />\n </FormField>\n <FormField label=\"Agreement\" error=\"You must accept the terms.\">\n <Checkbox label=\"I accept the terms of service\" />\n </FormField>\n </div>\n </section>\n\n <Separator />\n\n <!-- Real World Examples -->\n <section class=\"space-y-4\">\n <h2 id=\"Real-World-Examples\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Real-World-Examples\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World Examples\n </a>\n</h2>\n\n <div class=\"space-y-6\">\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Notification preferences (list)</p>\n <div\n class=\"max-w-sm space-y-3 rounded-lg border border-outline-variant bg-surface-container-low p-4\"\n >\n <Checkbox\n checked={true}\n label=\"Email digest\"\n description=\"Daily summary of activity.\"\n color=\"primary\"\n />\n <Checkbox\n checked={true}\n label=\"Push notifications\"\n description=\"Alerts on your device.\"\n color=\"primary\"\n />\n <Checkbox\n label=\"SMS alerts\"\n description=\"Text messages for critical updates.\"\n color=\"primary\"\n />\n <Checkbox\n loading\n label=\"Marketing emails\"\n description=\"Updating preference...\"\n color=\"primary\"\n />\n </div>\n </div>\n\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Plan selection (card variant)</p>\n <div class=\"max-w-sm space-y-3\">\n <Checkbox\n variant=\"card\"\n indicator=\"end\"\n checked={true}\n color=\"primary\"\n label=\"Starter\"\n description=\"$9/mo · Up to 3 projects\"\n />\n <Checkbox\n variant=\"card\"\n indicator=\"end\"\n color=\"primary\"\n label=\"Pro\"\n description=\"$29/mo · Unlimited projects\"\n />\n <Checkbox\n variant=\"card\"\n indicator=\"end\"\n color=\"primary\"\n label=\"Enterprise\"\n description=\"Custom pricing · SSO + advanced security\"\n />\n </div>\n </div>\n\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Permissions (hidden indicator)</p>\n <div\n class=\"max-w-sm space-y-2 rounded-lg border border-outline-variant bg-surface-container-low p-4\"\n >\n <Checkbox indicator=\"hidden\" checked={true} color=\"success\">\n {#snippet labelSlot()}\n <span\n class=\"flex w-full items-center justify-between text-sm text-on-surface\"\n >\n Read\n <span\n class=\"rounded bg-success-container px-1.5 py-0.5 text-[10px] font-semibold text-on-success-container\"\n >Granted</span\n >\n </span>\n {/snippet}\n </Checkbox>\n <Checkbox indicator=\"hidden\" checked={true} color=\"success\">\n {#snippet labelSlot()}\n <span\n class=\"flex w-full items-center justify-between text-sm text-on-surface\"\n >\n Write\n <span\n class=\"rounded bg-success-container px-1.5 py-0.5 text-[10px] font-semibold text-on-success-container\"\n >Granted</span\n >\n </span>\n {/snippet}\n </Checkbox>\n <Checkbox indicator=\"hidden\">\n {#snippet labelSlot()}\n <span\n class=\"flex w-full items-center justify-between text-sm text-on-surface-variant\"\n >\n Admin\n <span\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-[10px] font-semibold text-on-surface-variant\"\n >Denied</span\n >\n </span>\n {/snippet}\n </Checkbox>\n </div>\n </div>\n </div>\n </section>\n</div>\n",
139
143
  "color-picker": "<script lang=\"ts\">\n import { ColorPicker } from '$lib/index.js'\n \n let color1 = $state('#3b82f6')\n let color2 = $state('#f43f5e')\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">ColorPicker</h1>\n <p class=\"text-on-surface-variant\">\n A flexible component for selecting colors via swatches, hex input, or native color picker.\n </p>\n </div>\n\n <!-- Usage -->\n <section class=\"space-y-3\">\n <h2 id=\"Usage\" class=\"text-lg font-semibold\">\n<a href=\"#Usage\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Usage\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Bind a hex string variable to the <code class=\"rounded bg-surface-container-highest px-1\">value</code> prop using <code class=\"rounded bg-surface-container-highest px-1\">bind:value</code>.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4 flex flex-col gap-4 max-w-sm\">\n <ColorPicker bind:value={color1} />\n <div class=\"text-sm text-on-surface-variant\">\n Selected Color: <span class=\"font-mono font-bold\" style=\"color: {color1};\">{color1}</span>\n </div>\n </div>\n </section>\n\n <!-- Disabled State -->\n <section class=\"space-y-3\">\n <h2 id=\"Disabled\" class=\"text-lg font-semibold\">\n<a href=\"#Disabled\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Disabled\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use the <code class=\"rounded bg-surface-container-highest px-1\">disabled</code> prop to prevent interaction.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4 flex flex-col gap-4 max-w-sm\">\n <ColorPicker bind:value={color2} disabled />\n </div>\n </section>\n</div>\n",
140
144
  "checkbox-group": "<script lang=\"ts\">\n import { CheckboxGroup } from '$lib/index.js'\n import type { CheckboxGroupItem } from '$lib/index.js'\n\n const colors = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'success',\n 'warning',\n 'error',\n 'info',\n 'surface'\n ] as const\n\n const sizes = ['xs', 'sm', 'md', 'lg', 'xl'] as const\n\n const basicItems: CheckboxGroupItem[] = [\n { value: 'svelte', label: 'Svelte' },\n { value: 'react', label: 'React' },\n { value: 'vue', label: 'Vue' }\n ]\n\n const itemsWithDesc: CheckboxGroupItem[] = [\n {\n value: 'email',\n label: 'Email',\n description: 'Receive notifications via email'\n },\n {\n value: 'sms',\n label: 'SMS',\n description: 'Receive notifications via text message'\n },\n {\n value: 'push',\n label: 'Push',\n description: 'Receive browser push notifications'\n }\n ]\n\n const itemsWithDisabled: CheckboxGroupItem[] = [\n { value: 'read', label: 'Read' },\n { value: 'write', label: 'Write' },\n { value: 'delete', label: 'Delete', disabled: true },\n { value: 'admin', label: 'Admin', disabled: true }\n ]\n\n let basicValue = $state(['svelte'])\n let notifValue = $state(['email'])\n let cardValue = $state(['email'])\n let horizontalValue = $state<string[]>([])\n let permissionValue = $state(['read'])\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">CheckboxGroup</h1>\n <p class=\"text-on-surface-variant\">\n Group of checkboxes for selecting multiple values from a list.\n </p>\n </div>\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <CheckboxGroup items={basicItems} bind:value={basicValue} legend=\"Frameworks\" />\n <p class=\"mt-3 text-sm text-on-surface-variant\">\n Selected: {basicValue.join(', ') || 'none'}\n </p>\n </div>\n </section>\n\n <!-- With Description -->\n <section class=\"space-y-3\">\n <h2 id=\"With-Description\" class=\"text-lg font-semibold\">\n<a href=\"#With-Description\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n With Description\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <CheckboxGroup\n items={itemsWithDesc}\n bind:value={notifValue}\n legend=\"Notifications\"\n required\n />\n </div>\n </section>\n\n <!-- Variants -->\n <section class=\"space-y-3\">\n <h2 id=\"Variants\" class=\"text-lg font-semibold\">\n<a href=\"#Variants\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Variants\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-4 rounded-lg bg-surface-container-high p-4 sm:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">List (default)</p>\n <CheckboxGroup items={basicItems} value={['svelte']} legend=\"Pick frameworks\" />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Card</p>\n <CheckboxGroup\n items={itemsWithDesc}\n bind:value={cardValue}\n variant=\"card\"\n legend=\"Notifications\"\n />\n </div>\n </div>\n </section>\n\n <!-- Orientation -->\n <section class=\"space-y-3\">\n <h2 id=\"Orientation\" class=\"text-lg font-semibold\">\n<a href=\"#Orientation\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Orientation\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-4\">\n <div>\n <p class=\"mb-2 text-sm font-medium text-on-surface-variant\">Vertical (default)</p>\n <CheckboxGroup items={basicItems} value={['svelte', 'vue']} />\n </div>\n <div>\n <p class=\"mb-2 text-sm font-medium text-on-surface-variant\">Horizontal</p>\n <CheckboxGroup\n items={basicItems}\n bind:value={horizontalValue}\n orientation=\"horizontal\"\n />\n </div>\n <div>\n <p class=\"mb-2 text-sm font-medium text-on-surface-variant\">Horizontal · Card</p>\n <CheckboxGroup\n items={basicItems}\n value={['react']}\n variant=\"card\"\n orientation=\"horizontal\"\n />\n </div>\n </div>\n </section>\n\n <!-- Colors -->\n <section class=\"space-y-3\">\n <h2 id=\"Colors\" class=\"text-lg font-semibold\">\n<a href=\"#Colors\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Colors\n </a>\n</h2>\n <div class=\"space-y-3 rounded-lg bg-surface-container-high p-4\">\n {#each colors as color (color)}\n <CheckboxGroup items={[{ value: color, label: color }]} value={[color]} {color} />\n {/each}\n </div>\n </section>\n\n <!-- Sizes -->\n <section class=\"space-y-3\">\n <h2 id=\"Sizes\" class=\"text-lg font-semibold\">\n<a href=\"#Sizes\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Sizes\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-4\">\n {#each sizes as size (size)}\n <div class=\"flex items-center gap-4\">\n <span class=\"w-6 text-xs text-on-surface-variant\">{size}</span>\n <CheckboxGroup\n items={basicItems}\n value={['svelte']}\n {size}\n orientation=\"horizontal\"\n />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Indicator Position -->\n <section class=\"space-y-3\">\n <h2 id=\"Indicator-Position\" class=\"text-lg font-semibold\">\n<a href=\"#Indicator-Position\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Indicator Position\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-4 rounded-lg bg-surface-container-high p-4 sm:grid-cols-3\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Start (default)</p>\n <CheckboxGroup items={itemsWithDesc} value={['email']} indicator=\"start\" />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">End</p>\n <CheckboxGroup items={itemsWithDesc} value={['email']} indicator=\"end\" />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Hidden</p>\n <CheckboxGroup items={itemsWithDesc} value={['email']} indicator=\"hidden\" />\n </div>\n </div>\n </section>\n\n <!-- Disabled -->\n <section class=\"space-y-3\">\n <h2 id=\"Disabled\" class=\"text-lg font-semibold\">\n<a href=\"#Disabled\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Disabled\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-4 rounded-lg bg-surface-container-high p-4 sm:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Entire group disabled</p>\n <CheckboxGroup items={basicItems} value={['svelte']} disabled />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Per-item disabled</p>\n <CheckboxGroup\n items={itemsWithDisabled}\n bind:value={permissionValue}\n legend=\"Permissions\"\n />\n </div>\n </div>\n </section>\n\n <!-- Custom Slots -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-Slots\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-Slots\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Slots\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <CheckboxGroup items={itemsWithDesc} value={['email', 'push']}>\n {#snippet legendSlot()}\n <span class=\"mb-1 block text-sm font-semibold text-primary\">\n Notification channels\n </span>\n {/snippet}\n {#snippet labelSlot({ item })}\n <span class=\"ms-2 text-sm font-semibold text-on-surface\">{item.label}</span>\n {/snippet}\n {#snippet descriptionSlot({ item })}\n <span class=\"ms-2 text-xs text-on-surface-variant italic\"\n >{item.description}</span\n >\n {/snippet}\n </CheckboxGroup>\n </div>\n </section>\n\n <!-- Real World Examples -->\n <section class=\"space-y-3\">\n <h2 id=\"Real-World-Examples\" class=\"text-lg font-semibold\">\n<a href=\"#Real-World-Examples\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World Examples\n </a>\n</h2>\n <div class=\"space-y-6 rounded-lg bg-surface-container-high p-4\">\n <!-- Settings form -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Settings Form</p>\n <div\n class=\"max-w-sm space-y-4 rounded-lg border border-outline-variant bg-surface-container p-4\"\n >\n <CheckboxGroup\n items={[\n {\n value: 'marketing',\n label: 'Marketing emails',\n description: 'Promotions and offers'\n },\n {\n value: 'updates',\n label: 'Product updates',\n description: 'New features and improvements'\n },\n {\n value: 'security',\n label: 'Security alerts',\n description: 'Login and activity alerts'\n }\n ]}\n value={['security']}\n legend=\"Email preferences\"\n variant=\"card\"\n color=\"primary\"\n />\n </div>\n </div>\n\n <!-- Filter tags -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Filter Tags</p>\n <CheckboxGroup\n items={[\n { value: 'design', label: 'Design' },\n { value: 'engineering', label: 'Engineering' },\n { value: 'product', label: 'Product' },\n { value: 'marketing', label: 'Marketing' }\n ]}\n value={['design', 'engineering']}\n orientation=\"horizontal\"\n color=\"tertiary\"\n size=\"sm\"\n />\n </div>\n\n <!-- Permission checklist -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Permission Checklist</p>\n <CheckboxGroup\n items={[\n { value: 'read', label: 'Read', description: 'View resources' },\n {\n value: 'write',\n label: 'Write',\n description: 'Create and edit resources'\n },\n { value: 'delete', label: 'Delete', description: 'Remove resources' },\n {\n value: 'admin',\n label: 'Admin',\n description: 'Full access',\n disabled: true\n }\n ]}\n value={['read', 'write']}\n legend=\"API permissions\"\n variant=\"card\"\n color=\"error\"\n indicator=\"end\"\n />\n </div>\n </div>\n </section>\n</div>\n",
141
- "editor": "<script lang=\"ts\">\n import { Editor } from '$lib/components/Editor/index.js'\n import type { EditorApi, EditorJSON, MentionItem } from '$lib/components/Editor/index.js'\n import { Button, Badge, Separator, Card, Icon, Form, FormField, Input } from '$lib/index.js'\n\n let basicHtml = $state('<p>Start writing here…</p>')\n\n let jsonValue = $state<EditorJSON>({\n type: 'doc',\n content: [\n {\n type: 'paragraph',\n content: [{ type: 'text', text: 'This document is stored as JSON.' }]\n }\n ]\n })\n\n let customToolbarValue = $state('<p>Only <strong>bold</strong> and <em>italic</em>.</p>')\n\n let api = $state<EditorApi>()\n let apiValue = $state('<p>Drive me from outside.</p>')\n\n let limitedValue = $state('<p>Type here…</p>')\n\n let bubbleValue = $state('<p>Select any text to see the floating menu.</p>')\n\n // ----- Phase 2 demos -----\n\n let markdownValue = $state('# Hello\\n\\nThis editor outputs **Markdown**.')\n\n let imageValue = $state('<p>Click the image button to upload.</p>')\n async function fakeUploadImage(file: File): Promise<string> {\n // Demo: convert to data URL. Real apps would upload to a backend.\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.onload = () => resolve(String(reader.result))\n reader.onerror = () => reject(new Error('FileReader failed'))\n reader.readAsDataURL(file)\n })\n }\n\n let tableValue = $state('<p>Click the table button → pick dimensions → insert.</p>')\n\n let slashValue = $state('<p>Type / to open the command menu…</p>')\n let youtubeValue = $state(\n '<p>Click the YouTube button or use / → YouTube to embed a video.</p>'\n )\n let dragHandleValue = $state(\n '<h2 id=\"Drag-me\">Drag me!</h2><p>Hover any block to see the drag handle appear on the left. Drag to reorder.</p><ul><li>First item</li><li>Second item</li><li>Third item</li></ul>'\n )\n\n let mentionValue = $state('<p>Type @ to mention someone…</p>')\n const teamMembers: MentionItem[] = [\n { id: 'alice', label: 'Alice Nguyen' },\n { id: 'bob', label: 'Bob Tran' },\n { id: 'charlie', label: 'Charlie Le' },\n { id: 'diana', label: 'Diana Pham' },\n { id: 'evan', label: 'Evan Vo' }\n ]\n async function searchMembers(query: string): Promise<MentionItem[]> {\n await new Promise((r) => setTimeout(r, 50))\n const q = query.toLowerCase()\n return teamMembers.filter((m) => m.label.toLowerCase().includes(q))\n }\n\n const articleState = $state({ title: '', body: '<p></p>' })\n let articleSubmitted = $state<string | null>(null)\n function validateArticle(values: object) {\n const v = values as { title: string; body: string }\n const errors: { name: string; message: string }[] = []\n if (!v.title.trim()) errors.push({ name: 'title', message: 'Title is required' })\n if (v.body === '<p></p>' || !v.body.trim())\n errors.push({ name: 'body', message: 'Body cannot be empty' })\n return errors\n }\n function submitArticle(e: { data: unknown }) {\n articleSubmitted = JSON.stringify(e.data, null, 2)\n }\n\n const colors = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'success',\n 'warning',\n 'error',\n 'info',\n 'surface'\n ] as const\n\n let serializedJson = $derived(JSON.stringify(jsonValue, null, 2))\n\n const importExample = \"import { Editor } from 'svelora/editor'\"\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">Editor</h1>\n <p class=\"text-on-surface-variant\">\n Rich-text WYSIWYG editor built on <strong>Tiptap v3</strong> + ProseMirror. Imported via\n the sub-export\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >svelora/editor</code\n >\n — only adds Tiptap to your bundle when actually used.\n </p>\n <div class=\"flex flex-wrap gap-2 pt-1\">\n <Badge variant=\"soft\" color=\"info\" label=\"Phase 1 (v1.8)\" />\n <Badge variant=\"soft\" color=\"surface\" label=\"HTML or JSON output\" />\n <Badge variant=\"soft\" color=\"surface\" label=\"Config-driven toolbar\" />\n </div>\n </div>\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Minimal usage — default toolbar, HTML output, bindable\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">value</code>.\n </p>\n <Editor bind:value={basicHtml} placeholder=\"Write something…\" />\n <details class=\"text-sm\">\n <summary class=\"cursor-pointer text-on-surface-variant hover:text-on-surface\"\n >Show raw HTML</summary\n >\n <pre\n class=\"mt-2 overflow-x-auto rounded bg-surface-container-highest p-3 text-xs\">{basicHtml}</pre>\n </details>\n </section>\n\n <!-- JSON output -->\n <section class=\"space-y-3\">\n <h2 id=\"JSON-output\" class=\"text-lg font-semibold\">\n<a href=\"#JSON-output\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n JSON output\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Set <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >output=\"json\"</code\n >\n to receive a structured Tiptap document — ideal for database storage and downstream transformations.\n </p>\n <Editor bind:value={jsonValue} output=\"json\" placeholder=\"JSON-backed editor…\" />\n <details class=\"text-sm\">\n <summary class=\"cursor-pointer text-on-surface-variant hover:text-on-surface\"\n >Show serialized JSON</summary\n >\n <pre\n class=\"mt-2 max-h-80 overflow-auto rounded bg-surface-container-highest p-3 text-xs\">{serializedJson}</pre>\n </details>\n </section>\n\n <!-- Custom toolbar -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-toolbar-config\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-toolbar-config\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom toolbar config\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Pass an array of action ids + <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">'|'</code\n > separators to control exactly which buttons appear.\n </p>\n <Editor bind:value={customToolbarValue} toolbar={['bold', 'italic', '|', 'undo', 'redo']} />\n </section>\n\n <!-- Bubble menu only -->\n <section class=\"space-y-3\">\n <h2 id=\"Bubble-menu-no-top-toolbar\" class=\"text-lg font-semibold\">\n<a href=\"#Bubble-menu-no-top-toolbar\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Bubble menu (no top toolbar)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >toolbar={'{false}'}</code\n >\n +\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >bubbleMenu</code\n >\n shows a floating menu only when text is selected — minimal chrome.\n </p>\n <Editor bind:value={bubbleValue} toolbar={false} bubbleMenu />\n </section>\n\n <!-- Character count + maxLength -->\n <section class=\"space-y-3\">\n <h2 id=\"Character-limit\" class=\"text-lg font-semibold\">\n<a href=\"#Character-limit\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Character limit\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">maxLength</code\n >\n blocks input beyond the limit and shows a counter in the footer.\n </p>\n <Editor bind:value={limitedValue} maxLength={140} placeholder=\"Tweet-length post…\" />\n </section>\n\n <!-- Read-only / disabled -->\n <section class=\"space-y-3\">\n <h2 id=\"Read-only-amp-disabled\" class=\"text-lg font-semibold\">\n<a href=\"#Read-only-amp-disabled\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Read-only &amp; disabled\n </a>\n</h2>\n <div class=\"grid gap-4 lg:grid-cols-2\">\n <div>\n <p class=\"mb-2 text-sm font-medium\">readonly</p>\n <Editor\n value=\"<h3>Read-only</h3><p>Content renders but typing is blocked.</p>\"\n readonly\n />\n </div>\n <div>\n <p class=\"mb-2 text-sm font-medium\">disabled</p>\n <Editor value=\"<h3>Disabled</h3><p>Faded + non-interactive.</p>\" disabled />\n </div>\n </div>\n </section>\n\n <!-- Sizes -->\n <section class=\"space-y-3\">\n <h2 id=\"Sizes\" class=\"text-lg font-semibold\">\n<a href=\"#Sizes\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Sizes\n </a>\n</h2>\n <div class=\"space-y-4\">\n {#each ['sm', 'md', 'lg'] as const as size (size)}\n <div>\n <p class=\"mb-1 text-xs font-medium tracking-wide uppercase\">{size}</p>\n <Editor\n {size}\n value={`<p>Size: <strong>${size}</strong></p>`}\n toolbar={['bold', 'italic', '|', 'h1', 'h2']}\n />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Colors (focus ring) -->\n <section class=\"space-y-3\">\n <h2 id=\"Focus-ring-colors\" class=\"text-lg font-semibold\">\n<a href=\"#Focus-ring-colors\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Focus ring colors\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Click into each editor to see the focus ring.</p>\n <div class=\"grid gap-3 md:grid-cols-2\">\n {#each colors as color (color)}\n <div>\n <p class=\"mb-1 text-xs font-medium capitalize\">{color}</p>\n <Editor\n {color}\n value={`<p>Focus me — <strong>${color}</strong> ring.</p>`}\n toolbar={['bold', 'italic', '|', 'undo', 'redo']}\n />\n </div>\n {/each}\n </div>\n </section>\n\n <Separator />\n\n <!-- Imperative API -->\n <section class=\"space-y-3\">\n <h2 id=\"Imperative-API--bindapi\" class=\"text-lg font-semibold\">\n<a href=\"#Imperative-API--bindapi\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Imperative API — bind:api\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Drive the editor from outside via <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">bind:api</code\n >. Useful for custom toolbars, form submission, or external controls.\n </p>\n <Editor bind:api bind:value={apiValue} placeholder=\"Drive me from outside…\" />\n <div class=\"flex flex-wrap items-center gap-2\">\n <Button\n size=\"xs\"\n variant=\"outline\"\n leadingIcon=\"lucide:bold\"\n label=\"Bold\"\n onclick={() => api?.run('bold')}\n />\n <Button\n size=\"xs\"\n variant=\"outline\"\n leadingIcon=\"lucide:italic\"\n label=\"Italic\"\n onclick={() => api?.run('italic')}\n />\n <Button\n size=\"xs\"\n variant=\"outline\"\n leadingIcon=\"lucide:heading-1\"\n label=\"H1\"\n onclick={() => api?.run('h1')}\n />\n <Button\n size=\"xs\"\n variant=\"outline\"\n leadingIcon=\"lucide:link\"\n label=\"Link\"\n onclick={() => api?.run('link')}\n />\n <Button\n size=\"xs\"\n variant=\"outline\"\n leadingIcon=\"lucide:undo-2\"\n label=\"Undo\"\n disabled={!api?.state.can.undo}\n onclick={() => api?.run('undo')}\n />\n <Button\n size=\"xs\"\n variant=\"outline\"\n leadingIcon=\"lucide:redo-2\"\n label=\"Redo\"\n disabled={!api?.state.can.redo}\n onclick={() => api?.run('redo')}\n />\n <Button\n size=\"xs\"\n color=\"error\"\n variant=\"outline\"\n leadingIcon=\"lucide:trash-2\"\n label=\"Clear\"\n onclick={() => api?.clear()}\n />\n <Button\n size=\"xs\"\n color=\"primary\"\n variant=\"solid\"\n leadingIcon=\"lucide:plus\"\n label=\"Insert sample\"\n onclick={() => api?.insert(' — <em>inserted</em>')}\n />\n </div>\n <div class=\"flex flex-wrap items-center gap-2 pt-2 text-xs text-on-surface-variant\">\n <span>State:</span>\n <Badge size=\"xs\" variant=\"soft\" color={api?.state.active.bold ? 'primary' : 'surface'}\n >bold {api?.state.active.bold ?? false}</Badge\n >\n <Badge size=\"xs\" variant=\"soft\" color={api?.state.active.italic ? 'primary' : 'surface'}\n >italic {api?.state.active.italic ?? false}</Badge\n >\n <Badge size=\"xs\" variant=\"soft\" color={api?.state.isFocused ? 'success' : 'surface'}\n >focused {api?.state.isFocused ?? false}</Badge\n >\n <Badge size=\"xs\" variant=\"soft\" color={api?.state.isEmpty ? 'warning' : 'surface'}\n >empty {api?.state.isEmpty ?? false}</Badge\n >\n <Badge size=\"xs\" variant=\"soft\" color=\"info\">chars: {api?.state.charCount ?? 0}</Badge>\n <Badge size=\"xs\" variant=\"soft\" color=\"info\">words: {api?.state.wordCount ?? 0}</Badge>\n </div>\n </section>\n\n <Separator />\n\n <!-- Phase 2: Markdown output -->\n <section class=\"space-y-3\">\n <h2 id=\"Markdown-output-Phase-2\" class=\"text-lg font-semibold\">\n<a href=\"#Markdown-output-Phase-2\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Markdown output (Phase 2)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Set <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >output=\"markdown\"</code\n >\n to bind a Markdown string. Powered by the\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >tiptap-markdown</code\n > extension — paste markdown is also recognized.\n </p>\n <Editor\n bind:value={markdownValue}\n output=\"markdown\"\n placeholder=\"Type or paste markdown…\"\n />\n <details class=\"text-sm\">\n <summary class=\"cursor-pointer text-on-surface-variant hover:text-on-surface\"\n >Show raw markdown</summary\n >\n <pre\n class=\"mt-2 overflow-x-auto rounded bg-surface-container-highest p-3 text-xs\">{markdownValue}</pre>\n </details>\n </section>\n\n <!-- Phase 2: Image upload -->\n <section class=\"space-y-3\">\n <h2 id=\"Image-upload-Phase-2\" class=\"text-lg font-semibold\">\n<a href=\"#Image-upload-Phase-2\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Image upload (Phase 2)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">image</code>\n toolbar action opens a file picker → calls\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >onImageUpload(file)</code\n >\n → inserts the returned URL. Demo here converts to a data URL client-side.\n </p>\n <Editor\n bind:value={imageValue}\n image\n onImageUpload={fakeUploadImage}\n toolbar={[\n 'bold',\n 'italic',\n '|',\n 'h1',\n 'h2',\n '|',\n 'image',\n '|',\n 'bulletList',\n 'orderedList',\n '|',\n 'undo',\n 'redo'\n ]}\n />\n </section>\n\n <!-- Phase 2: Tables -->\n <section class=\"space-y-3\">\n <h2 id=\"Tables-Phase-2\" class=\"text-lg font-semibold\">\n<a href=\"#Tables-Phase-2\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Tables (Phase 2)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Enable with <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >tables</code\n >\n and add the\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">table</code> toolbar\n action. Click → hover the grid → pick dimensions → click to insert.\n </p>\n <Editor\n bind:value={tableValue}\n tables\n toolbar={['bold', 'italic', '|', 'h2', 'h3', '|', 'table', '|', 'undo', 'redo']}\n />\n </section>\n\n <!-- Phase 2: Mentions -->\n <section class=\"space-y-3\">\n <h2 id=\"Mentions-Phase-2\" class=\"text-lg font-semibold\">\n<a href=\"#Mentions-Phase-2\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Mentions (Phase 2)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Provide <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >onMention(query)</code\n >\n to enable @-style suggestions. Type\n <kbd\n class=\"rounded border border-outline-variant bg-surface-container-high px-1.5 py-0.5 text-xs\"\n >@</kbd\n >\n inside the editor — a popup will open with matching items. Arrow keys to navigate, Enter to\n select.\n </p>\n <Editor bind:value={mentionValue} onMention={searchMembers} placeholder=\"Try typing @al…\" />\n </section>\n\n <Separator />\n\n <!-- Phase 3: Slash commands -->\n <section class=\"space-y-3\">\n <h2 id=\"Slash-commands-Phase-3\" class=\"text-lg font-semibold\">\n<a href=\"#Slash-commands-Phase-3\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Slash commands (Phase 3)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Set <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">slash</code\n >\n to enable. Inside the editor, type\n <kbd\n class=\"rounded border border-outline-variant bg-surface-container-high px-1.5 py-0.5 text-xs\"\n >/</kbd\n >\n to open the command menu — fuzzy filter by typing, Arrow keys to navigate, Enter to run. Pass\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >slashCommands</code\n > to customize.\n </p>\n <Editor bind:value={slashValue} slash image tables youtube />\n </section>\n\n <!-- Phase 3: YouTube embed -->\n <section class=\"space-y-3\">\n <h2 id=\"YouTube-embeds-Phase-3\" class=\"text-lg font-semibold\">\n<a href=\"#YouTube-embeds-Phase-3\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n YouTube embeds (Phase 3)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">youtube</code>\n enables the\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">youtube</code> toolbar\n action which prompts for a URL and inserts a responsive embed.\n </p>\n <Editor\n bind:value={youtubeValue}\n youtube\n toolbar={['bold', 'italic', '|', 'h2', 'h3', '|', 'youtube', '|', 'undo', 'redo']}\n />\n </section>\n\n <!-- Phase 3: Drag handle -->\n <section class=\"space-y-3\">\n <h2 id=\"Drag-handle-Phase-3\" class=\"text-lg font-semibold\">\n<a href=\"#Drag-handle-Phase-3\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Drag handle (Phase 3)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >dragHandle</code\n >\n shows a draggable handle on the left of each block on hover. Drag to reorder paragraphs, headings,\n lists, etc.\n </p>\n <Editor bind:value={dragHandleValue} dragHandle />\n </section>\n\n <Separator />\n\n <!-- Phase 2: Form integration -->\n <section class=\"space-y-3\">\n <h2 id=\"Form-integration-Phase-2\" class=\"text-lg font-semibold\">\n<a href=\"#Form-integration-Phase-2\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Form integration (Phase 2)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Wrap in <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >&lt;FormField&gt;</code\n > to get error highlighting, label association, and per-field validation events.\n </p>\n <Card class=\"space-y-3 p-4\">\n <Form state={articleState} validate={validateArticle} onsubmit={submitArticle}>\n <div class=\"space-y-4\">\n <FormField name=\"title\" label=\"Title\" required>\n <Input bind:value={articleState.title} placeholder=\"Article title…\" />\n </FormField>\n <FormField name=\"body\" label=\"Body\" required>\n <Editor\n bind:value={articleState.body}\n placeholder=\"Write your article…\"\n toolbar={[\n 'bold',\n 'italic',\n '|',\n 'h2',\n 'h3',\n '|',\n 'bulletList',\n 'orderedList',\n 'blockquote',\n '|',\n 'link',\n '|',\n 'undo',\n 'redo'\n ]}\n />\n </FormField>\n <div class=\"flex justify-end\">\n <Button type=\"submit\" color=\"primary\" size=\"sm\" label=\"Submit article\" />\n </div>\n </div>\n </Form>\n {#if articleSubmitted}\n <pre\n class=\"mt-2 max-h-60 overflow-auto rounded bg-surface-container-highest p-3 text-xs\">{articleSubmitted}</pre>\n {/if}\n </Card>\n </section>\n\n <Separator />\n\n <!-- Real-world: comment box -->\n <section class=\"space-y-3\">\n <h2 id=\"Real-world--Comment-box\" class=\"text-lg font-semibold\">\n<a href=\"#Real-world--Comment-box\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real-world — Comment box\n </a>\n</h2>\n <Card class=\"space-y-3 p-4\">\n <Editor\n bind:value={limitedValue}\n placeholder=\"Add a thoughtful comment…\"\n maxLength={280}\n toolbar={['bold', 'italic', 'code', '|', 'bulletList', 'orderedList', '|', 'link']}\n bubbleMenu\n stickyToolbar\n />\n <div class=\"flex items-center justify-end gap-2\">\n <Button variant=\"ghost\" size=\"sm\" label=\"Cancel\" />\n <Button color=\"primary\" size=\"sm\" leadingIcon=\"lucide:send\" label=\"Post comment\" />\n </div>\n </Card>\n </section>\n\n <!-- Note about sub-export -->\n <section class=\"space-y-3\">\n <h2 id=\"Import-path\" class=\"text-lg font-semibold\">\n<a href=\"#Import-path\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Import path\n </a>\n</h2>\n <div class=\"rounded-lg border border-outline-variant bg-surface-container p-4 text-sm\">\n <p class=\"mb-2 flex items-center gap-2 font-medium\">\n <Icon name=\"lucide:package\" size=\"18\" class=\"text-primary\" />\n Editor is opt-in via a separate entry point\n </p>\n <pre class=\"mt-2 overflow-x-auto rounded bg-surface-container-highest p-3 text-xs\"><code\n >{importExample}</code\n ></pre>\n <p class=\"mt-3 text-on-surface-variant\">\n When your app doesn't reach this import path, Tiptap (~120 KB gzipped) is\n tree-shaken out of the production bundle.\n </p>\n </div>\n </section>\n</div>\n",
145
+ "editor": "<script lang=\"ts\">\n import { Editor } from '$lib/components/Editor/index.js'\n import type { EditorApi, EditorJSON, MentionItem } from '$lib/components/Editor/index.js'\n import { Button, Badge, Separator, Card, Icon, Form, FormField, Input } from '$lib/index.js'\n\n let basicHtml = $state('<p>Start writing here…</p>')\n\n let jsonValue = $state<EditorJSON>({\n type: 'doc',\n content: [\n {\n type: 'paragraph',\n content: [{ type: 'text', text: 'This document is stored as JSON.' }]\n }\n ]\n })\n\n let customToolbarValue = $state('<p>Only <strong>bold</strong> and <em>italic</em>.</p>')\n\n let api = $state<EditorApi>()\n let apiValue = $state('<p>Drive me from outside.</p>')\n\n let limitedValue = $state('<p>Type here…</p>')\n\n let bubbleValue = $state('<p>Select any text to see the floating menu.</p>')\n\n // ----- Phase 2 demos -----\n\n let markdownValue = $state('# Hello\\n\\nThis editor outputs **Markdown**.')\n\n let imageValue = $state('<p>Click the image button to upload.</p>')\n async function fakeUploadImage(file: File): Promise<string> {\n // Demo: convert to data URL. Real apps would upload to a backend.\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.onload = () => resolve(String(reader.result))\n reader.onerror = () => reject(new Error('FileReader failed'))\n reader.readAsDataURL(file)\n })\n }\n\n let tableValue = $state('<p>Click the table button → pick dimensions → insert.</p>')\n\n let slashValue = $state('<p>Type / to open the command menu…</p>')\n let youtubeValue = $state(\n '<p>Click the YouTube button or use / → YouTube to embed a video.</p>'\n )\n let dragHandleValue = $state(\n '<h2 id=\"Drag-me\">Drag me!</h2><p>Hover any block to see the drag handle appear on the left. Drag to reorder.</p><ul><li>First item</li><li>Second item</li><li>Third item</li></ul>'\n )\n\n let mentionValue = $state('<p>Type @ to mention someone…</p>')\n const teamMembers: MentionItem[] = [\n { id: 'alice', label: 'Alice Nguyen' },\n { id: 'bob', label: 'Bob Tran' },\n { id: 'charlie', label: 'Charlie Le' },\n { id: 'diana', label: 'Diana Pham' },\n { id: 'evan', label: 'Evan Vo' }\n ]\n async function searchMembers(query: string): Promise<MentionItem[]> {\n await new Promise((r) => setTimeout(r, 50))\n const q = query.toLowerCase()\n return teamMembers.filter((m) => m.label.toLowerCase().includes(q))\n }\n\n const articleState = $state({ title: '', body: '<p></p>' })\n let articleSubmitted = $state<string | null>(null)\n function validateArticle(values: object) {\n const v = values as { title: string; body: string }\n const errors: { name: string; message: string }[] = []\n if (!v.title.trim()) errors.push({ name: 'title', message: 'Title is required' })\n if (v.body === '<p></p>' || !v.body.trim())\n errors.push({ name: 'body', message: 'Body cannot be empty' })\n return errors\n }\n function submitArticle(e: { data: unknown }) {\n articleSubmitted = JSON.stringify(e.data, null, 2)\n }\n\n const colors = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'success',\n 'warning',\n 'error',\n 'info',\n 'surface'\n ] as const\n\n let serializedJson = $derived(JSON.stringify(jsonValue, null, 2))\n\n const importExample = \"import { Editor } from 'svelora/editor'\"\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">Editor</h1>\n <p class=\"text-on-surface-variant\">\n Rich-text WYSIWYG editor built on <strong>Tiptap v3</strong> + ProseMirror. Imported via\n the sub-export\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >svelora/editor</code\n >\n — only adds Tiptap to your bundle when actually used.\n </p>\n <div class=\"flex flex-wrap gap-2 pt-1\">\n <Badge variant=\"soft\" color=\"info\" label=\"Phase 1 (v1.8)\" />\n <Badge variant=\"soft\" color=\"surface\" label=\"HTML or JSON output\" />\n <Badge variant=\"soft\" color=\"surface\" label=\"Config-driven toolbar\" />\n </div>\n </div>\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Minimal usage — default toolbar, HTML output, bindable\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">value</code>.\n </p>\n <Editor bind:value={basicHtml} placeholder=\"Write something…\" />\n <details class=\"text-sm\">\n <summary class=\"cursor-pointer text-on-surface-variant hover:text-on-surface\"\n >Show raw HTML</summary\n >\n <pre\n class=\"mt-2 overflow-x-auto rounded bg-surface-container-highest p-3 text-xs\">{basicHtml}</pre>\n </details>\n </section>\n\n <!-- JSON output -->\n <section class=\"space-y-3\">\n <h2 id=\"JSON-output\" class=\"text-lg font-semibold\">\n<a href=\"#JSON-output\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n JSON output\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Set <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >output=\"json\"</code\n >\n to receive a structured Tiptap document — ideal for database storage and downstream transformations.\n </p>\n <Editor bind:value={jsonValue} output=\"json\" placeholder=\"JSON-backed editor…\" />\n <details class=\"text-sm\">\n <summary class=\"cursor-pointer text-on-surface-variant hover:text-on-surface\"\n >Show serialized JSON</summary\n >\n <pre\n class=\"mt-2 max-h-80 overflow-auto rounded bg-surface-container-highest p-3 text-xs\">{serializedJson}</pre>\n </details>\n </section>\n\n <!-- Custom toolbar -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-toolbar-config\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-toolbar-config\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom toolbar config\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Pass an array of action ids + <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">'|'</code\n > separators to control exactly which buttons appear.\n </p>\n <Editor bind:value={customToolbarValue} toolbar={['bold', 'italic', '|', 'undo', 'redo']} />\n </section>\n\n <!-- Bubble menu only -->\n <section class=\"space-y-3\">\n <h2 id=\"Bubble-menu-no-top-toolbar\" class=\"text-lg font-semibold\">\n<a href=\"#Bubble-menu-no-top-toolbar\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Bubble menu (no top toolbar)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >toolbar={'{false}'}</code\n >\n +\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >bubbleMenu</code\n >\n shows a floating menu only when text is selected — minimal chrome.\n </p>\n <Editor bind:value={bubbleValue} toolbar={false} bubbleMenu />\n </section>\n\n <!-- Character count + maxLength -->\n <section class=\"space-y-3\">\n <h2 id=\"Character-limit\" class=\"text-lg font-semibold\">\n<a href=\"#Character-limit\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Character limit\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">maxLength</code\n >\n blocks input beyond the limit and shows a counter in the footer.\n </p>\n <Editor bind:value={limitedValue} maxLength={140} placeholder=\"Tweet-length post…\" />\n </section>\n\n <!-- Read-only / disabled -->\n <section class=\"space-y-3\">\n <h2 id=\"Read-only-amp-disabled\" class=\"text-lg font-semibold\">\n<a href=\"#Read-only-amp-disabled\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Read-only &amp; disabled\n </a>\n</h2>\n <div class=\"grid gap-4 lg:grid-cols-2\">\n <div>\n <p class=\"mb-2 text-sm font-medium\">readonly</p>\n <Editor\n value=\"<h3>Read-only</h3><p>Content renders but typing is blocked.</p>\"\n readonly\n />\n </div>\n <div>\n <p class=\"mb-2 text-sm font-medium\">disabled</p>\n <Editor value=\"<h3>Disabled</h3><p>Faded + non-interactive.</p>\" disabled />\n </div>\n </div>\n </section>\n\n <!-- Sizes -->\n <section class=\"space-y-3\">\n <h2 id=\"Sizes\" class=\"text-lg font-semibold\">\n<a href=\"#Sizes\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Sizes\n </a>\n</h2>\n <div class=\"space-y-4\">\n {#each ['sm', 'md', 'lg'] as const as size (size)}\n <div>\n <p class=\"mb-1 text-xs font-medium tracking-wide uppercase\">{size}</p>\n <Editor\n {size}\n value={`<p>Size: <strong>${size}</strong></p>`}\n toolbar={['bold', 'italic', '|', 'h1', 'h2']}\n />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Colors (focus ring) -->\n <section class=\"space-y-3\">\n <h2 id=\"Focus-ring-colors\" class=\"text-lg font-semibold\">\n<a href=\"#Focus-ring-colors\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Focus ring colors\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Click into each editor to see the focus ring.</p>\n <div class=\"grid gap-3 md:grid-cols-2\">\n {#each colors as color (color)}\n <div>\n <p class=\"mb-1 text-xs font-medium capitalize\">{color}</p>\n <Editor\n {color}\n value={`<p>Focus me — <strong>${color}</strong> ring.</p>`}\n toolbar={['bold', 'italic', '|', 'undo', 'redo']}\n />\n </div>\n {/each}\n </div>\n </section>\n\n <Separator />\n\n <!-- Imperative API -->\n <section class=\"space-y-3\">\n <h2 id=\"Imperative-API--bindapi\" class=\"text-lg font-semibold\">\n<a href=\"#Imperative-API--bindapi\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Imperative API — bind:api\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Drive the editor from outside via <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">bind:api</code\n >. Useful for custom toolbars, form submission, or external controls.\n </p>\n <Editor bind:api bind:value={apiValue} placeholder=\"Drive me from outside…\" />\n <div class=\"flex flex-wrap items-center gap-2\">\n <Button\n size=\"xs\"\n variant=\"outline\"\n leadingIcon=\"lucide:bold\"\n label=\"Bold\"\n onclick={() => api?.run('bold')}\n />\n <Button\n size=\"xs\"\n variant=\"outline\"\n leadingIcon=\"lucide:italic\"\n label=\"Italic\"\n onclick={() => api?.run('italic')}\n />\n <Button\n size=\"xs\"\n variant=\"outline\"\n leadingIcon=\"lucide:heading-1\"\n label=\"H1\"\n onclick={() => api?.run('h1')}\n />\n <Button\n size=\"xs\"\n variant=\"outline\"\n leadingIcon=\"lucide:link\"\n label=\"Link\"\n onclick={() => api?.run('link')}\n />\n <Button\n size=\"xs\"\n variant=\"outline\"\n leadingIcon=\"lucide:undo-2\"\n label=\"Undo\"\n disabled={!api?.state.can.undo}\n onclick={() => api?.run('undo')}\n />\n <Button\n size=\"xs\"\n variant=\"outline\"\n leadingIcon=\"lucide:redo-2\"\n label=\"Redo\"\n disabled={!api?.state.can.redo}\n onclick={() => api?.run('redo')}\n />\n <Button\n size=\"xs\"\n color=\"error\"\n variant=\"outline\"\n leadingIcon=\"lucide:trash-2\"\n label=\"Clear\"\n onclick={() => api?.clear()}\n />\n <Button\n size=\"xs\"\n color=\"primary\"\n variant=\"solid\"\n leadingIcon=\"lucide:plus\"\n label=\"Insert sample\"\n onclick={() => api?.insert(' — <em>inserted</em>')}\n />\n </div>\n <div class=\"flex flex-wrap items-center gap-2 pt-2 text-xs text-on-surface-variant\">\n <span>State:</span>\n <Badge size=\"xs\" variant=\"soft\" color={api?.state.active.bold ? 'primary' : 'surface'}\n >bold {api?.state.active.bold ?? false}</Badge\n >\n <Badge size=\"xs\" variant=\"soft\" color={api?.state.active.italic ? 'primary' : 'surface'}\n >italic {api?.state.active.italic ?? false}</Badge\n >\n <Badge size=\"xs\" variant=\"soft\" color={api?.state.isFocused ? 'success' : 'surface'}\n >focused {api?.state.isFocused ?? false}</Badge\n >\n <Badge size=\"xs\" variant=\"soft\" color={api?.state.isEmpty ? 'warning' : 'surface'}\n >empty {api?.state.isEmpty ?? false}</Badge\n >\n <Badge size=\"xs\" variant=\"soft\" color=\"info\">chars: {api?.state.charCount ?? 0}</Badge>\n <Badge size=\"xs\" variant=\"soft\" color=\"info\">words: {api?.state.wordCount ?? 0}</Badge>\n </div>\n </section>\n\n <Separator />\n\n <!-- Phase 2: Markdown output -->\n <section class=\"space-y-3\">\n <h2 id=\"Markdown-output-Phase-2\" class=\"text-lg font-semibold\">\n<a href=\"#Markdown-output-Phase-2\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Markdown output (Phase 2)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Set <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >output=\"markdown\"</code\n >\n to bind a Markdown string. Powered by the\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >tiptap-markdown</code\n > extension — paste markdown is also recognized.\n </p>\n <Editor\n bind:value={markdownValue}\n output=\"markdown\"\n placeholder=\"Type or paste markdown…\"\n />\n <details class=\"text-sm\">\n <summary class=\"cursor-pointer text-on-surface-variant hover:text-on-surface\"\n >Show raw markdown</summary\n >\n <pre\n class=\"mt-2 overflow-x-auto rounded bg-surface-container-highest p-3 text-xs\">{markdownValue}</pre>\n </details>\n </section>\n\n <!-- Phase 2: Image upload -->\n <section class=\"space-y-3\">\n <h2 id=\"Image-upload-Phase-2\" class=\"text-lg font-semibold\">\n<a href=\"#Image-upload-Phase-2\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Image upload (Phase 2)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">image</code>\n toolbar action opens a file picker → calls\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >onImageUpload(file)</code\n >\n → inserts the returned URL. Demo here converts to a data URL client-side.\n </p>\n <Editor\n bind:value={imageValue}\n image\n onImageUpload={fakeUploadImage}\n toolbar={[\n 'bold',\n 'italic',\n '|',\n 'h1',\n 'h2',\n '|',\n 'image',\n '|',\n 'bulletList',\n 'orderedList',\n '|',\n 'undo',\n 'redo'\n ]}\n />\n </section>\n\n <!-- Phase 2: Tables -->\n <section class=\"space-y-3\">\n <h2 id=\"Tables-Phase-2\" class=\"text-lg font-semibold\">\n<a href=\"#Tables-Phase-2\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Tables (Phase 2)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Enable with <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >tables</code\n >\n and add the\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">table</code> toolbar\n action. Click → hover the grid → pick dimensions → click to insert.\n </p>\n <Editor\n bind:value={tableValue}\n tables\n toolbar={['bold', 'italic', '|', 'h2', 'h3', '|', 'table', '|', 'undo', 'redo']}\n />\n </section>\n\n <!-- Phase 2: Mentions -->\n <section class=\"space-y-3\">\n <h2 id=\"Mentions-Phase-2\" class=\"text-lg font-semibold\">\n<a href=\"#Mentions-Phase-2\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Mentions (Phase 2)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Provide <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >onMention(query)</code\n >\n to enable @-style suggestions. Type\n <kbd\n class=\"rounded border border-outline-variant bg-surface-container-high px-1.5 py-0.5 text-xs\"\n >@</kbd\n >\n inside the editor — a popup will open with matching items. Arrow keys to navigate, Enter to\n select.\n </p>\n <Editor bind:value={mentionValue} onMention={searchMembers} placeholder=\"Try typing @al…\" />\n </section>\n\n <Separator />\n\n <!-- Phase 3: Slash commands -->\n <section class=\"space-y-3\">\n <h2 id=\"Slash-commands-Phase-3\" class=\"text-lg font-semibold\">\n<a href=\"#Slash-commands-Phase-3\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Slash commands (Phase 3)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Set <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">slash</code\n >\n to enable. Inside the editor, type\n <kbd\n class=\"rounded border border-outline-variant bg-surface-container-high px-1.5 py-0.5 text-xs\"\n >/</kbd\n >\n to open the command menu — fuzzy filter by typing, Arrow keys to navigate, Enter to run. Pass\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >slashCommands</code\n > to customize.\n </p>\n <Editor bind:value={slashValue} slash image tables youtube />\n </section>\n\n <!-- Phase 3: YouTube embed -->\n <section class=\"space-y-3\">\n <h2 id=\"YouTube-embeds-Phase-3\" class=\"text-lg font-semibold\">\n<a href=\"#YouTube-embeds-Phase-3\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n YouTube embeds (Phase 3)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">youtube</code>\n enables the\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">youtube</code> toolbar\n action which prompts for a URL and inserts a responsive embed.\n </p>\n <Editor\n bind:value={youtubeValue}\n youtube\n toolbar={['bold', 'italic', '|', 'h2', 'h3', '|', 'youtube', '|', 'undo', 'redo']}\n />\n </section>\n\n <!-- Phase 3: Drag handle -->\n <section class=\"space-y-3\">\n <h2 id=\"Drag-handle-Phase-3\" class=\"text-lg font-semibold\">\n<a href=\"#Drag-handle-Phase-3\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Drag handle (Phase 3)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >dragHandle</code\n >\n shows a draggable handle on the left of each block on hover. Drag to reorder paragraphs, headings,\n lists, etc. This is built into Editor (TipTap) — for app-level list reordering use\n <a href=\"/docs/hooks/use-sortable\" class=\"text-primary underline\">useSortable</a>\n instead.\n </p>\n <Editor bind:value={dragHandleValue} dragHandle />\n </section>\n\n <Separator />\n\n <!-- Phase 2: Form integration -->\n <section class=\"space-y-3\">\n <h2 id=\"Form-integration-Phase-2\" class=\"text-lg font-semibold\">\n<a href=\"#Form-integration-Phase-2\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Form integration (Phase 2)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Wrap in <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >&lt;FormField&gt;</code\n > to get error highlighting, label association, and per-field validation events.\n </p>\n <Card class=\"space-y-3 p-4\">\n <Form state={articleState} validate={validateArticle} onsubmit={submitArticle}>\n <div class=\"space-y-4\">\n <FormField name=\"title\" label=\"Title\" required>\n <Input bind:value={articleState.title} placeholder=\"Article title…\" />\n </FormField>\n <FormField name=\"body\" label=\"Body\" required>\n <Editor\n bind:value={articleState.body}\n placeholder=\"Write your article…\"\n toolbar={[\n 'bold',\n 'italic',\n '|',\n 'h2',\n 'h3',\n '|',\n 'bulletList',\n 'orderedList',\n 'blockquote',\n '|',\n 'link',\n '|',\n 'undo',\n 'redo'\n ]}\n />\n </FormField>\n <div class=\"flex justify-end\">\n <Button type=\"submit\" color=\"primary\" size=\"sm\" label=\"Submit article\" />\n </div>\n </div>\n </Form>\n {#if articleSubmitted}\n <pre\n class=\"mt-2 max-h-60 overflow-auto rounded bg-surface-container-highest p-3 text-xs\">{articleSubmitted}</pre>\n {/if}\n </Card>\n </section>\n\n <Separator />\n\n <!-- Real-world: comment box -->\n <section class=\"space-y-3\">\n <h2 id=\"Real-world--Comment-box\" class=\"text-lg font-semibold\">\n<a href=\"#Real-world--Comment-box\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real-world — Comment box\n </a>\n</h2>\n <Card class=\"space-y-3 p-4\">\n <Editor\n bind:value={limitedValue}\n placeholder=\"Add a thoughtful comment…\"\n maxLength={280}\n toolbar={['bold', 'italic', 'code', '|', 'bulletList', 'orderedList', '|', 'link']}\n bubbleMenu\n stickyToolbar\n />\n <div class=\"flex items-center justify-end gap-2\">\n <Button variant=\"ghost\" size=\"sm\" label=\"Cancel\" />\n <Button color=\"primary\" size=\"sm\" leadingIcon=\"lucide:send\" label=\"Post comment\" />\n </div>\n </Card>\n </section>\n\n <!-- Note about sub-export -->\n <section class=\"space-y-3\">\n <h2 id=\"Import-path\" class=\"text-lg font-semibold\">\n<a href=\"#Import-path\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Import path\n </a>\n</h2>\n <div class=\"rounded-lg border border-outline-variant bg-surface-container p-4 text-sm\">\n <p class=\"mb-2 flex items-center gap-2 font-medium\">\n <Icon name=\"lucide:package\" size=\"18\" class=\"text-primary\" />\n Editor is opt-in via a separate entry point\n </p>\n <pre class=\"mt-2 overflow-x-auto rounded bg-surface-container-highest p-3 text-xs\"><code\n >{importExample}</code\n ></pre>\n <p class=\"mt-3 text-on-surface-variant\">\n When your app doesn't reach this import path, Tiptap (~120 KB gzipped) is\n tree-shaken out of the production bundle.\n </p>\n </div>\n </section>\n</div>\n",
142
146
  "input": "<script lang=\"ts\">\n import { Input, FormField, FieldGroup, Separator } from '$lib/index.js'\n\n const variants = ['outline', 'soft', 'subtle', 'ghost', 'none'] as const\n const colors = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'success',\n 'warning',\n 'error',\n 'info',\n 'surface'\n ] as const\n const sizes = ['xs', 'sm', 'md', 'lg', 'xl'] as const\n\n let bindValue = $state('')\n</script>\n\n<div class=\"space-y-8\">\n <h1 class=\"text-2xl font-bold text-on-surface\">Input</h1>\n\n <!-- Basic Usage -->\n <section class=\"space-y-4\">\n <h2 id=\"Basic-Usage\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Basic-Usage\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic Usage\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n A form input component with variants, colors, icons, and integration with FormField and\n FieldGroup.\n </p>\n <div class=\"max-w-sm\">\n <Input placeholder=\"Enter text...\" />\n </div>\n </section>\n\n <!-- Two-way Binding -->\n <section class=\"space-y-4\">\n <h2 id=\"Two-way-Binding\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Two-way-Binding\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Two-way Binding\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >bind:value</code\n > for reactive two-way data binding.\n </p>\n <div class=\"max-w-sm space-y-3\">\n <Input\n bind:value={bindValue}\n leadingIcon=\"lucide:pencil\"\n placeholder=\"Type something...\"\n />\n <p class=\"text-sm text-on-surface-variant\">\n Value: <span class=\"font-mono text-on-surface\">{bindValue || '(empty)'}</span>\n </p>\n <p class=\"text-sm text-on-surface-variant\">\n Length: <span class=\"font-mono text-on-surface\">{bindValue.length}</span>\n </p>\n </div>\n </section>\n\n <!-- Variants × Colors -->\n <section class=\"space-y-4\">\n <h2 id=\"Variants-times-Colors\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Variants-times-Colors\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Variants &times; Colors\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >variant</code\n >\n and\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">color</code> to control\n appearance.\n </p>\n <div class=\"overflow-x-auto\">\n <table class=\"w-full border-collapse\">\n <thead>\n <tr>\n <th class=\"px-3 py-2 text-left text-xs font-medium text-on-surface-variant\"\n ></th>\n {#each colors as color (color)}\n <th\n class=\"px-3 py-2 text-left text-xs font-medium text-on-surface-variant capitalize\"\n >{color}</th\n >\n {/each}\n </tr>\n </thead>\n <tbody>\n {#each variants as variant (variant)}\n <tr>\n <td\n class=\"px-3 py-2 text-xs font-medium text-on-surface-variant capitalize\"\n >{variant}</td\n >\n {#each colors as color (color)}\n <td class=\"px-3 py-2\">\n <Input {variant} {color} placeholder={color} />\n </td>\n {/each}\n </tr>\n {/each}\n </tbody>\n </table>\n </div>\n </section>\n\n <!-- Size -->\n <section class=\"space-y-4\">\n <h2 id=\"Size\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Size\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Size\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">size</code> to\n control the dimensions and text size.\n </p>\n <div class=\"flex flex-wrap items-end gap-4\">\n {#each sizes as size (size)}\n <div class=\"w-48\">\n <Input {size} placeholder=\"{size} size\" />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Icons -->\n <section class=\"space-y-4\">\n <h2 id=\"Icons\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Icons\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Icons\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >leadingIcon</code\n >\n and\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >trailingIcon</code\n > to add icons.\n </p>\n <div class=\"flex flex-wrap gap-4\">\n <div class=\"w-64\">\n <Input leadingIcon=\"lucide:search\" placeholder=\"Search...\" />\n </div>\n <div class=\"w-64\">\n <Input trailingIcon=\"lucide:eye\" placeholder=\"Password\" type=\"password\" />\n </div>\n <div class=\"w-64\">\n <Input\n leadingIcon=\"lucide:mail\"\n trailingIcon=\"lucide:check\"\n placeholder=\"Email\"\n type=\"email\"\n />\n </div>\n </div>\n </section>\n\n <!-- Icon (with trailing) -->\n <section class=\"space-y-4\">\n <h2 id=\"Icon-with-trailing\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Icon-with-trailing\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Icon (with trailing)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">icon</code>\n with\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">trailing</code> to\n position it.\n </p>\n <div class=\"flex flex-wrap gap-4\">\n <div class=\"w-64\">\n <Input icon=\"lucide:user\" placeholder=\"Leading icon\" />\n </div>\n <div class=\"w-64\">\n <Input icon=\"lucide:user\" trailing placeholder=\"Trailing icon\" />\n </div>\n </div>\n </section>\n\n <!-- Avatar -->\n <section class=\"space-y-4\">\n <h2 id=\"Avatar\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Avatar\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Avatar\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >avatar</code\n > to display an avatar before the input.\n </p>\n <div class=\"w-64\">\n <Input\n avatar={{ src: 'https://i.pravatar.cc/120?img=1', alt: 'User' }}\n placeholder=\"With avatar\"\n />\n </div>\n </section>\n\n <!-- Loading -->\n <section class=\"space-y-4\">\n <h2 id=\"Loading\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Loading\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Loading\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >loading</code\n > to show a loading spinner.\n </p>\n <div class=\"flex flex-wrap gap-4\">\n <div class=\"w-64\">\n <Input loading placeholder=\"Loading (leading)...\" />\n </div>\n <div class=\"w-64\">\n <Input loading trailing placeholder=\"Loading (trailing)...\" />\n </div>\n </div>\n </section>\n\n <!-- Disabled -->\n <section class=\"space-y-4\">\n <h2 id=\"Disabled\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Disabled\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Disabled\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >disabled</code\n > to prevent interaction.\n </p>\n <div class=\"flex flex-wrap gap-4\">\n <div class=\"w-64\">\n <Input disabled placeholder=\"Disabled\" />\n </div>\n <div class=\"w-64\">\n <Input disabled value=\"Disabled with value\" />\n </div>\n </div>\n </section>\n\n <!-- Highlight -->\n <section class=\"space-y-4\">\n <h2 id=\"Highlight\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Highlight\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Highlight\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >highlight</code\n > to emphasize the ring color like a focus state.\n </p>\n <div class=\"flex flex-wrap gap-4\">\n {#each colors as color (color)}\n <div class=\"w-48\">\n <Input highlight {color} placeholder={color} />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- FormField Integration -->\n <section class=\"space-y-4\">\n <h2 id=\"FormField-Integration\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#FormField-Integration\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n FormField Integration\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n When used inside a <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">FormField</code\n >, the Input automatically inherits size, error state, and accessibility attributes.\n </p>\n <div class=\"max-w-sm space-y-4\">\n <FormField\n label=\"Email\"\n description=\"We'll use this to send you notifications.\"\n required\n >\n <Input leadingIcon=\"lucide:mail\" placeholder=\"Enter your email\" type=\"email\" />\n </FormField>\n\n <FormField\n label=\"Password\"\n help=\"Must be at least 8 characters.\"\n error=\"Password is too short.\"\n >\n <Input\n trailingIcon=\"lucide:eye\"\n placeholder=\"Enter your password\"\n type=\"password\"\n />\n </FormField>\n </div>\n </section>\n\n <!-- FieldGroup Integration -->\n <section class=\"space-y-4\">\n <h2 id=\"FieldGroup-Integration\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#FieldGroup-Integration\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n FieldGroup Integration\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n When used inside a <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">FieldGroup</code\n >, inputs are visually connected.\n </p>\n <div class=\"space-y-6\">\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">horizontal (default)</p>\n <FieldGroup orientation=\"horizontal\">\n <Input placeholder=\"First name\" />\n <Input placeholder=\"Last name\" />\n <Input placeholder=\"Email\" />\n </FieldGroup>\n </div>\n\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">vertical</p>\n <div class=\"max-w-sm\">\n <FieldGroup orientation=\"vertical\">\n <Input placeholder=\"Street address\" />\n <Input placeholder=\"City\" />\n <Input placeholder=\"Zip code\" />\n </FieldGroup>\n </div>\n </div>\n </div>\n </section>\n\n <Separator />\n\n <!-- Real World Examples -->\n <section class=\"space-y-4\">\n <h2 id=\"Real-World-Examples\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Real-World-Examples\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World Examples\n </a>\n</h2>\n\n <div class=\"space-y-6\">\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Search bar</p>\n <div class=\"max-w-md\">\n <FieldGroup>\n <Input leadingIcon=\"lucide:search\" placeholder=\"Search products...\" />\n <Input placeholder=\"Location\" leadingIcon=\"lucide:map-pin\" />\n </FieldGroup>\n </div>\n </div>\n\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Login form</p>\n <div\n class=\"max-w-sm space-y-4 rounded-lg border border-outline-variant bg-surface-container-low p-4\"\n >\n <FormField label=\"Email\" required>\n <Input\n leadingIcon=\"lucide:mail\"\n placeholder=\"john@example.com\"\n type=\"email\"\n />\n </FormField>\n\n <FormField label=\"Password\" required help=\"Must be at least 8 characters.\">\n <Input\n leadingIcon=\"lucide:lock\"\n placeholder=\"Enter your password\"\n type=\"password\"\n />\n </FormField>\n </div>\n </div>\n\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Validation states</p>\n <div\n class=\"max-w-sm space-y-4 rounded-lg border border-outline-variant bg-surface-container-low p-4\"\n >\n <FormField label=\"Username\" error=\"Username is already taken.\">\n <Input value=\"admin\" color=\"error\" leadingIcon=\"lucide:user\" />\n </FormField>\n\n <FormField label=\"Email\" help=\"Looks good!\">\n <Input\n type=\"email\"\n value=\"valid@example.com\"\n color=\"success\"\n leadingIcon=\"lucide:mail\"\n />\n </FormField>\n </div>\n </div>\n </div>\n </section>\n</div>\n",
143
147
  "radio-group": "<script lang=\"ts\">\n import { RadioGroup, FormField, Separator } from '$lib/index.js'\n import type { RadioGroupItem } from '$lib/index.js'\n\n const colors = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'success',\n 'warning',\n 'error',\n 'info',\n 'surface'\n ] as const\n const sizes = ['xs', 'sm', 'md', 'lg', 'xl'] as const\n\n const fruits: RadioGroupItem[] = [\n { value: 'apple', label: 'Apple' },\n { value: 'banana', label: 'Banana' },\n { value: 'orange', label: 'Orange' }\n ]\n\n const plans: RadioGroupItem[] = [\n { value: 'free', label: 'Free', description: 'Basic features for personal use.' },\n { value: 'pro', label: 'Pro', description: 'Advanced features for professionals.' },\n { value: 'enterprise', label: 'Enterprise', description: 'Full suite for organizations.' }\n ]\n\n const partialDisabled: RadioGroupItem[] = [\n { value: 'available', label: 'Available' },\n { value: 'unavailable', label: 'Unavailable', disabled: true },\n { value: 'limited', label: 'Limited' }\n ]\n\n let bindValue = $state('')\n</script>\n\n<div class=\"space-y-8\">\n <h1 class=\"text-2xl font-bold text-on-surface\">RadioGroup</h1>\n\n <!-- Basic Usage -->\n <section class=\"space-y-4\">\n <h2 id=\"Basic-Usage\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Basic-Usage\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic Usage\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n A radio group component for single selection from a list of options.\n </p>\n <RadioGroup items={fruits} />\n </section>\n\n <!-- Two-way Binding -->\n <section class=\"space-y-4\">\n <h2 id=\"Two-way-Binding\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Two-way-Binding\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Two-way Binding\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >bind:value</code\n > for reactive two-way data binding.\n </p>\n <div class=\"flex flex-wrap items-start gap-8\">\n <RadioGroup items={fruits} bind:value={bindValue} />\n <p class=\"text-sm text-on-surface-variant\">\n Selected: <span class=\"font-mono text-on-surface\">{bindValue || '(none)'}</span>\n </p>\n </div>\n </section>\n\n <!-- Label & Description -->\n <section class=\"space-y-4\">\n <h2 id=\"Label-amp-Description\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Label-amp-Description\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Label &amp; Description\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Each item supports <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">label</code\n >\n and\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >description</code\n > properties.\n </p>\n <RadioGroup items={plans} />\n </section>\n\n <!-- Legend -->\n <section class=\"space-y-4\">\n <h2 id=\"Legend\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Legend\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Legend\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >legend</code\n >\n to add a title above the radio group. Use\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">required</code> to\n show an asterisk.\n </p>\n <div class=\"flex flex-wrap gap-8\">\n <RadioGroup items={fruits} legend=\"Select a fruit\" />\n <RadioGroup items={fruits} legend=\"Select a fruit\" required />\n </div>\n </section>\n\n <!-- Colors -->\n <section class=\"space-y-4\">\n <h2 id=\"Colors\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Colors\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Colors\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">color</code\n > to control the checked indicator color.\n </p>\n <div class=\"flex flex-wrap gap-8\">\n {#each colors as color (color)}\n <RadioGroup {color} value=\"a\" items={[{ value: 'a', label: color }]} />\n {/each}\n </div>\n </section>\n\n <!-- Sizes -->\n <section class=\"space-y-4\">\n <h2 id=\"Sizes\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Sizes\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Sizes\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">size</code> to\n control the dimensions.\n </p>\n <div class=\"flex flex-wrap items-start gap-8\">\n {#each sizes as size (size)}\n <RadioGroup {size} value=\"a\" items={[{ value: 'a', label: size }]} />\n {/each}\n </div>\n </section>\n\n <!-- Orientation -->\n <section class=\"space-y-4\">\n <h2 id=\"Orientation\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Orientation\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Orientation\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >orientation</code\n > to control layout direction.\n </p>\n <div class=\"flex flex-col gap-6\">\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Vertical (default)</p>\n <RadioGroup items={fruits} orientation=\"vertical\" />\n </div>\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Horizontal</p>\n <RadioGroup items={fruits} orientation=\"horizontal\" />\n </div>\n </div>\n </section>\n\n <!-- Variant -->\n <section class=\"space-y-4\">\n <h2 id=\"Variant\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Variant\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Variant\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >variant=\"card\"</code\n > to display each item as a bordered card. The border highlights when selected.\n </p>\n <div class=\"flex flex-col gap-6\">\n <div class=\"max-w-sm\">\n <p class=\"mb-2 text-xs text-on-surface-variant\">Vertical cards</p>\n <RadioGroup variant=\"card\" value=\"pro\" items={plans} color=\"primary\" />\n </div>\n <div class=\"max-w-sm\">\n <p class=\"mb-2 text-xs text-on-surface-variant\">Horizontal cards</p>\n <RadioGroup\n variant=\"card\"\n value=\"apple\"\n items={fruits}\n orientation=\"horizontal\"\n color=\"success\"\n />\n </div>\n </div>\n </section>\n\n <!-- Indicator -->\n <section class=\"space-y-4\">\n <h2 id=\"Indicator-Position\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Indicator-Position\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Indicator Position\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >indicator</code\n >\n to control where the radio appears:\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">start</code>,\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">end</code>, or\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">hidden</code>.\n </p>\n <div class=\"flex flex-wrap gap-8\">\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Start (default)</p>\n <RadioGroup indicator=\"start\" items={fruits} value=\"apple\" />\n </div>\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">End</p>\n <RadioGroup indicator=\"end\" items={fruits} value=\"banana\" />\n </div>\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Hidden</p>\n <RadioGroup indicator=\"hidden\" items={fruits} value=\"orange\" />\n </div>\n </div>\n <div class=\"max-w-sm\">\n <p class=\"mb-2 text-xs text-on-surface-variant\">Card + end indicator</p>\n <RadioGroup variant=\"card\" indicator=\"end\" items={plans} value=\"pro\" color=\"primary\" />\n </div>\n </section>\n\n <!-- Disabled -->\n <section class=\"space-y-4\">\n <h2 id=\"Disabled\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Disabled\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Disabled\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >disabled</code\n >\n to disable the entire group or individual items via\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >item.disabled</code\n >.\n </p>\n <div class=\"flex flex-wrap gap-8\">\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Entire group disabled</p>\n <RadioGroup items={fruits} disabled value=\"apple\" />\n </div>\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Individual item disabled</p>\n <RadioGroup items={partialDisabled} />\n </div>\n </div>\n </section>\n\n <!-- Loading -->\n <section class=\"space-y-4\">\n <h2 id=\"Loading\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Loading\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Loading\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >loading</code\n > to show a spinner and disable interaction.\n </p>\n <RadioGroup items={fruits} loading value=\"apple\" />\n </section>\n\n <!-- Custom Slots -->\n <section class=\"space-y-4\">\n <h2 id=\"Custom-Slots\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Custom-Slots\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Slots\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >legendSlot</code\n >,\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">labelSlot</code\n >, and\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >descriptionSlot</code\n > for fully custom content.\n </p>\n <div class=\"flex flex-col gap-6\">\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Custom legend</p>\n <RadioGroup items={fruits} value=\"apple\">\n {#snippet legendSlot()}\n <span\n class=\"mb-2 flex items-center gap-1.5 text-sm font-semibold text-on-surface\"\n >\n <span class=\"inline-block size-2 rounded-full bg-primary\"></span>\n Pick your favorite\n </span>\n {/snippet}\n </RadioGroup>\n </div>\n\n <div class=\"max-w-sm\">\n <p class=\"mb-2 text-xs text-on-surface-variant\">\n Custom label + description per item\n </p>\n <RadioGroup items={plans} value=\"pro\">\n {#snippet labelSlot({ item })}\n <span class=\"flex items-center gap-2 text-sm font-medium text-on-surface\">\n {item.label}\n {#if item.value === 'pro'}\n <span\n class=\"rounded bg-primary-container px-1.5 py-0.5 text-[10px] font-bold text-on-primary-container\"\n >POPULAR</span\n >\n {/if}\n </span>\n {/snippet}\n {#snippet descriptionSlot({ item })}\n <span class=\"text-xs text-on-surface-variant\">{item.description}</span>\n {/snippet}\n </RadioGroup>\n </div>\n </div>\n </section>\n\n <!-- FormField Integration -->\n <section class=\"space-y-4\">\n <h2 id=\"FormField-Integration\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#FormField-Integration\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n FormField Integration\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n When used inside a <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">FormField</code\n >, the RadioGroup automatically inherits size and error state.\n </p>\n <div class=\"max-w-sm space-y-4\">\n <FormField label=\"Subscription Plan\" description=\"Choose the plan that works for you.\">\n <RadioGroup items={plans} />\n </FormField>\n <FormField label=\"Preferred Fruit\" error=\"Please select a fruit.\">\n <RadioGroup items={fruits} />\n </FormField>\n </div>\n </section>\n\n <Separator />\n\n <!-- Real World Examples -->\n <section class=\"space-y-4\">\n <h2 id=\"Real-World-Examples\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Real-World-Examples\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World Examples\n </a>\n</h2>\n\n <div class=\"space-y-6\">\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">\n Pricing plan selection (card + end)\n </p>\n <div class=\"max-w-sm space-y-2\">\n <RadioGroup\n variant=\"card\"\n indicator=\"end\"\n value=\"pro\"\n color=\"primary\"\n items={[\n {\n value: 'starter',\n label: 'Starter',\n description: '$9/mo · Up to 3 projects'\n },\n {\n value: 'pro',\n label: 'Pro',\n description: '$29/mo · Unlimited projects'\n },\n {\n value: 'enterprise',\n label: 'Enterprise',\n description: 'Custom pricing · SSO + security'\n }\n ]}\n />\n </div>\n </div>\n\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">\n Notification frequency (horizontal + card)\n </p>\n <RadioGroup\n variant=\"card\"\n orientation=\"horizontal\"\n value=\"daily\"\n color=\"secondary\"\n items={[\n { value: 'realtime', label: 'Real-time' },\n { value: 'daily', label: 'Daily' },\n { value: 'weekly', label: 'Weekly' },\n { value: 'never', label: 'Never' }\n ]}\n />\n </div>\n\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">\n Theme selector (hidden indicator + custom label)\n </p>\n <RadioGroup\n indicator=\"hidden\"\n orientation=\"horizontal\"\n value=\"system\"\n items={[\n { value: 'light', label: 'Light' },\n { value: 'dark', label: 'Dark' },\n { value: 'system', label: 'System' }\n ]}\n >\n {#snippet labelSlot({ item })}\n <span class=\"text-sm text-on-surface\">{item.label}</span>\n {/snippet}\n </RadioGroup>\n </div>\n </div>\n </section>\n</div>\n",
144
148
  "select": "<script lang=\"ts\">\n import { Select, FormField, FieldGroup, Separator } from '$lib/index.js'\n import type { SelectItem, SelectItemType } from '$lib/index.js'\n\n const variants = ['outline', 'soft', 'subtle', 'ghost', 'none'] as const\n const colors = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'success',\n 'warning',\n 'error',\n 'info',\n 'surface'\n ] as const\n const sizes = ['xs', 'sm', 'md', 'lg', 'xl'] as const\n\n let bindValue = $state('')\n let multipleValue = $state<string[]>(['apple', 'banana'])\n\n const fruits: SelectItem[] = [\n { value: 'apple', label: 'Apple' },\n { value: 'banana', label: 'Banana' },\n { value: 'cherry', label: 'Cherry' },\n { value: 'grape', label: 'Grape' },\n { value: 'orange', label: 'Orange' }\n ]\n\n const iconItems: SelectItem[] = [\n { value: 'home', label: 'Home', icon: 'lucide:home' },\n { value: 'settings', label: 'Settings', icon: 'lucide:settings' },\n { value: 'profile', label: 'Profile', icon: 'lucide:user' },\n { value: 'notifications', label: 'Notifications', icon: 'lucide:bell' }\n ]\n\n const avatarItems: SelectItem[] = [\n {\n value: 'alice',\n label: 'Alice',\n avatar: { src: 'https://i.pravatar.cc/120?img=1', alt: 'Alice' }\n },\n {\n value: 'bob',\n label: 'Bob',\n avatar: { src: 'https://i.pravatar.cc/120?img=3', alt: 'Bob' }\n },\n {\n value: 'charlie',\n label: 'Charlie',\n avatar: { src: 'https://i.pravatar.cc/120?img=5', alt: 'Charlie' }\n }\n ]\n\n const descriptionItems: SelectItem[] = [\n { value: 'standard', label: 'Standard', description: 'Free shipping, 5-7 business days' },\n { value: 'express', label: 'Express', description: 'Paid shipping, 2-3 business days' },\n {\n value: 'overnight',\n label: 'Overnight',\n description: 'Premium shipping, next business day'\n }\n ]\n\n const groupedItems: SelectItemType[] = [\n { type: 'label', label: 'Fruits' },\n { value: 'apple', label: 'Apple' },\n { value: 'banana', label: 'Banana' },\n { type: 'separator' },\n { type: 'label', label: 'Vegetables' },\n { value: 'carrot', label: 'Carrot' },\n { value: 'broccoli', label: 'Broccoli' }\n ]\n\n const manyItems: SelectItem[] = Array.from({ length: 50 }, (_, i) => ({\n value: `item-${i + 1}`,\n label: `Item ${i + 1}`\n }))\n\n const disabledItems: SelectItem[] = [\n { value: 'active', label: 'Active' },\n { value: 'disabled', label: 'Disabled', disabled: true },\n { value: 'pending', label: 'Pending' }\n ]\n</script>\n\n<div class=\"space-y-8\">\n <h1 class=\"text-2xl font-bold text-on-surface\">Select</h1>\n\n <!-- Basic Usage -->\n <section class=\"space-y-4\">\n <h2 id=\"Basic-Usage\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Basic-Usage\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic Usage\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n A dropdown select component with variants, colors, icons, avatars, and integration with\n FormField and FieldGroup.\n </p>\n <div class=\"max-w-sm\">\n <Select items={fruits} placeholder=\"Pick a fruit...\" />\n </div>\n </section>\n\n <!-- Two-way Binding -->\n <section class=\"space-y-4\">\n <h2 id=\"Two-way-Binding\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Two-way-Binding\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Two-way Binding\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >bind:value</code\n > for reactive two-way data binding.\n </p>\n <div class=\"max-w-sm space-y-3\">\n <Select\n bind:value={bindValue}\n items={fruits}\n placeholder=\"Select a fruit...\"\n leadingIcon=\"lucide:apple\"\n />\n <p class=\"text-sm text-on-surface-variant\">\n Value: <span class=\"font-mono text-on-surface\">{bindValue || '(empty)'}</span>\n </p>\n </div>\n </section>\n\n <!-- Variants × Colors -->\n <section class=\"space-y-4\">\n <h2 id=\"Variants-times-Colors\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Variants-times-Colors\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Variants &times; Colors\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >variant</code\n >\n and\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">color</code> to control\n appearance.\n </p>\n <div class=\"overflow-x-auto\">\n <table class=\"w-full border-collapse\">\n <thead>\n <tr>\n <th class=\"px-3 py-2 text-left text-xs font-medium text-on-surface-variant\"\n ></th>\n {#each colors as color (color)}\n <th\n class=\"px-3 py-2 text-left text-xs font-medium text-on-surface-variant capitalize\"\n >{color}</th\n >\n {/each}\n </tr>\n </thead>\n <tbody>\n {#each variants as variant (variant)}\n <tr>\n <td\n class=\"px-3 py-2 text-xs font-medium text-on-surface-variant capitalize\"\n >{variant}</td\n >\n {#each colors as color (color)}\n <td class=\"px-3 py-2\">\n <Select {variant} {color} items={fruits} placeholder={color} />\n </td>\n {/each}\n </tr>\n {/each}\n </tbody>\n </table>\n </div>\n </section>\n\n <!-- Size -->\n <section class=\"space-y-4\">\n <h2 id=\"Size\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Size\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Size\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">size</code> to\n control the dimensions and text size.\n </p>\n <div class=\"flex flex-wrap items-end gap-4\">\n {#each sizes as size (size)}\n <div class=\"w-48\">\n <Select {size} items={fruits} placeholder=\"{size} size\" />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Icons -->\n <section class=\"space-y-4\">\n <h2 id=\"Icons\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Icons\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Icons\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >leadingIcon</code\n > to add an icon before the value.\n </p>\n <div class=\"flex flex-wrap gap-4\">\n <div class=\"w-64\">\n <Select leadingIcon=\"lucide:search\" items={fruits} placeholder=\"Search...\" />\n </div>\n <div class=\"w-64\">\n <Select leadingIcon=\"lucide:globe\" items={fruits} placeholder=\"Language\" />\n </div>\n </div>\n </section>\n\n <!-- Items with Icons -->\n <section class=\"space-y-4\">\n <h2 id=\"Items-with-Icons\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Items-with-Icons\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Items with Icons\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Each item can have its own icon.</p>\n <div class=\"w-64\">\n <Select items={iconItems} placeholder=\"Choose a page...\" />\n </div>\n </section>\n\n <!-- Avatar -->\n <section class=\"space-y-4\">\n <h2 id=\"Avatar\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Avatar\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Avatar\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >avatar</code\n > on the trigger, or provide avatars on individual items.\n </p>\n <div class=\"w-64\">\n <Select\n avatar={{ src: 'https://i.pravatar.cc/120?img=1', alt: 'User' }}\n items={avatarItems}\n placeholder=\"Assign to...\"\n />\n </div>\n </section>\n\n <!-- Item Descriptions -->\n <section class=\"space-y-4\">\n <h2 id=\"Item-Descriptions\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Item-Descriptions\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Item Descriptions\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Items can include descriptions shown below the label.\n </p>\n <div class=\"w-80\">\n <Select items={descriptionItems} placeholder=\"Choose shipping...\" />\n </div>\n </section>\n\n <!-- Grouped Items -->\n <section class=\"space-y-4\">\n <h2 id=\"Grouped-Items\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Grouped-Items\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Grouped Items\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >type: 'label'</code\n >\n and\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >type: 'separator'</code\n > to organize items into groups.\n </p>\n <div class=\"w-64\">\n <Select items={groupedItems} placeholder=\"Pick an item...\" />\n </div>\n </section>\n\n <!-- Disabled Items -->\n <section class=\"space-y-4\">\n <h2 id=\"Disabled-Items\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Disabled-Items\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Disabled Items\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Individual items can be disabled.</p>\n <div class=\"w-64\">\n <Select items={disabledItems} placeholder=\"Select status...\" />\n </div>\n </section>\n\n <!-- Many Items (Scrollable) -->\n <section class=\"space-y-4\">\n <h2 id=\"Many-Items-Scrollable\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Many-Items-Scrollable\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Many Items (Scrollable)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n With 50 items, the dropdown automatically scrolls within its max height.\n </p>\n <div class=\"w-64\">\n <Select items={manyItems} placeholder=\"Select from 50 items...\" />\n </div>\n </section>\n\n <!-- Loading -->\n <section class=\"space-y-4\">\n <h2 id=\"Loading\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Loading\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Loading\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >loading</code\n > to show a loading spinner.\n </p>\n <div class=\"w-64\">\n <Select loading items={fruits} placeholder=\"Loading...\" />\n </div>\n </section>\n\n <!-- Disabled -->\n <section class=\"space-y-4\">\n <h2 id=\"Disabled\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Disabled\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Disabled\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >disabled</code\n > to prevent interaction.\n </p>\n <div class=\"flex flex-wrap gap-4\">\n <div class=\"w-64\">\n <Select disabled items={fruits} placeholder=\"Disabled\" />\n </div>\n <div class=\"w-64\">\n <Select disabled items={fruits} value=\"apple\" />\n </div>\n </div>\n </section>\n\n <!-- Highlight -->\n <section class=\"space-y-4\">\n <h2 id=\"Highlight\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Highlight\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Highlight\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >highlight</code\n > to emphasize the ring color.\n </p>\n <div class=\"flex flex-wrap gap-4\">\n {#each colors as color (color)}\n <div class=\"w-48\">\n <Select highlight {color} items={fruits} placeholder={color} />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- FormField Integration -->\n <section class=\"space-y-4\">\n <h2 id=\"FormField-Integration\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#FormField-Integration\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n FormField Integration\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n When used inside a <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">FormField</code\n >, the Select automatically inherits size, error state, and accessibility attributes.\n </p>\n <div class=\"max-w-sm space-y-4\">\n <FormField label=\"Country\" description=\"Select your country of residence.\" required>\n <Select\n leadingIcon=\"lucide:globe\"\n items={[\n { value: 'us', label: 'United States' },\n { value: 'uk', label: 'United Kingdom' },\n { value: 'ca', label: 'Canada' },\n { value: 'au', label: 'Australia' }\n ]}\n placeholder=\"Choose a country\"\n />\n </FormField>\n\n <FormField label=\"Role\" error=\"Please select a role.\">\n <Select\n items={[\n { value: 'admin', label: 'Admin' },\n { value: 'editor', label: 'Editor' },\n { value: 'viewer', label: 'Viewer' }\n ]}\n placeholder=\"Choose a role\"\n />\n </FormField>\n </div>\n </section>\n\n <!-- FieldGroup Integration -->\n <section class=\"space-y-4\">\n <h2 id=\"FieldGroup-Integration\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#FieldGroup-Integration\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n FieldGroup Integration\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n When used inside a <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">FieldGroup</code\n >, selects are visually connected.\n </p>\n <FieldGroup orientation=\"horizontal\">\n <Select\n items={[\n { value: 'mr', label: 'Mr.' },\n { value: 'mrs', label: 'Mrs.' },\n { value: 'ms', label: 'Ms.' }\n ]}\n placeholder=\"Title\"\n />\n <Select items={fruits} placeholder=\"First\" />\n <Select items={fruits} placeholder=\"Last\" />\n </FieldGroup>\n </section>\n\n <Separator />\n\n <!-- Real World Examples -->\n <section class=\"space-y-4\">\n <h2 id=\"Real-World-Examples\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Real-World-Examples\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World Examples\n </a>\n</h2>\n\n <div class=\"space-y-6\">\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">User assignment</p>\n <div\n class=\"max-w-sm space-y-4 rounded-lg border border-outline-variant bg-surface-container-low p-4\"\n >\n <FormField label=\"Assignee\" required>\n <Select items={avatarItems} placeholder=\"Select a team member...\" />\n </FormField>\n\n <FormField label=\"Priority\">\n <Select\n items={[\n { value: 'low', label: 'Low', icon: 'lucide:arrow-down' },\n { value: 'medium', label: 'Medium', icon: 'lucide:minus' },\n { value: 'high', label: 'High', icon: 'lucide:arrow-up' },\n { value: 'urgent', label: 'Urgent', icon: 'lucide:alert-triangle' }\n ]}\n placeholder=\"Set priority...\"\n />\n </FormField>\n </div>\n </div>\n\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Shipping options</p>\n <div class=\"max-w-md\">\n <Select items={descriptionItems} placeholder=\"Choose shipping method...\" />\n </div>\n </div>\n\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Grouped categories</p>\n <div class=\"max-w-sm\">\n <Select\n items={groupedItems}\n placeholder=\"Select category...\"\n leadingIcon=\"lucide:tag\"\n />\n </div>\n </div>\n </div>\n </section>\n\n <Separator />\n\n <section>\n <h2 id=\"Multiple-selection\" class=\"mb-3 text-lg font-semibold\">\n<a href=\"#Multiple-selection\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Multiple selection\n </a>\n</h2>\n <p class=\"mb-4 text-sm text-on-surface-variant\">\n Set <code>multiple</code> to allow selecting more than one option. The\n <code>value</code> becomes a <code>string[]</code> and selected labels are joined with\n <code>separator</code> (default <code>, </code>).\n </p>\n\n <div class=\"grid gap-4 sm:grid-cols-2\">\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Default (comma-separated)</p>\n <div class=\"max-w-sm\">\n <Select\n multiple\n bind:value={multipleValue}\n items={fruits}\n placeholder=\"Pick fruits...\"\n />\n </div>\n <p class=\"mt-2 text-xs text-on-surface-variant\">\n Selected: {JSON.stringify(multipleValue)}\n </p>\n </div>\n\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Custom separator + leading icon</p>\n <div class=\"max-w-sm\">\n <Select\n multiple\n items={iconItems}\n separator=\" • \"\n leadingIcon=\"lucide:list-checks\"\n placeholder=\"Pick sections...\"\n />\n </div>\n </div>\n </div>\n </section>\n</div>\n",
145
149
  "select-menu": "<script lang=\"ts\">\n import { SelectMenu, FormField, FieldGroup, Separator, Icon } from '$lib/index.js'\n import type { SelectMenuItem, SelectMenuItemType } from '$lib/index.js'\n\n const variants = ['outline', 'soft', 'subtle', 'ghost', 'none'] as const\n const colors = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'success',\n 'warning',\n 'error',\n 'info',\n 'surface'\n ] as const\n const sizes = ['xs', 'sm', 'md', 'lg', 'xl'] as const\n\n let bindValue = $state('')\n let multipleValue = $state<string[]>(['apple', 'banana'])\n let chipValue = $state<string[]>(['alice'])\n let createValue = $state('')\n let createLastEvent = $state('')\n let createTags = $state<string[]>(['svelte', 'tailwind'])\n let createTagItems = $state<SelectMenuItem[]>([\n { value: 'svelte', label: 'Svelte' },\n { value: 'tailwind', label: 'Tailwind' },\n { value: 'vite', label: 'Vite' }\n ])\n\n const fruits: SelectMenuItem[] = [\n { value: 'apple', label: 'Apple' },\n { value: 'banana', label: 'Banana' },\n { value: 'cherry', label: 'Cherry' },\n { value: 'grape', label: 'Grape' },\n { value: 'orange', label: 'Orange' }\n ]\n\n const iconItems: SelectMenuItem[] = [\n { value: 'home', label: 'Home', icon: 'lucide:home' },\n { value: 'settings', label: 'Settings', icon: 'lucide:settings' },\n { value: 'profile', label: 'Profile', icon: 'lucide:user' },\n { value: 'notifications', label: 'Notifications', icon: 'lucide:bell' }\n ]\n\n const avatarItems: SelectMenuItem[] = [\n {\n value: 'alice',\n label: 'Alice',\n avatar: { src: 'https://i.pravatar.cc/120?img=1', alt: 'Alice' }\n },\n {\n value: 'bob',\n label: 'Bob',\n avatar: { src: 'https://i.pravatar.cc/120?img=3', alt: 'Bob' }\n },\n {\n value: 'charlie',\n label: 'Charlie',\n avatar: { src: 'https://i.pravatar.cc/120?img=5', alt: 'Charlie' }\n }\n ]\n\n const descriptionItems: SelectMenuItem[] = [\n { value: 'standard', label: 'Standard', description: 'Free shipping, 5-7 business days' },\n { value: 'express', label: 'Express', description: 'Paid shipping, 2-3 business days' },\n {\n value: 'overnight',\n label: 'Overnight',\n description: 'Premium shipping, next business day'\n }\n ]\n\n const groupedItems: SelectMenuItemType[] = [\n { type: 'label', label: 'Fruits' },\n { value: 'apple', label: 'Apple' },\n { value: 'banana', label: 'Banana' },\n { type: 'separator' },\n { type: 'label', label: 'Vegetables' },\n { value: 'carrot', label: 'Carrot' },\n { value: 'broccoli', label: 'Broccoli' }\n ]\n\n const disabledItems: SelectMenuItem[] = [\n { value: 'active', label: 'Active' },\n { value: 'disabled', label: 'Disabled', disabled: true },\n { value: 'pending', label: 'Pending' }\n ]\n\n const manyItems: SelectMenuItem[] = Array.from({ length: 50 }, (_, i) => ({\n value: `item-${i + 1}`,\n label: `Item ${i + 1}`\n }))\n\n type StatusItem = SelectMenuItem & { status?: 'active' | 'idle' | 'offline' }\n const statusItems: StatusItem[] = [\n {\n value: 'alice',\n label: 'Alice Johnson',\n description: 'Frontend Engineer',\n status: 'active',\n avatar: { src: 'https://i.pravatar.cc/120?img=1', alt: 'Alice' }\n },\n {\n value: 'bob',\n label: 'Bob Smith',\n description: 'Backend Engineer',\n status: 'idle',\n avatar: { src: 'https://i.pravatar.cc/120?img=3', alt: 'Bob' }\n },\n {\n value: 'charlie',\n label: 'Charlie Lee',\n description: 'Designer',\n status: 'offline',\n avatar: { src: 'https://i.pravatar.cc/120?img=5', alt: 'Charlie' }\n },\n {\n value: 'diana',\n label: 'Diana Prince',\n description: 'Product Manager',\n status: 'active',\n avatar: { src: 'https://i.pravatar.cc/120?img=9', alt: 'Diana' }\n }\n ]\n\n const statusColor: Record<string, string> = {\n active: 'bg-success',\n idle: 'bg-warning',\n offline: 'bg-on-surface-variant/40'\n }\n\n const planItems: SelectMenuItem[] = [\n { value: 'free', label: 'Free', description: 'Up to 3 projects' },\n { value: 'pro', label: 'Pro', description: 'Unlimited projects' },\n { value: 'team', label: 'Team', description: 'Collaboration features' },\n { value: 'enterprise', label: 'Enterprise', description: 'Custom pricing' }\n ]\n\n const planBadge: Record<string, { label: string; class: string }> = {\n free: { label: 'FREE', class: 'bg-surface-container text-on-surface-variant' },\n pro: { label: 'PRO', class: 'bg-primary-container text-on-primary-container' },\n team: { label: 'TEAM', class: 'bg-secondary-container text-on-secondary-container' },\n enterprise: { label: 'ENT', class: 'bg-tertiary-container text-on-tertiary-container' }\n }\n</script>\n\n<div class=\"space-y-8\">\n <h1 class=\"text-2xl font-bold text-on-surface\">SelectMenu</h1>\n\n <!-- Basic Usage -->\n <section class=\"space-y-4\">\n <h2 id=\"Basic-Usage\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Basic-Usage\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic Usage\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n A searchable select dropdown built on bits-ui Combobox. Supports filtering, icons,\n avatars, groups, and empty states.\n </p>\n <div class=\"max-w-sm\">\n <SelectMenu items={fruits} placeholder=\"Pick a fruit...\" />\n </div>\n </section>\n\n <!-- Two-way Binding -->\n <section class=\"space-y-4\">\n <h2 id=\"Two-way-Binding\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Two-way-Binding\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Two-way Binding\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >bind:value</code\n > for reactive two-way data binding.\n </p>\n <div class=\"max-w-sm space-y-3\">\n <SelectMenu\n bind:value={bindValue}\n items={fruits}\n placeholder=\"Select a fruit...\"\n leadingIcon=\"lucide:apple\"\n />\n <p class=\"text-sm text-on-surface-variant\">\n Value: <span class=\"font-mono text-on-surface\">{bindValue || '(empty)'}</span>\n </p>\n </div>\n </section>\n\n <!-- Variants × Colors -->\n <section class=\"space-y-4\">\n <h2 id=\"Variants-times-Colors\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Variants-times-Colors\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Variants &times; Colors\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >variant</code\n >\n and\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">color</code> to control\n appearance.\n </p>\n <div class=\"overflow-x-auto\">\n <table class=\"w-full border-collapse\">\n <thead>\n <tr>\n <th class=\"px-3 py-2 text-left text-xs font-medium text-on-surface-variant\"\n ></th>\n {#each colors as color (color)}\n <th\n class=\"px-3 py-2 text-left text-xs font-medium text-on-surface-variant capitalize\"\n >{color}</th\n >\n {/each}\n </tr>\n </thead>\n <tbody>\n {#each variants as variant (variant)}\n <tr>\n <td\n class=\"px-3 py-2 text-xs font-medium text-on-surface-variant capitalize\"\n >{variant}</td\n >\n {#each colors as color (color)}\n <td class=\"px-3 py-2\">\n <SelectMenu\n {variant}\n {color}\n items={fruits}\n placeholder={color}\n />\n </td>\n {/each}\n </tr>\n {/each}\n </tbody>\n </table>\n </div>\n </section>\n\n <!-- Size -->\n <section class=\"space-y-4\">\n <h2 id=\"Size\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Size\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Size\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">size</code> to\n control the dimensions and text size.\n </p>\n <div class=\"flex flex-wrap items-end gap-4\">\n {#each sizes as size (size)}\n <div class=\"w-48\">\n <SelectMenu {size} items={fruits} placeholder=\"{size} size\" />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Icons -->\n <section class=\"space-y-4\">\n <h2 id=\"Icons\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Icons\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Icons\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >leadingIcon</code\n > to add an icon before the value.\n </p>\n <div class=\"flex flex-wrap gap-4\">\n <div class=\"w-64\">\n <SelectMenu leadingIcon=\"lucide:search\" items={fruits} placeholder=\"Search...\" />\n </div>\n <div class=\"w-64\">\n <SelectMenu leadingIcon=\"lucide:globe\" items={fruits} placeholder=\"Language\" />\n </div>\n </div>\n </section>\n\n <!-- Items with Icons -->\n <section class=\"space-y-4\">\n <h2 id=\"Items-with-Icons\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Items-with-Icons\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Items with Icons\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Each item can have its own icon.</p>\n <div class=\"w-64\">\n <SelectMenu items={iconItems} placeholder=\"Choose a page...\" />\n </div>\n </section>\n\n <!-- Avatar -->\n <section class=\"space-y-4\">\n <h2 id=\"Avatar\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Avatar\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Avatar\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >avatar</code\n > on the trigger, or provide avatars on individual items.\n </p>\n <div class=\"w-64\">\n <SelectMenu\n avatar={{ src: 'https://i.pravatar.cc/120?img=1', alt: 'User' }}\n items={avatarItems}\n placeholder=\"Assign to...\"\n />\n </div>\n </section>\n\n <!-- Item Descriptions -->\n <section class=\"space-y-4\">\n <h2 id=\"Item-Descriptions\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Item-Descriptions\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Item Descriptions\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Items can include descriptions shown below the label.\n </p>\n <div class=\"w-80\">\n <SelectMenu items={descriptionItems} placeholder=\"Choose shipping...\" />\n </div>\n </section>\n\n <!-- Grouped Items -->\n <section class=\"space-y-4\">\n <h2 id=\"Grouped-Items\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Grouped-Items\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Grouped Items\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >type: 'label'</code\n >\n and\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >type: 'separator'</code\n > to organize items into groups.\n </p>\n <div class=\"w-64\">\n <SelectMenu items={groupedItems} placeholder=\"Pick an item...\" />\n </div>\n </section>\n\n <!-- Disabled Items -->\n <section class=\"space-y-4\">\n <h2 id=\"Disabled-Items\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Disabled-Items\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Disabled Items\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Individual items can be disabled.</p>\n <div class=\"w-64\">\n <SelectMenu items={disabledItems} placeholder=\"Select status...\" />\n </div>\n </section>\n\n <!-- Filtering -->\n <section class=\"space-y-4\">\n <h2 id=\"Filtering\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Filtering\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Filtering\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n The dropdown includes a search input. Items are filtered client-side by\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >filterFields</code\n > (defaults to label and value).\n </p>\n <div class=\"w-64\">\n <SelectMenu items={manyItems} placeholder=\"Search 50 items...\" />\n </div>\n </section>\n\n <!-- Loading -->\n <section class=\"space-y-4\">\n <h2 id=\"Loading\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Loading\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Loading\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >loading</code\n > to show a loading spinner.\n </p>\n <div class=\"w-64\">\n <SelectMenu loading items={fruits} placeholder=\"Loading...\" />\n </div>\n </section>\n\n <!-- Disabled -->\n <section class=\"space-y-4\">\n <h2 id=\"Disabled\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Disabled\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Disabled\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >disabled</code\n > to prevent interaction.\n </p>\n <div class=\"flex flex-wrap gap-4\">\n <div class=\"w-64\">\n <SelectMenu disabled items={fruits} placeholder=\"Disabled\" />\n </div>\n <div class=\"w-64\">\n <SelectMenu disabled items={fruits} value=\"apple\" />\n </div>\n </div>\n </section>\n\n <!-- Highlight -->\n <section class=\"space-y-4\">\n <h2 id=\"Highlight\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Highlight\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Highlight\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >highlight</code\n > to emphasize the ring color.\n </p>\n <div class=\"flex flex-wrap gap-4\">\n {#each colors as color (color)}\n <div class=\"w-48\">\n <SelectMenu highlight {color} items={fruits} placeholder={color} />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- FormField Integration -->\n <section class=\"space-y-4\">\n <h2 id=\"FormField-Integration\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#FormField-Integration\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n FormField Integration\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n When used inside a <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">FormField</code\n >, the SelectMenu automatically inherits size, error state, and accessibility\n attributes.\n </p>\n <div class=\"max-w-sm space-y-4\">\n <FormField label=\"Country\" description=\"Select your country of residence.\" required>\n <SelectMenu\n leadingIcon=\"lucide:globe\"\n items={[\n { value: 'us', label: 'United States' },\n { value: 'uk', label: 'United Kingdom' },\n { value: 'ca', label: 'Canada' },\n { value: 'au', label: 'Australia' }\n ]}\n placeholder=\"Choose a country\"\n />\n </FormField>\n\n <FormField label=\"Role\" error=\"Please select a role.\">\n <SelectMenu\n items={[\n { value: 'admin', label: 'Admin' },\n { value: 'editor', label: 'Editor' },\n { value: 'viewer', label: 'Viewer' }\n ]}\n placeholder=\"Choose a role\"\n />\n </FormField>\n </div>\n </section>\n\n <!-- FieldGroup Integration -->\n <section class=\"space-y-4\">\n <h2 id=\"FieldGroup-Integration\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#FieldGroup-Integration\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n FieldGroup Integration\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n When used inside a <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">FieldGroup</code\n >, select menus are visually connected.\n </p>\n <FieldGroup orientation=\"horizontal\">\n <SelectMenu\n items={[\n { value: 'mr', label: 'Mr.' },\n { value: 'mrs', label: 'Mrs.' },\n { value: 'ms', label: 'Ms.' }\n ]}\n placeholder=\"Title\"\n />\n <SelectMenu items={fruits} placeholder=\"First\" />\n <SelectMenu items={fruits} placeholder=\"Last\" />\n </FieldGroup>\n </section>\n\n <!-- Custom Slots -->\n <section class=\"space-y-4\">\n <h2 id=\"Custom-Slots\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Custom-Slots\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Slots\n </a>\n</h2>\n\n <!-- itemLeading slot -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >itemLeading</code\n >\n — Custom leading per item (online status dot)\n </p>\n <div class=\"w-72\">\n <SelectMenu items={statusItems} placeholder=\"Assign to...\">\n {#snippet itemLeading({ item })}\n {@const s = item as StatusItem}\n <div class=\"relative shrink-0\">\n <img\n src={s.avatar?.src}\n alt={s.avatar?.alt ?? ''}\n class=\"size-7 rounded-full object-cover\"\n />\n <span\n class=\"absolute right-0 bottom-0 size-2 rounded-full ring-1 ring-surface-container-low {statusColor[\n s.status ?? 'offline'\n ]}\"\n ></span>\n </div>\n {/snippet}\n </SelectMenu>\n </div>\n </div>\n\n <!-- itemLabel slot -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >itemLabel</code\n >\n — Custom label with badge\n </p>\n <div class=\"w-64\">\n <SelectMenu items={planItems} placeholder=\"Choose a plan...\">\n {#snippet itemLabel({ item })}\n <span class=\"flex items-center gap-2\">\n <span class=\"flex-1 text-sm text-on-surface\">{item.label}</span>\n <span\n class=\"rounded px-1.5 py-0.5 text-[10px] font-bold {planBadge[\n item.value\n ]?.class}\"\n >\n {planBadge[item.value]?.label}\n </span>\n </span>\n {#if item.description}\n <span class=\"text-xs text-on-surface-variant\">{item.description}</span>\n {/if}\n {/snippet}\n </SelectMenu>\n </div>\n </div>\n\n <!-- itemTrailing slot -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >itemTrailing</code\n >\n — Custom trailing (keyboard shortcut)\n </p>\n <div class=\"w-64\">\n <SelectMenu\n items={[\n { value: 'cut', label: 'Cut' },\n { value: 'copy', label: 'Copy' },\n { value: 'paste', label: 'Paste' },\n { value: 'select-all', label: 'Select All' }\n ]}\n placeholder=\"Choose action...\"\n >\n {#snippet itemTrailing({ item, selected })}\n {#if selected}\n <Icon name=\"lucide:check\" class=\"size-4 text-primary\" />\n {:else}\n <span class=\"text-xs text-on-surface-variant/60\">\n {#if item.value === 'cut'}⌘X\n {:else if item.value === 'copy'}⌘C\n {:else if item.value === 'paste'}⌘V\n {:else if item.value === 'select-all'}⌘A{/if}\n </span>\n {/if}\n {/snippet}\n </SelectMenu>\n </div>\n </div>\n\n <!-- item slot -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">item</code>\n — Fully custom item rendering\n </p>\n <div class=\"w-80\">\n <SelectMenu items={statusItems} placeholder=\"Select a team member...\">\n {#snippet item({ item, selected })}\n {@const s = item as StatusItem}\n <div\n class=\"flex items-center gap-3 rounded-md px-2 py-1.5 {selected\n ? 'bg-primary-container'\n : 'hover:bg-surface-container-high'} cursor-pointer transition-colors\"\n >\n <div class=\"relative shrink-0\">\n <img\n src={s.avatar?.src}\n alt={s.avatar?.alt ?? ''}\n class=\"size-8 rounded-full object-cover\"\n />\n <span\n class=\"absolute right-0 bottom-0 size-2.5 rounded-full ring-2 ring-surface-container-low {statusColor[\n s.status ?? 'offline'\n ]}\"\n ></span>\n </div>\n <div class=\"min-w-0 flex-1\">\n <p\n class=\"truncate text-sm font-medium {selected\n ? 'text-on-primary-container'\n : 'text-on-surface'}\"\n >\n {item.label}\n </p>\n <p\n class=\"truncate text-xs {selected\n ? 'text-on-primary-container/70'\n : 'text-on-surface-variant'}\"\n >\n {item.description}\n </p>\n </div>\n <span\n class=\"shrink-0 text-xs capitalize {statusColor[\n s.status ?? 'offline'\n ]\n .replace('bg-', 'text-')\n .replace('bg-on-', 'text-on-')} {s.status === 'offline'\n ? 'text-on-surface-variant/40'\n : ''}\"\n >\n {s.status}\n </span>\n </div>\n {/snippet}\n </SelectMenu>\n </div>\n </div>\n\n <!-- empty slot -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">empty</code\n >\n — Custom empty state\n </p>\n <div class=\"w-64\">\n <SelectMenu items={fruits} placeholder=\"Search (try 'xyz')...\">\n {#snippet empty({ searchTerm })}\n <div class=\"flex flex-col items-center gap-1 py-6 text-center\">\n <Icon\n name=\"lucide:search-x\"\n class=\"size-8 text-on-surface-variant/40\"\n />\n <p class=\"text-sm font-medium text-on-surface\">No match</p>\n <p class=\"text-xs text-on-surface-variant\">\n Nothing found for <span class=\"font-mono text-primary\"\n >\"{searchTerm}\"</span\n >\n </p>\n </div>\n {/snippet}\n </SelectMenu>\n </div>\n </div>\n\n <!-- leadingSlot + trailingSlot -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >leadingSlot</code\n >\n /\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >trailingSlot</code\n >\n — Custom trigger leading & trailing\n </p>\n <div class=\"w-72\">\n <SelectMenu items={fruits} placeholder=\"Pick a fruit...\">\n {#snippet leadingSlot()}\n <span class=\"text-base\">🍎</span>\n {/snippet}\n {#snippet trailingSlot()}\n <Icon\n name=\"lucide:chevrons-up-down\"\n class=\"size-4 text-on-surface-variant/60\"\n />\n {/snippet}\n </SelectMenu>\n </div>\n </div>\n </section>\n\n <Separator />\n\n <!-- Real World Examples -->\n <section class=\"space-y-4\">\n <h2 id=\"Real-World-Examples\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Real-World-Examples\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World Examples\n </a>\n</h2>\n\n <div class=\"space-y-6\">\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">User assignment</p>\n <div\n class=\"max-w-sm space-y-4 rounded-lg border border-outline-variant bg-surface-container-low p-4\"\n >\n <FormField label=\"Assignee\" required>\n <SelectMenu items={avatarItems} placeholder=\"Select a team member...\" />\n </FormField>\n\n <FormField label=\"Priority\">\n <SelectMenu\n items={[\n { value: 'low', label: 'Low', icon: 'lucide:arrow-down' },\n { value: 'medium', label: 'Medium', icon: 'lucide:minus' },\n { value: 'high', label: 'High', icon: 'lucide:arrow-up' },\n { value: 'urgent', label: 'Urgent', icon: 'lucide:alert-triangle' }\n ]}\n placeholder=\"Set priority...\"\n />\n </FormField>\n </div>\n </div>\n\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Shipping options</p>\n <div class=\"max-w-md\">\n <SelectMenu items={descriptionItems} placeholder=\"Choose shipping method...\" />\n </div>\n </div>\n\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Large list with search</p>\n <div class=\"max-w-sm\">\n <SelectMenu\n items={manyItems}\n placeholder=\"Search from 50 items...\"\n leadingIcon=\"lucide:search\"\n />\n </div>\n </div>\n </div>\n </section>\n\n <Separator />\n\n <section>\n <h2 id=\"Multiple-selection\" class=\"mb-3 text-lg font-semibold\">\n<a href=\"#Multiple-selection\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Multiple selection\n </a>\n</h2>\n <p class=\"mb-4 text-sm text-on-surface-variant\">\n Pass <code>multiple</code> to allow selecting more than one option. The dropdown stays\n open after each click so users can pick several items in a row. Use the\n <code>selected</code> snippet to render chips/tags instead of the default comma-separated\n labels.\n </p>\n\n <div class=\"grid gap-4 sm:grid-cols-2\">\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Default (comma-separated)</p>\n <div class=\"max-w-sm\">\n <SelectMenu\n multiple\n bind:value={multipleValue}\n items={fruits}\n placeholder=\"Pick fruits...\"\n />\n </div>\n <p class=\"mt-2 text-xs text-on-surface-variant\">\n Selected: {JSON.stringify(multipleValue)}\n </p>\n </div>\n\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">\n With chips via `selected` snippet\n </p>\n <div class=\"max-w-sm\">\n <SelectMenu\n multiple\n bind:value={chipValue}\n items={avatarItems}\n placeholder=\"Add members...\"\n leadingIcon=\"lucide:users\"\n >\n {#snippet selected({ items, remove })}\n <span class=\"flex flex-wrap items-center gap-1\">\n {#each items as item (item.value)}\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-primary-container px-2 py-0.5 text-xs text-on-primary-container\"\n >\n {item.label ?? item.value}\n <button\n type=\"button\"\n aria-label={`Remove ${item.label ?? item.value}`}\n class=\"-mr-0.5 inline-flex size-3 items-center justify-center rounded-full hover:bg-on-primary-container/20\"\n onclick={(e) => {\n e.stopPropagation()\n remove(item.value)\n }}\n >\n <Icon name=\"lucide:x\" class=\"size-2.5\" />\n </button>\n </span>\n {/each}\n </span>\n {/snippet}\n </SelectMenu>\n </div>\n <p class=\"mt-2 text-xs text-on-surface-variant\">\n Selected: {JSON.stringify(chipValue)}\n </p>\n </div>\n </div>\n </section>\n\n <Separator />\n\n <section>\n <h2 id=\"Create-new-items-on-the-fly\" class=\"mb-3 text-lg font-semibold\">\n<a href=\"#Create-new-items-on-the-fly\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Create new items on the fly\n </a>\n</h2>\n <p class=\"mb-4 text-sm text-on-surface-variant\">\n Pass <code>createItem</code> to let users add values that are not in the original\n <code>items</code> list. Use <code>'lazy'</code> (default when <code>true</code>) to\n only offer the create option when no item matches the search; use\n <code>'always'</code> to keep it visible. The <code>onCreate</code> callback fires with\n the trimmed search term, and the component internally tracks the new value so the\n trigger can render its label even if the caller does not push it into\n <code>items</code>. Press <kbd>Enter</kbd> when there are no matches to create.\n </p>\n\n <div class=\"grid gap-4 sm:grid-cols-2\">\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Single mode (lazy)</p>\n <div class=\"max-w-sm\">\n <SelectMenu\n bind:value={createValue}\n items={fruits}\n createItem\n placeholder=\"Pick or create a fruit...\"\n leadingIcon=\"lucide:apple\"\n onCreate={(v) => (createLastEvent = v)}\n />\n </div>\n <p class=\"mt-2 text-xs text-on-surface-variant\">\n Value: <span class=\"font-mono text-on-surface\">{createValue || '(empty)'}</span>\n </p>\n {#if createLastEvent}\n <p class=\"mt-1 text-xs text-on-surface-variant\">\n Last <code>onCreate</code>:\n <span class=\"font-mono text-on-surface\">{createLastEvent}</span>\n </p>\n {/if}\n </div>\n\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">\n Multiple + always + caller-managed items\n </p>\n <div class=\"max-w-sm\">\n <SelectMenu\n multiple\n bind:value={createTags}\n items={createTagItems}\n createItem=\"always\"\n placeholder=\"Add tags...\"\n leadingIcon=\"lucide:tag\"\n createItemLabel={(v) => `Add tag \"${v}\"`}\n onCreate={(v) =>\n (createTagItems = [...createTagItems, { value: v, label: v }])}\n />\n </div>\n <p class=\"mt-2 text-xs text-on-surface-variant\">\n Tags: {JSON.stringify(createTags)}\n </p>\n <p class=\"mt-1 text-xs text-on-surface-variant\">\n Items ({createTagItems.length}): {createTagItems.map((i) => i.label).join(', ')}\n </p>\n </div>\n </div>\n </section>\n</div>\n",
146
150
  "slider": "<script lang=\"ts\">\n import { Slider, FormField } from '$lib/index.js'\n\n const colors = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'success',\n 'warning',\n 'error',\n 'info',\n 'surface'\n ] as const\n\n const sizes = ['xs', 'sm', 'md', 'lg', 'xl'] as const\n\n let basicValue = $state(40)\n let rangeValue = $state([20, 80])\n let stepValue = $state(50)\n let tooltipValue = $state(65)\n let tooltipRangeValue = $state([30, 70])\n let verticalValue = $state(60)\n let verticalRangeValue = $state([25, 75])\n let formValue = $state(50)\n let formFieldValue = $state(40)\n let formFieldErrorValue = $state(15)\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">Slider</h1>\n <p class=\"text-on-surface-variant\">\n Accessible range input built on bits-ui. Supports single thumb, range, tooltip, and all\n orientations.\n </p>\n </div>\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-6\">\n <Slider bind:value={basicValue} />\n <p class=\"text-sm text-on-surface-variant\">Value: {basicValue}</p>\n </div>\n </section>\n\n <!-- Range -->\n <section class=\"space-y-3\">\n <h2 id=\"Range-Multiple-Thumbs\" class=\"text-lg font-semibold\">\n<a href=\"#Range-Multiple-Thumbs\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Range (Multiple Thumbs)\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-6\">\n <Slider bind:value={rangeValue} />\n <p class=\"text-sm text-on-surface-variant\">Value: [{rangeValue.join(', ')}]</p>\n </div>\n </section>\n\n <!-- Step -->\n <section class=\"space-y-3\">\n <h2 id=\"Step\" class=\"text-lg font-semibold\">\n<a href=\"#Step\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Step\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-6 rounded-lg bg-surface-container-high p-6 sm:grid-cols-2\">\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium text-on-surface-variant\">step=10</p>\n <Slider bind:value={stepValue} step={10} />\n <p class=\"text-sm text-on-surface-variant\">Value: {stepValue}</p>\n </div>\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium text-on-surface-variant\">\n Discrete steps: [0, 25, 50, 75, 100]\n </p>\n <Slider value={25} step={[0, 25, 50, 75, 100]} />\n </div>\n </div>\n </section>\n\n <!-- Tooltip -->\n <section class=\"space-y-3\">\n <h2 id=\"Tooltip\" class=\"text-lg font-semibold\">\n<a href=\"#Tooltip\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Tooltip\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-8 rounded-lg bg-surface-container-high p-6 sm:grid-cols-2\">\n <div class=\"space-y-3 pt-6\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Single</p>\n <Slider bind:value={tooltipValue} tooltip />\n </div>\n <div class=\"space-y-3 pt-6\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Range</p>\n <Slider bind:value={tooltipRangeValue} tooltip />\n </div>\n </div>\n </section>\n\n <!-- Orientation -->\n <section class=\"space-y-3\">\n <h2 id=\"Orientation\" class=\"text-lg font-semibold\">\n<a href=\"#Orientation\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Orientation\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-6\">\n <div class=\"flex items-start gap-12\">\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Horizontal (default)</p>\n <div class=\"w-64\">\n <Slider bind:value={verticalValue} />\n </div>\n </div>\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Vertical</p>\n <div class=\"h-40\">\n <Slider bind:value={verticalValue} orientation=\"vertical\" />\n </div>\n </div>\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Vertical Range</p>\n <div class=\"h-40\">\n <Slider bind:value={verticalRangeValue} orientation=\"vertical\" />\n </div>\n </div>\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Vertical + Tooltip</p>\n <div class=\"h-40 ps-6\">\n <Slider bind:value={verticalValue} orientation=\"vertical\" tooltip />\n </div>\n </div>\n </div>\n </div>\n </section>\n\n <!-- Colors -->\n <section class=\"space-y-3\">\n <h2 id=\"Colors\" class=\"text-lg font-semibold\">\n<a href=\"#Colors\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Colors\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-6\">\n {#each colors as color (color)}\n <div class=\"flex items-center gap-4\">\n <span class=\"w-20 text-sm text-on-surface-variant\">{color}</span>\n <div class=\"flex-1\">\n <Slider {color} value={55} />\n </div>\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Sizes -->\n <section class=\"space-y-3\">\n <h2 id=\"Sizes\" class=\"text-lg font-semibold\">\n<a href=\"#Sizes\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Sizes\n </a>\n</h2>\n <div class=\"space-y-5 rounded-lg bg-surface-container-high p-6\">\n {#each sizes as size (size)}\n <div class=\"flex items-center gap-4\">\n <span class=\"w-6 text-xs text-on-surface-variant\">{size}</span>\n <div class=\"flex-1\">\n <Slider {size} value={60} />\n </div>\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Disabled -->\n <section class=\"space-y-3\">\n <h2 id=\"Disabled\" class=\"text-lg font-semibold\">\n<a href=\"#Disabled\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Disabled\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-6 rounded-lg bg-surface-container-high p-6 sm:grid-cols-2\">\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Single</p>\n <Slider value={40} disabled />\n </div>\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Range</p>\n <Slider value={[25, 75]} disabled />\n </div>\n </div>\n </section>\n\n <!-- Min / Max -->\n <section class=\"space-y-3\">\n <h2 id=\"Min--Max\" class=\"text-lg font-semibold\">\n<a href=\"#Min--Max\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Min / Max\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-6 rounded-lg bg-surface-container-high p-6 sm:grid-cols-2\">\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium text-on-surface-variant\">min=20, max=80</p>\n <Slider min={20} max={80} value={50} tooltip />\n </div>\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium text-on-surface-variant\">min=-50, max=50</p>\n <Slider min={-50} max={50} value={0} tooltip />\n </div>\n </div>\n </section>\n\n <!-- Form Integration -->\n <section class=\"space-y-3\">\n <h2 id=\"Form-Integration\" class=\"text-lg font-semibold\">\n<a href=\"#Form-Integration\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Form Integration\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-6\">\n <form\n class=\"max-w-sm space-y-4\"\n onsubmit={(e) => {\n e.preventDefault()\n alert(`volume = ${formValue}`)\n }}\n >\n <div class=\"space-y-2\">\n <label class=\"block text-sm font-medium text-on-surface\" for=\"vol-out\">\n Volume: {formValue}\n </label>\n <Slider name=\"volume\" bind:value={formValue} color=\"success\" />\n </div>\n <button\n type=\"submit\"\n class=\"rounded-md bg-primary px-4 py-2 text-sm font-medium text-on-primary\"\n >\n Submit\n </button>\n </form>\n </div>\n </section>\n\n <!-- FormField Integration -->\n <section class=\"space-y-3\">\n <h2 id=\"FormField-Integration\" class=\"text-lg font-semibold\">\n<a href=\"#FormField-Integration\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n FormField Integration\n </a>\n</h2>\n <div class=\"max-w-sm space-y-4 rounded-lg bg-surface-container-high p-6\">\n <FormField\n label=\"Volume\"\n description=\"Adjust the playback volume.\"\n hint=\"{formFieldValue}%\"\n >\n <Slider bind:value={formFieldValue} class=\"mt-1\" />\n </FormField>\n\n <FormField\n label=\"Brightness\"\n required\n help=\"Recommended between 20–80 for eye comfort.\"\n >\n <Slider bind:value={formFieldValue} color=\"warning\" class=\"mt-1\" />\n </FormField>\n\n <FormField\n label=\"Quality\"\n error={formFieldErrorValue < 20 ? 'Value must be at least 20.' : undefined}\n >\n <Slider bind:value={formFieldErrorValue} class=\"mt-1\" />\n </FormField>\n </div>\n </section>\n\n <!-- Custom ui -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-UI-Slots\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-UI-Slots\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom UI Slots\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-6\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">\n Thick track + large rounded thumb\n </p>\n <Slider\n value={55}\n color=\"tertiary\"\n ui={{\n track: 'h-4 rounded-md',\n range: 'rounded-md',\n thumb: 'size-6 rounded-md shadow-md'\n }}\n />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Custom tooltip style</p>\n <Slider\n value={70}\n tooltip\n color=\"info\"\n ui={{ tooltip: 'bg-info text-on-info rounded-full px-2' }}\n class=\"pt-6\"\n />\n </div>\n </div>\n </section>\n</div>\n",
151
+ "sortable-list": "<script lang=\"ts\">\n import { Button, Card, Link, SortableList } from '$lib/index.js'\n\n type Item = { id: string; label: string }\n\n let items = $state<Item[]>([\n { id: '1', label: 'Inbox zero' },\n { id: '2', label: 'Prepare release notes' },\n { id: '3', label: 'Record demo video' },\n { id: '4', label: 'Update changelog' }\n ])\n\n let horizontalItems = $state<Item[]>([\n { id: 'h1', label: 'Draft' },\n { id: 'h2', label: 'Review' },\n { id: 'h3', label: 'Publish' }\n ])\n\n let wholeRowItems = $state<Item[]>([\n { id: 'w1', label: 'Drag anywhere on this row' },\n { id: 'w2', label: 'No grip handle' }\n ])\n\n let sortDisabled = $state(false)\n\n const propsReference = [\n { name: 'items', type: 'T[]', description: 'Bindable array — updated automatically on reorder.' },\n { name: 'getKey', type: '(item: T) => string | number', description: 'Stable key per item (used in #each).' },\n { name: 'onReorder', type: '(items: T[]) => void', description: 'Optional callback after bind:items updates.' },\n { name: 'handle', type: 'boolean', description: 'Show grip handle. Default true. Set false to drag the whole row.' },\n { name: 'axis', type: \"'vertical' | 'horizontal' | 'grid'\", description: 'List layout axis. Default vertical. Use grid with CSS grid classes.' },\n { name: 'disabled', type: 'boolean', description: 'Disable sorting.' },\n { name: 'class', type: 'ClassNameValue', description: 'Classes on the list container.' },\n { name: 'itemClass', type: 'ClassNameValue', description: 'Classes on each sortable row.' }\n ] as const\n\n const snippetReference = [\n { name: 'item', type: 'T', description: 'The row item.' },\n { name: 'index', type: 'number', description: 'Zero-based position in the array.' },\n { name: 'dragging', type: 'boolean', description: 'True while this row is being dragged.' }\n ] as const\n\n function resetItems() {\n items = [\n { id: '1', label: 'Inbox zero' },\n { id: '2', label: 'Prepare release notes' },\n { id: '3', label: 'Record demo video' },\n { id: '4', label: 'Update changelog' }\n ]\n }\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">SortableList</h1>\n <p class=\"text-on-surface-variant\">\n Opinionated sortable list built on\n <Link href=\"/docs/hooks/use-sortable\" class=\"text-primary underline\">useSortable</Link>.\n Uses <code class=\"rounded bg-surface-container-high px-1\">bind:items</code>\n to keep array state in sync. See the\n <Link href=\"/docs/drag-and-drop\" class=\"text-primary underline\">Drag & Drop playground</Link>\n for more examples. For kanban or cross-column moves, use\n <Link href=\"/docs/hooks/use-drag-drop\" class=\"text-primary underline\">useDragDrop</Link>.\n </p>\n </div>\n\n <section class=\"space-y-3\">\n <h2 id=\"When-to-use-what\" class=\"text-lg font-semibold\">\n <a href=\"#When-to-use-what\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n When to use what\n </a>\n </h2>\n <Card class=\"border border-outline-variant/70 p-4 text-sm text-on-surface-variant\">\n <p>\n Use <strong class=\"text-on-surface\">SortableList</strong> for standard vertical/horizontal task lists with a grip handle.\n Use <Link href=\"/docs/hooks/use-sortable\" class=\"text-primary underline\">useSortable</Link> directly when you need full control over markup.\n See the <Link href=\"/docs/hooks/use-sortable#When-to-use-what\" class=\"text-primary underline\">drag & drop overview</Link>\n for FileUpload and Editor.\n </p>\n </Card>\n </section>\n\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n <a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n </h2>\n <Card class=\"border border-outline-variant/70 p-4\">\n <SortableList bind:items disabled={sortDisabled} getKey={(item) => item.id}>\n {#snippet children({ item, index, dragging })}\n <div class=\"flex w-full items-center justify-between gap-3\">\n <span class=\"text-sm font-medium\" class:opacity-60={dragging}>{item.label}</span>\n <span class=\"text-xs text-on-surface-variant\">#{index + 1}</span>\n </div>\n {/snippet}\n </SortableList>\n </Card>\n <div class=\"flex flex-wrap gap-2\">\n <Button variant=\"outline\" size=\"sm\" label=\"Reset\" onclick={resetItems} />\n <Button\n variant={sortDisabled ? 'solid' : 'outline'}\n size=\"sm\"\n label={sortDisabled ? 'Sorting disabled' : 'Disable sorting'}\n onclick={() => (sortDisabled = !sortDisabled)}\n />\n </div>\n </section>\n\n <section class=\"space-y-3\">\n <h2 id=\"Horizontal\" class=\"text-lg font-semibold\">\n <a href=\"#Horizontal\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Horizontal\n </a>\n </h2>\n <Card class=\"border border-outline-variant/70 p-4\">\n <SortableList bind:items={horizontalItems} axis=\"horizontal\" getKey={(item) => item.id}>\n {#snippet children({ item })}\n <span class=\"text-sm font-medium\">{item.label}</span>\n {/snippet}\n </SortableList>\n </Card>\n </section>\n\n <section class=\"space-y-3\">\n <h2 id=\"Whole-row-drag\" class=\"text-lg font-semibold\">\n <a href=\"#Whole-row-drag\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Whole row drag\n </a>\n </h2>\n <p class=\"text-sm text-on-surface-variant\">Set <code class=\"rounded bg-surface-container-high px-1\">handle={'{false}'}</code> to drag the entire row.</p>\n <Card class=\"border border-outline-variant/70 p-4\">\n <SortableList bind:items={wholeRowItems} handle={false} getKey={(item) => item.id}>\n {#snippet children({ item })}\n <span class=\"cursor-grab text-sm font-medium active:cursor-grabbing\">{item.label}</span>\n {/snippet}\n </SortableList>\n </Card>\n </section>\n\n <section class=\"space-y-3\">\n <h2 id=\"API-Reference\" class=\"text-lg font-semibold\">\n <a href=\"#API-Reference\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n API Reference\n </a>\n </h2>\n <div class=\"grid gap-4 lg:grid-cols-2\">\n <Card class=\"border border-outline-variant/70\">\n <div class=\"space-y-4\">\n <h3 class=\"text-base font-semibold\">Props</h3>\n <div class=\"space-y-3\">\n {#each propsReference as item (item.name)}\n <div class=\"border-b border-outline-variant/60 pb-3 last:border-b-0 last:pb-0\">\n <p class=\"font-mono text-xs text-primary\">{item.name}</p>\n <p class=\"mt-1 text-sm font-medium\">{item.type}</p>\n <p class=\"mt-1 text-sm text-on-surface-variant\">{item.description}</p>\n </div>\n {/each}\n </div>\n </div>\n </Card>\n <Card class=\"border border-outline-variant/70\">\n <div class=\"space-y-4\">\n <h3 class=\"text-base font-semibold\">children snippet</h3>\n <div class=\"space-y-3\">\n {#each snippetReference as item (item.name)}\n <div class=\"border-b border-outline-variant/60 pb-3 last:border-b-0 last:pb-0\">\n <p class=\"font-mono text-xs text-primary\">{item.name}</p>\n <p class=\"mt-1 text-sm font-medium\">{item.type}</p>\n <p class=\"mt-1 text-sm text-on-surface-variant\">{item.description}</p>\n </div>\n {/each}\n </div>\n </div>\n </Card>\n </div>\n </section>\n</div>\n",
147
152
  "switch": "<script lang=\"ts\">\n import { Switch, FormField, Separator } from '$lib/index.js'\n\n const colors = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'success',\n 'warning',\n 'error',\n 'info',\n 'surface'\n ] as const\n const sizes = ['xs', 'sm', 'md', 'lg', 'xl'] as const\n\n let bindChecked = $state(false)\n</script>\n\n<div class=\"space-y-8\">\n <h1 class=\"text-2xl font-bold text-on-surface\">Switch</h1>\n\n <!-- Basic Usage -->\n <section class=\"space-y-4\">\n <h2 id=\"Basic-Usage\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Basic-Usage\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic Usage\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">A toggle switch component for binary states.</p>\n <Switch />\n </section>\n\n <!-- Two-way Binding -->\n <section class=\"space-y-4\">\n <h2 id=\"Two-way-Binding\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Two-way-Binding\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Two-way Binding\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >bind:checked</code\n > for reactive two-way data binding.\n </p>\n <div class=\"flex flex-wrap items-center gap-6\">\n <Switch bind:checked={bindChecked} label=\"Toggle me\" />\n <p class=\"text-sm text-on-surface-variant\">\n Checked: <span class=\"font-mono text-on-surface\">{bindChecked}</span>\n </p>\n </div>\n </section>\n\n <!-- Label & Description -->\n <section class=\"space-y-4\">\n <h2 id=\"Label-amp-Description\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Label-amp-Description\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Label &amp; Description\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">label</code\n >\n and\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >description</code\n > to add text next to the switch.\n </p>\n <div class=\"flex flex-col gap-4\">\n <Switch label=\"Notifications\" />\n <Switch label=\"Dark mode\" description=\"Enable dark mode for the application.\" />\n <Switch\n label=\"Marketing emails\"\n description=\"Receive emails about new products and features.\"\n />\n </div>\n </section>\n\n <!-- Colors -->\n <section class=\"space-y-4\">\n <h2 id=\"Colors\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Colors\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Colors\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">color</code\n > to control the checked background color.\n </p>\n <div class=\"flex flex-wrap gap-6\">\n {#each colors as color (color)}\n <Switch {color} checked={true} label={color} />\n {/each}\n </div>\n </section>\n\n <!-- Sizes -->\n <section class=\"space-y-4\">\n <h2 id=\"Sizes\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Sizes\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Sizes\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">size</code> to\n control the dimensions.\n </p>\n <div class=\"flex flex-wrap items-center gap-6\">\n {#each sizes as size (size)}\n <Switch {size} checked={true} label={size} />\n {/each}\n </div>\n </section>\n\n <!-- Icons -->\n <section class=\"space-y-4\">\n <h2 id=\"Icons\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Icons\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Icons\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >checkedIcon</code\n >\n and\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >uncheckedIcon</code\n > to display icons inside the thumb.\n </p>\n <div class=\"flex flex-col gap-4\">\n <Switch\n checkedIcon=\"lucide:check\"\n uncheckedIcon=\"lucide:x\"\n label=\"With check / x icons\"\n />\n <Switch\n checkedIcon=\"lucide:sun\"\n uncheckedIcon=\"lucide:moon\"\n label=\"Theme toggle\"\n description=\"Switch between light and dark mode.\"\n />\n <Switch checkedIcon=\"lucide:volume-2\" uncheckedIcon=\"lucide:volume-x\" label=\"Sound\" />\n <Switch\n checkedIcon=\"lucide:wifi\"\n uncheckedIcon=\"lucide:wifi-off\"\n label=\"Wi-Fi\"\n color=\"success\"\n />\n </div>\n </section>\n\n <!-- Loading -->\n <section class=\"space-y-4\">\n <h2 id=\"Loading\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Loading\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Loading\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >loading</code\n > to show a spinner inside the thumb and disable interaction.\n </p>\n <div class=\"flex flex-col gap-4\">\n <Switch loading label=\"Syncing...\" />\n <Switch loading checked={true} label=\"Saving preferences...\" color=\"success\" />\n </div>\n </section>\n\n <!-- Disabled -->\n <section class=\"space-y-4\">\n <h2 id=\"Disabled\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Disabled\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Disabled\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >disabled</code\n > to prevent interaction.\n </p>\n <div class=\"flex flex-wrap gap-6\">\n <Switch disabled label=\"Disabled (off)\" />\n <Switch disabled checked={true} label=\"Disabled (on)\" />\n </div>\n </section>\n\n <!-- Required -->\n <section class=\"space-y-4\">\n <h2 id=\"Required\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Required\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Required\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >required</code\n > to show an asterisk next to the label.\n </p>\n <Switch required label=\"Accept terms and conditions\" />\n </section>\n\n <!-- Custom Slots -->\n <section class=\"space-y-4\">\n <h2 id=\"Custom-Slots\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Custom-Slots\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Slots\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >labelSlot</code\n >\n and\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >descriptionSlot</code\n > for fully custom label content.\n </p>\n <div class=\"flex flex-col gap-4\">\n <Switch checked={true} color=\"success\">\n {#snippet labelSlot()}\n <span class=\"flex items-center gap-1.5 text-sm font-medium text-on-surface\">\n <span class=\"inline-block size-2 rounded-full bg-success\"></span>\n System online\n </span>\n {/snippet}\n {#snippet descriptionSlot()}\n <span class=\"text-xs text-on-surface-variant\">\n All services are running normally. Last checked <strong>just now</strong>.\n </span>\n {/snippet}\n </Switch>\n\n <Switch>\n {#snippet labelSlot()}\n <span class=\"flex items-center gap-2 text-sm font-medium text-on-surface\">\n Beta features\n <span\n class=\"rounded bg-tertiary-container px-1.5 py-0.5 text-[10px] font-bold text-on-tertiary-container\"\n >BETA</span\n >\n </span>\n {/snippet}\n </Switch>\n </div>\n </section>\n\n <!-- FormField Integration -->\n <section class=\"space-y-4\">\n <h2 id=\"FormField-Integration\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#FormField-Integration\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n FormField Integration\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n When used inside a <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">FormField</code\n >, the Switch automatically inherits size and error state.\n </p>\n <div class=\"max-w-sm space-y-4\">\n <FormField label=\"Notifications\" description=\"Choose how you want to be notified.\">\n <Switch label=\"Push notifications\" />\n </FormField>\n <FormField label=\"Agreement\" error=\"You must accept the terms.\">\n <Switch label=\"I accept the terms of service\" />\n </FormField>\n </div>\n </section>\n\n <Separator />\n\n <!-- Real World Examples -->\n <section class=\"space-y-4\">\n <h2 id=\"Real-World-Examples\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Real-World-Examples\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World Examples\n </a>\n</h2>\n\n <div class=\"space-y-6\">\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Notification preferences</p>\n <div\n class=\"max-w-sm space-y-4 rounded-lg border border-outline-variant bg-surface-container-low p-4\"\n >\n <Switch\n checked={true}\n label=\"Push notifications\"\n description=\"Receive alerts on your device.\"\n color=\"primary\"\n />\n <Switch\n checked={true}\n label=\"Email digest\"\n description=\"Daily summary of activity.\"\n color=\"primary\"\n />\n <Switch\n label=\"SMS alerts\"\n description=\"Text messages for critical updates.\"\n color=\"primary\"\n />\n <Switch\n loading\n label=\"Marketing emails\"\n description=\"Updating preference...\"\n color=\"primary\"\n />\n </div>\n </div>\n\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Privacy settings</p>\n <div\n class=\"max-w-sm space-y-4 rounded-lg border border-outline-variant bg-surface-container-low p-4\"\n >\n <Switch\n checked={true}\n checkedIcon=\"lucide:eye\"\n uncheckedIcon=\"lucide:eye-off\"\n label=\"Profile visibility\"\n description=\"Make your profile public.\"\n color=\"secondary\"\n />\n <Switch\n checkedIcon=\"lucide:share-2\"\n uncheckedIcon=\"lucide:share-2\"\n label=\"Data sharing\"\n description=\"Share usage data to improve the product.\"\n color=\"secondary\"\n />\n <Switch\n checked={true}\n checkedIcon=\"lucide:shield-check\"\n uncheckedIcon=\"lucide:shield-off\"\n label=\"Two-factor auth\"\n description=\"Add an extra layer of security.\"\n color=\"success\"\n disabled\n />\n </div>\n </div>\n </div>\n </section>\n</div>\n",
148
153
  "textarea": "<script lang=\"ts\">\n import { Textarea, FormField, FieldGroup, Separator } from '$lib/index.js'\n\n const variants = ['outline', 'soft', 'subtle', 'ghost', 'none'] as const\n const colors = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'success',\n 'warning',\n 'error',\n 'info',\n 'surface'\n ] as const\n const sizes = ['xs', 'sm', 'md', 'lg', 'xl'] as const\n\n let bindValue = $state('')\n</script>\n\n<div class=\"space-y-8\">\n <h1 class=\"text-2xl font-bold text-on-surface\">Textarea</h1>\n\n <!-- Basic Usage -->\n <section class=\"space-y-4\">\n <h2 id=\"Basic-Usage\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Basic-Usage\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic Usage\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n A multi-line text input component with variants, colors, icons, autoresize, and\n integration with FormField and FieldGroup.\n </p>\n <div class=\"max-w-sm\">\n <Textarea placeholder=\"Enter text...\" />\n </div>\n </section>\n\n <!-- Two-way Binding -->\n <section class=\"space-y-4\">\n <h2 id=\"Two-way-Binding\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Two-way-Binding\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Two-way Binding\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >bind:value</code\n > for reactive two-way data binding.\n </p>\n <div class=\"max-w-sm space-y-3\">\n <Textarea\n bind:value={bindValue}\n leadingIcon=\"lucide:pencil\"\n placeholder=\"Type something...\"\n />\n <p class=\"text-sm text-on-surface-variant\">\n Value: <span class=\"font-mono text-on-surface\">{bindValue || '(empty)'}</span>\n </p>\n <p class=\"text-sm text-on-surface-variant\">\n Length: <span class=\"font-mono text-on-surface\">{bindValue.length}</span>\n </p>\n </div>\n </section>\n\n <!-- Variants × Colors -->\n <section class=\"space-y-4\">\n <h2 id=\"Variants-times-Colors\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Variants-times-Colors\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Variants &times; Colors\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >variant</code\n >\n and\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">color</code> to control\n appearance.\n </p>\n <div class=\"overflow-x-auto\">\n <table class=\"w-full border-collapse\">\n <thead>\n <tr>\n <th class=\"px-3 py-2 text-left text-xs font-medium text-on-surface-variant\"\n ></th>\n {#each colors as color (color)}\n <th\n class=\"px-3 py-2 text-left text-xs font-medium text-on-surface-variant capitalize\"\n >{color}</th\n >\n {/each}\n </tr>\n </thead>\n <tbody>\n {#each variants as variant (variant)}\n <tr>\n <td\n class=\"px-3 py-2 text-xs font-medium text-on-surface-variant capitalize\"\n >{variant}</td\n >\n {#each colors as color (color)}\n <td class=\"px-3 py-2\">\n <Textarea {variant} {color} placeholder={color} rows={2} />\n </td>\n {/each}\n </tr>\n {/each}\n </tbody>\n </table>\n </div>\n </section>\n\n <!-- Size -->\n <section class=\"space-y-4\">\n <h2 id=\"Size\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Size\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Size\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">size</code> to\n control the dimensions and text size.\n </p>\n <div class=\"flex flex-wrap items-end gap-4\">\n {#each sizes as size (size)}\n <div class=\"w-48\">\n <Textarea {size} placeholder=\"{size} size\" rows={2} />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Rows -->\n <section class=\"space-y-4\">\n <h2 id=\"Rows\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Rows\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Rows\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">rows</code> to\n set the number of visible text lines.\n </p>\n <div class=\"flex flex-wrap gap-4\">\n <div class=\"w-64\">\n <Textarea rows={2} placeholder=\"2 rows\" />\n </div>\n <div class=\"w-64\">\n <Textarea rows={4} placeholder=\"4 rows\" />\n </div>\n <div class=\"w-64\">\n <Textarea rows={6} placeholder=\"6 rows\" />\n </div>\n </div>\n </section>\n\n <!-- Autoresize -->\n <section class=\"space-y-4\">\n <h2 id=\"Autoresize\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Autoresize\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Autoresize\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >autoresize</code\n >\n to automatically adjust height based on content. Combine with\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">maxrows</code> to\n limit growth.\n </p>\n <div class=\"space-y-4\">\n <div class=\"max-w-sm\">\n <p class=\"mb-2 text-xs text-on-surface-variant\">Unlimited autoresize</p>\n <Textarea autoresize placeholder=\"Type to grow...\" rows={1} />\n </div>\n <div class=\"max-w-sm\">\n <p class=\"mb-2 text-xs text-on-surface-variant\">Autoresize with maxrows=5</p>\n <Textarea autoresize maxrows={5} placeholder=\"Grows up to 5 rows...\" rows={1} />\n </div>\n </div>\n </section>\n\n <!-- Icons -->\n <section class=\"space-y-4\">\n <h2 id=\"Icons\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Icons\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Icons\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >leadingIcon</code\n >\n and\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >trailingIcon</code\n > to add icons.\n </p>\n <div class=\"flex flex-wrap gap-4\">\n <div class=\"w-64\">\n <Textarea leadingIcon=\"lucide:message-square\" placeholder=\"Comment...\" rows={2} />\n </div>\n <div class=\"w-64\">\n <Textarea trailingIcon=\"lucide:send\" placeholder=\"Message...\" rows={2} />\n </div>\n <div class=\"w-64\">\n <Textarea\n leadingIcon=\"lucide:file-text\"\n trailingIcon=\"lucide:check\"\n placeholder=\"Description...\"\n rows={2}\n />\n </div>\n </div>\n </section>\n\n <!-- Icon (with trailing) -->\n <section class=\"space-y-4\">\n <h2 id=\"Icon-with-trailing\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Icon-with-trailing\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Icon (with trailing)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">icon</code>\n with\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">trailing</code> to\n position it.\n </p>\n <div class=\"flex flex-wrap gap-4\">\n <div class=\"w-64\">\n <Textarea icon=\"lucide:pencil\" placeholder=\"Leading icon\" rows={2} />\n </div>\n <div class=\"w-64\">\n <Textarea icon=\"lucide:pencil\" trailing placeholder=\"Trailing icon\" rows={2} />\n </div>\n </div>\n </section>\n\n <!-- Loading -->\n <section class=\"space-y-4\">\n <h2 id=\"Loading\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Loading\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Loading\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >loading</code\n > to show a loading spinner.\n </p>\n <div class=\"flex flex-wrap gap-4\">\n <div class=\"w-64\">\n <Textarea loading placeholder=\"Loading (leading)...\" rows={2} />\n </div>\n <div class=\"w-64\">\n <Textarea loading trailing placeholder=\"Loading (trailing)...\" rows={2} />\n </div>\n </div>\n </section>\n\n <!-- Disabled -->\n <section class=\"space-y-4\">\n <h2 id=\"Disabled\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Disabled\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Disabled\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >disabled</code\n > to prevent interaction.\n </p>\n <div class=\"flex flex-wrap gap-4\">\n <div class=\"w-64\">\n <Textarea disabled placeholder=\"Disabled\" rows={2} />\n </div>\n <div class=\"w-64\">\n <Textarea disabled value=\"Disabled with value\" rows={2} />\n </div>\n </div>\n </section>\n\n <!-- Highlight -->\n <section class=\"space-y-4\">\n <h2 id=\"Highlight\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Highlight\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Highlight\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >highlight</code\n > to emphasize the ring color like a focus state.\n </p>\n <div class=\"flex flex-wrap gap-4\">\n {#each colors as color (color)}\n <div class=\"w-48\">\n <Textarea highlight {color} placeholder={color} rows={2} />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- FormField Integration -->\n <section class=\"space-y-4\">\n <h2 id=\"FormField-Integration\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#FormField-Integration\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n FormField Integration\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n When used inside a <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">FormField</code\n >, the Textarea automatically inherits size, error state, and accessibility attributes.\n </p>\n <div class=\"max-w-sm space-y-4\">\n <FormField\n label=\"Description\"\n description=\"Provide a detailed description of the issue.\"\n required\n >\n <Textarea leadingIcon=\"lucide:file-text\" placeholder=\"Describe the issue...\" />\n </FormField>\n\n <FormField\n label=\"Notes\"\n help=\"Optional additional notes.\"\n error=\"Notes cannot be empty.\"\n >\n <Textarea placeholder=\"Enter notes...\" />\n </FormField>\n </div>\n </section>\n\n <!-- FieldGroup Integration -->\n <section class=\"space-y-4\">\n <h2 id=\"FieldGroup-Integration\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#FieldGroup-Integration\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n FieldGroup Integration\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n When used inside a <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">FieldGroup</code\n >, textareas are visually connected.\n </p>\n <div class=\"max-w-sm\">\n <FieldGroup orientation=\"vertical\">\n <Textarea placeholder=\"Title\" rows={1} />\n <Textarea placeholder=\"Description\" rows={3} />\n <Textarea placeholder=\"Additional notes\" rows={2} />\n </FieldGroup>\n </div>\n </section>\n\n <Separator />\n\n <!-- Real World Examples -->\n <section class=\"space-y-4\">\n <h2 id=\"Real-World-Examples\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Real-World-Examples\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World Examples\n </a>\n</h2>\n\n <div class=\"space-y-6\">\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Feedback form</p>\n <div\n class=\"max-w-sm space-y-4 rounded-lg border border-outline-variant bg-surface-container-low p-4\"\n >\n <FormField label=\"Subject\" required>\n <Textarea placeholder=\"Brief summary...\" rows={1} autoresize />\n </FormField>\n\n <FormField label=\"Details\" required help=\"Be as specific as possible.\">\n <Textarea\n placeholder=\"Describe your feedback in detail...\"\n autoresize\n maxrows={8}\n />\n </FormField>\n </div>\n </div>\n\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Chat message input</p>\n <div class=\"max-w-md\">\n <Textarea\n leadingIcon=\"lucide:message-square\"\n trailingIcon=\"lucide:send\"\n placeholder=\"Type a message...\"\n autoresize\n maxrows={5}\n rows={1}\n />\n </div>\n </div>\n\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Code snippet input</p>\n <div class=\"max-w-md\">\n <Textarea\n variant=\"soft\"\n color=\"surface\"\n placeholder=\"Paste your code here...\"\n rows={6}\n ui={{ base: 'font-mono text-xs' }}\n />\n </div>\n </div>\n </div>\n </section>\n</div>\n",
149
154
  "password-input": "<script lang=\"ts\">\n import { PasswordInput } from '$lib/index.js'\n \n let pwd1 = $state('')\n let pwd2 = $state('')\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">PasswordInput</h1>\n <p class=\"text-on-surface-variant\">\n A secure input for passwords with a built-in visibility toggle and strength meter.\n </p>\n </div>\n\n <!-- Usage -->\n <section class=\"space-y-3\">\n <h2 id=\"Usage\" class=\"text-lg font-semibold\">\n<a href=\"#Usage\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Usage\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Behaves like a regular <code class=\"rounded bg-surface-container-highest px-1\">Input</code> but adds an eye icon to toggle visibility.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4 max-w-sm\">\n <PasswordInput bind:value={pwd1} placeholder=\"Enter your password\" />\n </div>\n </section>\n\n <!-- Strength Meter -->\n <section class=\"space-y-3\">\n <h2 id=\"Strength-Meter\" class=\"text-lg font-semibold\">\n<a href=\"#Strength-Meter\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Strength Meter\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Set <code class=\"rounded bg-surface-container-highest px-1\">showStrength={`{true}`}</code> to display a color-coded strength indicator below the input.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4 max-w-sm\">\n <PasswordInput bind:value={pwd2} showStrength={true} placeholder=\"Create a strong password\" />\n </div>\n </section>\n</div>\n",
150
- "file-upload": "<script lang=\"ts\">\n import { FileUpload, Button, Form, FormField } from '$lib/index.js'\n import type { FileUploadRejection, FormApi } from '$lib/index.js'\n import { z } from 'zod'\n\n const colors = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'success',\n 'warning',\n 'error',\n 'info',\n 'surface'\n ] as const\n\n const sizes = ['xs', 'sm', 'md', 'lg', 'xl'] as const\n\n let basicFiles = $state<File[]>([])\n let multipleFiles = $state<File[]>([])\n let gridSingleFile = $state<File[]>([])\n let gridMultipleFiles = $state<File[]>([])\n let buttonFiles = $state<File[]>([])\n let actionsFiles = $state<File[]>([])\n let highlightFiles = $state<File[]>([])\n let noDropzoneFiles = $state<File[]>([])\n let noPreviewFiles = $state<File[]>([])\n let imageFiles = $state<File[]>([])\n\n let maxSizeFiles = $state<File[]>([])\n let maxFilesFiles = $state<File[]>([])\n let validationFiles = $state<File[]>([])\n let rejections = $state<FileUploadRejection[]>([])\n let combinedRejections = $state<FileUploadRejection[]>([])\n\n function reasonLabel(reason: FileUploadRejection['reason']): string {\n if (reason === 'maxSize') return 'too large'\n if (reason === 'maxFiles') return 'too many'\n return 'wrong type'\n }\n\n const fileUploadSchema = z.object({\n avatar: z.array(z.instanceof(File)).min(1, 'Avatar is required'),\n gallery: z.array(z.instanceof(File)).min(2, 'Pick at least 2 images')\n })\n\n let fileUploadFormState = $state<{ avatar: File[]; gallery: File[] }>({\n avatar: [],\n gallery: []\n })\n let fileUploadFormApi = $state<FormApi<unknown>>()\n let fileUploadSubmitted = $state<string | null>(null)\n\n function handleFileUploadSubmit(event: { data: unknown }) {\n const data = event.data as { avatar: File[]; gallery: File[] }\n fileUploadSubmitted = JSON.stringify(\n {\n avatar: data.avatar.map((f) => f.name),\n gallery: data.gallery.map((f) => f.name)\n },\n null,\n 2\n )\n }\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">FileUpload</h1>\n <p class=\"text-on-surface-variant\">\n Upload files via drag-and-drop or file dialog with preview and removal.\n </p>\n </div>\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <FileUpload bind:value={basicFiles} />\n <p class=\"mt-3 text-sm text-on-surface-variant\">\n Selected: {basicFiles.length ? basicFiles.map((f) => f.name).join(', ') : 'none'}\n </p>\n </div>\n </section>\n\n <!-- Multiple Files -->\n <section class=\"space-y-3\">\n <h2 id=\"Multiple-Files\" class=\"text-lg font-semibold\">\n<a href=\"#Multiple-Files\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Multiple Files\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <FileUpload\n bind:value={multipleFiles}\n multiple\n label=\"Drop multiple files here\"\n description=\"Upload as many files as you need\"\n />\n </div>\n </section>\n\n <!-- Variants -->\n <section class=\"space-y-3\">\n <h2 id=\"Variants\" class=\"text-lg font-semibold\">\n<a href=\"#Variants\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Variants\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-4 rounded-lg bg-surface-container-high p-4 sm:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Area (default)</p>\n <FileUpload label=\"Drop files here\" description=\"Supports drag & drop\" />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Button</p>\n <FileUpload variant=\"button\" label=\"Upload file\" bind:value={buttonFiles} />\n {#if buttonFiles.length > 0}\n <p class=\"text-sm text-on-surface-variant\">\n {buttonFiles.map((f) => f.name).join(', ')}\n </p>\n {/if}\n </div>\n </div>\n </section>\n\n <!-- Layout -->\n <section class=\"space-y-3\">\n <h2 id=\"Layout\" class=\"text-lg font-semibold\">\n<a href=\"#Layout\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Layout\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-4 rounded-lg bg-surface-container-high p-4 sm:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">List (default)</p>\n <FileUpload\n bind:value={multipleFiles}\n multiple\n layout=\"list\"\n label=\"Upload files\"\n description=\"Files shown as list\"\n />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Grid · Multiple</p>\n <FileUpload\n bind:value={gridMultipleFiles}\n multiple\n layout=\"grid\"\n label=\"Upload images\"\n description=\"Files shown as grid thumbnails\"\n accept=\"image/*\"\n />\n </div>\n </div>\n </section>\n\n <!-- Grid Single File -->\n <section class=\"space-y-3\">\n <h2 id=\"Grid--Single-File-Preview\" class=\"text-lg font-semibold\">\n<a href=\"#Grid--Single-File-Preview\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Grid · Single File Preview\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <div class=\"max-w-xs\">\n <FileUpload\n bind:value={gridSingleFile}\n layout=\"grid\"\n label=\"Upload image\"\n description=\"Preview fills the area\"\n accept=\"image/*\"\n ui={{ base: 'min-h-48' }}\n />\n </div>\n </div>\n </section>\n\n <!-- Dropzone vs No Dropzone -->\n <section class=\"space-y-3\">\n <h2 id=\"Dropzone\" class=\"text-lg font-semibold\">\n<a href=\"#Dropzone\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Dropzone\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-4 rounded-lg bg-surface-container-high p-4 sm:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">With dropzone (default)</p>\n <FileUpload\n label=\"Drag & drop or click\"\n description=\"Drag files anywhere on this area\"\n />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">No dropzone · click only</p>\n <FileUpload\n bind:value={noDropzoneFiles}\n dropzone={false}\n label=\"Click to upload\"\n description=\"No drag-and-drop\"\n />\n </div>\n </div>\n </section>\n\n <!-- Custom Actions Slot -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-Actions\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-Actions\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Actions\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <FileUpload\n bind:value={actionsFiles}\n multiple\n interactive={false}\n label=\"Drag files here\"\n description=\"or click the button below\"\n >\n {#snippet actionsSlot({ open })}\n <Button\n size=\"sm\"\n variant=\"outline\"\n color=\"primary\"\n leadingIcon=\"lucide:folder-open\"\n label=\"Browse files\"\n onclick={open}\n />\n {/snippet}\n </FileUpload>\n </div>\n </section>\n\n <!-- Highlight -->\n <section class=\"space-y-3\">\n <h2 id=\"Highlight\" class=\"text-lg font-semibold\">\n<a href=\"#Highlight\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Highlight\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-4 rounded-lg bg-surface-container-high p-4 sm:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Normal</p>\n <FileUpload bind:value={basicFiles} label=\"No highlight\" />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Highlighted</p>\n <FileUpload bind:value={highlightFiles} highlight label=\"Highlighted border\" />\n </div>\n </div>\n </section>\n\n <!-- Colors -->\n <section class=\"space-y-3\">\n <h2 id=\"Colors\" class=\"text-lg font-semibold\">\n<a href=\"#Colors\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Colors\n </a>\n</h2>\n <div class=\"space-y-3 rounded-lg bg-surface-container-high p-4\">\n {#each colors as color (color)}\n <FileUpload\n {color}\n highlight\n label={color}\n description=\"Drop files or click to browse\"\n ui={{ wrapper: 'py-2' }}\n />\n {/each}\n </div>\n </section>\n\n <!-- Sizes -->\n <section class=\"space-y-3\">\n <h2 id=\"Sizes\" class=\"text-lg font-semibold\">\n<a href=\"#Sizes\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Sizes\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-4\">\n {#each sizes as size (size)}\n <div class=\"flex items-start gap-4\">\n <span class=\"w-6 pt-3 text-xs text-on-surface-variant\">{size}</span>\n <FileUpload\n {size}\n label=\"Upload file\"\n description=\"Drag & drop or click\"\n class=\"flex-1\"\n />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Disabled & Loading -->\n <section class=\"space-y-3\">\n <h2 id=\"States\" class=\"text-lg font-semibold\">\n<a href=\"#States\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n States\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-4 rounded-lg bg-surface-container-high p-4 sm:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Disabled</p>\n <FileUpload disabled label=\"Disabled upload\" description=\"Cannot interact\" />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Loading</p>\n <FileUpload loading label=\"Processing files…\" description=\"Please wait\" />\n </div>\n </div>\n </section>\n\n <!-- No Preview -->\n <section class=\"space-y-3\">\n <h2 id=\"Preview-Disabled\" class=\"text-lg font-semibold\">\n<a href=\"#Preview-Disabled\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Preview Disabled\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <FileUpload\n bind:value={noPreviewFiles}\n multiple\n preview={false}\n label=\"Upload without preview\"\n description=\"Files are selected but not listed\"\n />\n <p class=\"mt-3 text-sm text-on-surface-variant\">\n {noPreviewFiles.length} file(s) selected\n </p>\n </div>\n </section>\n\n <!-- Accept Filter -->\n <section class=\"space-y-3\">\n <h2 id=\"Accept-Filter\" class=\"text-lg font-semibold\">\n<a href=\"#Accept-Filter\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Accept Filter\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-4 rounded-lg bg-surface-container-high p-4 sm:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Images only</p>\n <FileUpload\n bind:value={imageFiles}\n multiple\n accept=\"image/*\"\n color=\"tertiary\"\n icon=\"lucide:image\"\n label=\"Drop images here\"\n description=\"Only image files accepted\"\n />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">PDF only</p>\n <FileUpload\n accept=\".pdf\"\n color=\"error\"\n icon=\"lucide:file-text\"\n label=\"Drop PDF here\"\n description=\"Only .pdf files accepted\"\n />\n </div>\n </div>\n </section>\n\n <!-- Image Preview -->\n <section class=\"space-y-3\">\n <h2 id=\"Image-Preview\" class=\"text-lg font-semibold\">\n<a href=\"#Image-Preview\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Image Preview\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-4 rounded-lg bg-surface-container-high p-4 sm:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Preview enabled (default)</p>\n <FileUpload\n bind:value={imageFiles}\n multiple\n accept=\"image/*\"\n icon=\"lucide:image\"\n label=\"Drop images here\"\n description=\"Click the zoom icon to preview\"\n />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Preview disabled</p>\n <FileUpload\n multiple\n accept=\"image/*\"\n icon=\"lucide:image\"\n label=\"Drop images here\"\n description=\"No preview button shown\"\n imagePreview={false}\n />\n </div>\n </div>\n </section>\n\n <!-- Custom Slots -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-Slots\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-Slots\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Slots\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <FileUpload multiple bind:value={basicFiles} color=\"success\">\n {#snippet leadingSlot()}\n <div\n class=\"flex size-12 items-center justify-center rounded-full bg-success-container text-on-success-container\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"size-6\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12\"\n />\n </svg>\n </div>\n {/snippet}\n {#snippet labelSlot()}\n <span class=\"mt-2 block text-sm font-semibold text-success\">\n Drag & drop your files\n </span>\n {/snippet}\n {#snippet descriptionSlot()}\n <span class=\"mt-1 block text-xs text-on-surface-variant\">\n PNG, JPG, GIF up to 10MB each\n </span>\n {/snippet}\n </FileUpload>\n </div>\n </section>\n\n <!-- Real World Examples -->\n <section class=\"space-y-3\">\n <h2 id=\"Real-World-Examples\" class=\"text-lg font-semibold\">\n<a href=\"#Real-World-Examples\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World Examples\n </a>\n</h2>\n <div class=\"space-y-6 rounded-lg bg-surface-container-high p-4\">\n <!-- Avatar Upload -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Avatar Upload</p>\n <div class=\"max-w-xs\">\n <FileUpload\n layout=\"grid\"\n accept=\"image/*\"\n icon=\"lucide:user-round\"\n label=\"Upload photo\"\n description=\"PNG or JPG up to 2MB\"\n color=\"primary\"\n ui={{ base: 'min-h-40 rounded-full', wrapper: 'py-4' }}\n />\n </div>\n </div>\n\n <!-- Document Upload -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Document Upload</p>\n <div\n class=\"max-w-lg space-y-4 rounded-lg border border-outline-variant bg-surface-container p-4\"\n >\n <div>\n <p class=\"mb-1 text-sm font-medium text-on-surface\">Resume *</p>\n <FileUpload\n accept=\".pdf,.doc,.docx\"\n icon=\"lucide:file-text\"\n label=\"Upload your resume\"\n description=\"PDF, DOC, DOCX up to 5MB\"\n size=\"sm\"\n color=\"primary\"\n />\n </div>\n <div>\n <p class=\"mb-1 text-sm font-medium text-on-surface\">Portfolio (optional)</p>\n <FileUpload\n multiple\n accept=\"image/*,.pdf\"\n icon=\"lucide:briefcase\"\n label=\"Upload portfolio files\"\n description=\"Images or PDFs\"\n size=\"sm\"\n color=\"secondary\"\n />\n </div>\n </div>\n </div>\n\n <!-- Gallery Upload -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Gallery Upload</p>\n <FileUpload\n multiple\n layout=\"grid\"\n accept=\"image/*\"\n icon=\"lucide:images\"\n label=\"Upload gallery images\"\n description=\"Drop multiple images to create a gallery\"\n color=\"tertiary\"\n />\n </div>\n </div>\n </section>\n\n <section class=\"space-y-3\">\n <h2 id=\"Max-size-per-file\" class=\"text-lg font-semibold\">\n<a href=\"#Max-size-per-file\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Max size per file\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code>maxSize</code> (bytes) to reject files above a threshold. Rejections are\n reported through <code>onReject</code>.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <FileUpload\n bind:value={maxSizeFiles}\n multiple\n maxSize={1024 * 1024}\n label=\"Max 1 MB per file\"\n description=\"Try a small text file vs. a high-res image\"\n onReject={(r) => (rejections = r)}\n />\n {#if rejections.length}\n <ul class=\"mt-3 space-y-1 text-sm text-error\">\n {#each rejections as r (`${r.file.name}-${r.reason}`)}\n <li>\n {r.file.name} — {reasonLabel(r.reason)}\n </li>\n {/each}\n </ul>\n {/if}\n </div>\n </section>\n\n <section class=\"space-y-3\">\n <h2 id=\"Max-files-count\" class=\"text-lg font-semibold\">\n<a href=\"#Max-files-count\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Max files count\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code>maxFiles</code> to cap the number of files in the selection. When the cap is\n reached, the root element exposes <code>data-full</code> so CSS can style the area as inactive.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <FileUpload\n bind:value={maxFilesFiles}\n multiple\n maxFiles={3}\n label=\"Up to 3 files\"\n description=\"Try selecting 4 — the 4th is rejected\"\n ui={{\n base: 'data-[full]:opacity-60 data-[full]:pointer-events-none'\n }}\n />\n <p class=\"mt-3 text-sm text-on-surface-variant\">\n {maxFilesFiles.length} / 3 selected\n </p>\n </div>\n </section>\n\n <section class=\"space-y-3\">\n <h2 id=\"Combined-validation\" class=\"text-lg font-semibold\">\n<a href=\"#Combined-validation\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Combined validation\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n All three rules (<code>accept</code>, <code>maxSize</code>, <code>maxFiles</code>) work\n together. <code>onReject</code> reports every rejected file in one call with its reason.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <FileUpload\n bind:value={validationFiles}\n multiple\n accept=\"image/*\"\n maxSize={2 * 1024 * 1024}\n maxFiles={5}\n label=\"Up to 5 images, max 2 MB each\"\n description=\"image/* — up to 5 files — max 2 MB\"\n onReject={(r) => (combinedRejections = r)}\n />\n {#if combinedRejections.length}\n <ul class=\"mt-3 space-y-1 text-sm text-error\">\n {#each combinedRejections as r (`${r.file.name}-${r.reason}`)}\n <li>\n {r.file.name} — {reasonLabel(r.reason)}\n </li>\n {/each}\n </ul>\n {/if}\n </div>\n </section>\n\n <section class=\"space-y-3\">\n <h2 id=\"Inside-a-Form-Zod-schema\" class=\"text-lg font-semibold\">\n<a href=\"#Inside-a-Form-Zod-schema\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Inside a Form (Zod schema)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n FileUpload reads the parent <code>FormField</code> + <code>Form</code> context. When the\n Zod schema fails, the FormField shows the error and FileUpload picks up\n <code>aria-invalid</code> + the error highlight color. Once you pick the required number of\n files, the schema passes and the error clears automatically — no manual error state.\n </p>\n <div class=\"rounded-lg border border-outline-variant bg-surface-container p-6\">\n <Form\n bind:api={fileUploadFormApi}\n bind:state={fileUploadFormState}\n schema={fileUploadSchema}\n onsubmit={handleFileUploadSubmit}\n class=\"max-w-md space-y-4\"\n >\n <FormField name=\"avatar\" label=\"Avatar\" description=\"JPG or PNG, max 5 MB\" required>\n <FileUpload\n bind:value={fileUploadFormState.avatar}\n accept=\"image/*\"\n label=\"Drop avatar here\"\n />\n </FormField>\n\n <FormField name=\"gallery\" label=\"Gallery\" required>\n <FileUpload\n bind:value={fileUploadFormState.gallery}\n multiple\n layout=\"grid\"\n accept=\"image/*\"\n label=\"Drop at least 2 images\"\n />\n </FormField>\n\n <div class=\"flex items-center gap-3\">\n <Button type=\"submit\" loading={fileUploadFormApi?.loading}>Submit</Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n color=\"secondary\"\n onclick={() => fileUploadFormApi?.clear()}\n >\n Clear errors\n </Button>\n </div>\n </Form>\n\n {#if fileUploadSubmitted}\n <div\n class=\"mt-4 rounded-md border border-primary/20 bg-primary-container p-3 text-sm text-on-primary-container\"\n >\n <p class=\"font-medium\">Submitted:</p>\n <pre class=\"mt-1 text-xs\">{fileUploadSubmitted}</pre>\n </div>\n {/if}\n </div>\n </section>\n</div>\n",
155
+ "file-upload": "<script lang=\"ts\">\n import { FileUpload, Button, Form, FormField } from '$lib/index.js'\n import type { FileUploadRejection, FormApi } from '$lib/index.js'\n import { z } from 'zod'\n\n const colors = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'success',\n 'warning',\n 'error',\n 'info',\n 'surface'\n ] as const\n\n const sizes = ['xs', 'sm', 'md', 'lg', 'xl'] as const\n\n let basicFiles = $state<File[]>([])\n let multipleFiles = $state<File[]>([])\n let gridSingleFile = $state<File[]>([])\n let gridMultipleFiles = $state<File[]>([])\n let buttonFiles = $state<File[]>([])\n let actionsFiles = $state<File[]>([])\n let highlightFiles = $state<File[]>([])\n let noDropzoneFiles = $state<File[]>([])\n let noPreviewFiles = $state<File[]>([])\n let imageFiles = $state<File[]>([])\n\n let maxSizeFiles = $state<File[]>([])\n let maxFilesFiles = $state<File[]>([])\n let validationFiles = $state<File[]>([])\n let rejections = $state<FileUploadRejection[]>([])\n let combinedRejections = $state<FileUploadRejection[]>([])\n\n function reasonLabel(reason: FileUploadRejection['reason']): string {\n if (reason === 'maxSize') return 'too large'\n if (reason === 'maxFiles') return 'too many'\n return 'wrong type'\n }\n\n const fileUploadSchema = z.object({\n avatar: z.array(z.instanceof(File)).min(1, 'Avatar is required'),\n gallery: z.array(z.instanceof(File)).min(2, 'Pick at least 2 images')\n })\n\n let fileUploadFormState = $state<{ avatar: File[]; gallery: File[] }>({\n avatar: [],\n gallery: []\n })\n let fileUploadFormApi = $state<FormApi<unknown>>()\n let fileUploadSubmitted = $state<string | null>(null)\n\n function handleFileUploadSubmit(event: { data: unknown }) {\n const data = event.data as { avatar: File[]; gallery: File[] }\n fileUploadSubmitted = JSON.stringify(\n {\n avatar: data.avatar.map((f) => f.name),\n gallery: data.gallery.map((f) => f.name)\n },\n null,\n 2\n )\n }\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">FileUpload</h1>\n <p class=\"text-on-surface-variant\">\n Upload files via drag-and-drop or file dialog with preview and removal. This is for\n <strong>OS file drops</strong> — to reorder UI lists use\n <a href=\"/docs/hooks/use-sortable\" class=\"text-primary underline\">useSortable</a>\n or\n <a href=\"/docs/components/sortable-list\" class=\"text-primary underline\">SortableList</a>.\n </p>\n </div>\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <FileUpload bind:value={basicFiles} />\n <p class=\"mt-3 text-sm text-on-surface-variant\">\n Selected: {basicFiles.length ? basicFiles.map((f) => f.name).join(', ') : 'none'}\n </p>\n </div>\n </section>\n\n <!-- Multiple Files -->\n <section class=\"space-y-3\">\n <h2 id=\"Multiple-Files\" class=\"text-lg font-semibold\">\n<a href=\"#Multiple-Files\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Multiple Files\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <FileUpload\n bind:value={multipleFiles}\n multiple\n label=\"Drop multiple files here\"\n description=\"Upload as many files as you need\"\n />\n </div>\n </section>\n\n <!-- Variants -->\n <section class=\"space-y-3\">\n <h2 id=\"Variants\" class=\"text-lg font-semibold\">\n<a href=\"#Variants\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Variants\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-4 rounded-lg bg-surface-container-high p-4 sm:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Area (default)</p>\n <FileUpload label=\"Drop files here\" description=\"Supports drag & drop\" />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Button</p>\n <FileUpload variant=\"button\" label=\"Upload file\" bind:value={buttonFiles} />\n {#if buttonFiles.length > 0}\n <p class=\"text-sm text-on-surface-variant\">\n {buttonFiles.map((f) => f.name).join(', ')}\n </p>\n {/if}\n </div>\n </div>\n </section>\n\n <!-- Layout -->\n <section class=\"space-y-3\">\n <h2 id=\"Layout\" class=\"text-lg font-semibold\">\n<a href=\"#Layout\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Layout\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-4 rounded-lg bg-surface-container-high p-4 sm:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">List (default)</p>\n <FileUpload\n bind:value={multipleFiles}\n multiple\n layout=\"list\"\n label=\"Upload files\"\n description=\"Files shown as list\"\n />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Grid · Multiple</p>\n <FileUpload\n bind:value={gridMultipleFiles}\n multiple\n layout=\"grid\"\n label=\"Upload images\"\n description=\"Files shown as grid thumbnails\"\n accept=\"image/*\"\n />\n </div>\n </div>\n </section>\n\n <!-- Grid Single File -->\n <section class=\"space-y-3\">\n <h2 id=\"Grid--Single-File-Preview\" class=\"text-lg font-semibold\">\n<a href=\"#Grid--Single-File-Preview\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Grid · Single File Preview\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <div class=\"max-w-xs\">\n <FileUpload\n bind:value={gridSingleFile}\n layout=\"grid\"\n label=\"Upload image\"\n description=\"Preview fills the area\"\n accept=\"image/*\"\n ui={{ base: 'min-h-48' }}\n />\n </div>\n </div>\n </section>\n\n <!-- Dropzone vs No Dropzone -->\n <section class=\"space-y-3\">\n <h2 id=\"Dropzone\" class=\"text-lg font-semibold\">\n<a href=\"#Dropzone\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Dropzone\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-4 rounded-lg bg-surface-container-high p-4 sm:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">With dropzone (default)</p>\n <FileUpload\n label=\"Drag & drop or click\"\n description=\"Drag files anywhere on this area\"\n />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">No dropzone · click only</p>\n <FileUpload\n bind:value={noDropzoneFiles}\n dropzone={false}\n label=\"Click to upload\"\n description=\"No drag-and-drop\"\n />\n </div>\n </div>\n </section>\n\n <!-- Custom Actions Slot -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-Actions\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-Actions\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Actions\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <FileUpload\n bind:value={actionsFiles}\n multiple\n interactive={false}\n label=\"Drag files here\"\n description=\"or click the button below\"\n >\n {#snippet actionsSlot({ open })}\n <Button\n size=\"sm\"\n variant=\"outline\"\n color=\"primary\"\n leadingIcon=\"lucide:folder-open\"\n label=\"Browse files\"\n onclick={open}\n />\n {/snippet}\n </FileUpload>\n </div>\n </section>\n\n <!-- Highlight -->\n <section class=\"space-y-3\">\n <h2 id=\"Highlight\" class=\"text-lg font-semibold\">\n<a href=\"#Highlight\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Highlight\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-4 rounded-lg bg-surface-container-high p-4 sm:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Normal</p>\n <FileUpload bind:value={basicFiles} label=\"No highlight\" />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Highlighted</p>\n <FileUpload bind:value={highlightFiles} highlight label=\"Highlighted border\" />\n </div>\n </div>\n </section>\n\n <!-- Colors -->\n <section class=\"space-y-3\">\n <h2 id=\"Colors\" class=\"text-lg font-semibold\">\n<a href=\"#Colors\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Colors\n </a>\n</h2>\n <div class=\"space-y-3 rounded-lg bg-surface-container-high p-4\">\n {#each colors as color (color)}\n <FileUpload\n {color}\n highlight\n label={color}\n description=\"Drop files or click to browse\"\n ui={{ wrapper: 'py-2' }}\n />\n {/each}\n </div>\n </section>\n\n <!-- Sizes -->\n <section class=\"space-y-3\">\n <h2 id=\"Sizes\" class=\"text-lg font-semibold\">\n<a href=\"#Sizes\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Sizes\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-4\">\n {#each sizes as size (size)}\n <div class=\"flex items-start gap-4\">\n <span class=\"w-6 pt-3 text-xs text-on-surface-variant\">{size}</span>\n <FileUpload\n {size}\n label=\"Upload file\"\n description=\"Drag & drop or click\"\n class=\"flex-1\"\n />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Disabled & Loading -->\n <section class=\"space-y-3\">\n <h2 id=\"States\" class=\"text-lg font-semibold\">\n<a href=\"#States\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n States\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-4 rounded-lg bg-surface-container-high p-4 sm:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Disabled</p>\n <FileUpload disabled label=\"Disabled upload\" description=\"Cannot interact\" />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Loading</p>\n <FileUpload loading label=\"Processing files…\" description=\"Please wait\" />\n </div>\n </div>\n </section>\n\n <!-- No Preview -->\n <section class=\"space-y-3\">\n <h2 id=\"Preview-Disabled\" class=\"text-lg font-semibold\">\n<a href=\"#Preview-Disabled\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Preview Disabled\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <FileUpload\n bind:value={noPreviewFiles}\n multiple\n preview={false}\n label=\"Upload without preview\"\n description=\"Files are selected but not listed\"\n />\n <p class=\"mt-3 text-sm text-on-surface-variant\">\n {noPreviewFiles.length} file(s) selected\n </p>\n </div>\n </section>\n\n <!-- Accept Filter -->\n <section class=\"space-y-3\">\n <h2 id=\"Accept-Filter\" class=\"text-lg font-semibold\">\n<a href=\"#Accept-Filter\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Accept Filter\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-4 rounded-lg bg-surface-container-high p-4 sm:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Images only</p>\n <FileUpload\n bind:value={imageFiles}\n multiple\n accept=\"image/*\"\n color=\"tertiary\"\n icon=\"lucide:image\"\n label=\"Drop images here\"\n description=\"Only image files accepted\"\n />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">PDF only</p>\n <FileUpload\n accept=\".pdf\"\n color=\"error\"\n icon=\"lucide:file-text\"\n label=\"Drop PDF here\"\n description=\"Only .pdf files accepted\"\n />\n </div>\n </div>\n </section>\n\n <!-- Image Preview -->\n <section class=\"space-y-3\">\n <h2 id=\"Image-Preview\" class=\"text-lg font-semibold\">\n<a href=\"#Image-Preview\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Image Preview\n </a>\n</h2>\n <div class=\"grid grid-cols-1 gap-4 rounded-lg bg-surface-container-high p-4 sm:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Preview enabled (default)</p>\n <FileUpload\n bind:value={imageFiles}\n multiple\n accept=\"image/*\"\n icon=\"lucide:image\"\n label=\"Drop images here\"\n description=\"Click the zoom icon to preview\"\n />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Preview disabled</p>\n <FileUpload\n multiple\n accept=\"image/*\"\n icon=\"lucide:image\"\n label=\"Drop images here\"\n description=\"No preview button shown\"\n imagePreview={false}\n />\n </div>\n </div>\n </section>\n\n <!-- Custom Slots -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-Slots\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-Slots\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Slots\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <FileUpload multiple bind:value={basicFiles} color=\"success\">\n {#snippet leadingSlot()}\n <div\n class=\"flex size-12 items-center justify-center rounded-full bg-success-container text-on-success-container\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"size-6\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12\"\n />\n </svg>\n </div>\n {/snippet}\n {#snippet labelSlot()}\n <span class=\"mt-2 block text-sm font-semibold text-success\">\n Drag & drop your files\n </span>\n {/snippet}\n {#snippet descriptionSlot()}\n <span class=\"mt-1 block text-xs text-on-surface-variant\">\n PNG, JPG, GIF up to 10MB each\n </span>\n {/snippet}\n </FileUpload>\n </div>\n </section>\n\n <!-- Real World Examples -->\n <section class=\"space-y-3\">\n <h2 id=\"Real-World-Examples\" class=\"text-lg font-semibold\">\n<a href=\"#Real-World-Examples\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World Examples\n </a>\n</h2>\n <div class=\"space-y-6 rounded-lg bg-surface-container-high p-4\">\n <!-- Avatar Upload -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Avatar Upload</p>\n <div class=\"max-w-xs\">\n <FileUpload\n layout=\"grid\"\n accept=\"image/*\"\n icon=\"lucide:user-round\"\n label=\"Upload photo\"\n description=\"PNG or JPG up to 2MB\"\n color=\"primary\"\n ui={{ base: 'min-h-40 rounded-full', wrapper: 'py-4' }}\n />\n </div>\n </div>\n\n <!-- Document Upload -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Document Upload</p>\n <div\n class=\"max-w-lg space-y-4 rounded-lg border border-outline-variant bg-surface-container p-4\"\n >\n <div>\n <p class=\"mb-1 text-sm font-medium text-on-surface\">Resume *</p>\n <FileUpload\n accept=\".pdf,.doc,.docx\"\n icon=\"lucide:file-text\"\n label=\"Upload your resume\"\n description=\"PDF, DOC, DOCX up to 5MB\"\n size=\"sm\"\n color=\"primary\"\n />\n </div>\n <div>\n <p class=\"mb-1 text-sm font-medium text-on-surface\">Portfolio (optional)</p>\n <FileUpload\n multiple\n accept=\"image/*,.pdf\"\n icon=\"lucide:briefcase\"\n label=\"Upload portfolio files\"\n description=\"Images or PDFs\"\n size=\"sm\"\n color=\"secondary\"\n />\n </div>\n </div>\n </div>\n\n <!-- Gallery Upload -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Gallery Upload</p>\n <FileUpload\n multiple\n layout=\"grid\"\n accept=\"image/*\"\n icon=\"lucide:images\"\n label=\"Upload gallery images\"\n description=\"Drop multiple images to create a gallery\"\n color=\"tertiary\"\n />\n </div>\n </div>\n </section>\n\n <section class=\"space-y-3\">\n <h2 id=\"Max-size-per-file\" class=\"text-lg font-semibold\">\n<a href=\"#Max-size-per-file\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Max size per file\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code>maxSize</code> (bytes) to reject files above a threshold. Rejections are\n reported through <code>onReject</code>.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <FileUpload\n bind:value={maxSizeFiles}\n multiple\n maxSize={1024 * 1024}\n label=\"Max 1 MB per file\"\n description=\"Try a small text file vs. a high-res image\"\n onReject={(r) => (rejections = r)}\n />\n {#if rejections.length}\n <ul class=\"mt-3 space-y-1 text-sm text-error\">\n {#each rejections as r (`${r.file.name}-${r.reason}`)}\n <li>\n {r.file.name} — {reasonLabel(r.reason)}\n </li>\n {/each}\n </ul>\n {/if}\n </div>\n </section>\n\n <section class=\"space-y-3\">\n <h2 id=\"Max-files-count\" class=\"text-lg font-semibold\">\n<a href=\"#Max-files-count\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Max files count\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code>maxFiles</code> to cap the number of files in the selection. When the cap is\n reached, the root element exposes <code>data-full</code> so CSS can style the area as inactive.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <FileUpload\n bind:value={maxFilesFiles}\n multiple\n maxFiles={3}\n label=\"Up to 3 files\"\n description=\"Try selecting 4 — the 4th is rejected\"\n ui={{\n base: 'data-[full]:opacity-60 data-[full]:pointer-events-none'\n }}\n />\n <p class=\"mt-3 text-sm text-on-surface-variant\">\n {maxFilesFiles.length} / 3 selected\n </p>\n </div>\n </section>\n\n <section class=\"space-y-3\">\n <h2 id=\"Combined-validation\" class=\"text-lg font-semibold\">\n<a href=\"#Combined-validation\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Combined validation\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n All three rules (<code>accept</code>, <code>maxSize</code>, <code>maxFiles</code>) work\n together. <code>onReject</code> reports every rejected file in one call with its reason.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <FileUpload\n bind:value={validationFiles}\n multiple\n accept=\"image/*\"\n maxSize={2 * 1024 * 1024}\n maxFiles={5}\n label=\"Up to 5 images, max 2 MB each\"\n description=\"image/* — up to 5 files — max 2 MB\"\n onReject={(r) => (combinedRejections = r)}\n />\n {#if combinedRejections.length}\n <ul class=\"mt-3 space-y-1 text-sm text-error\">\n {#each combinedRejections as r (`${r.file.name}-${r.reason}`)}\n <li>\n {r.file.name} — {reasonLabel(r.reason)}\n </li>\n {/each}\n </ul>\n {/if}\n </div>\n </section>\n\n <section class=\"space-y-3\">\n <h2 id=\"Inside-a-Form-Zod-schema\" class=\"text-lg font-semibold\">\n<a href=\"#Inside-a-Form-Zod-schema\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Inside a Form (Zod schema)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n FileUpload reads the parent <code>FormField</code> + <code>Form</code> context. When the\n Zod schema fails, the FormField shows the error and FileUpload picks up\n <code>aria-invalid</code> + the error highlight color. Once you pick the required number of\n files, the schema passes and the error clears automatically — no manual error state.\n </p>\n <div class=\"rounded-lg border border-outline-variant bg-surface-container p-6\">\n <Form\n bind:api={fileUploadFormApi}\n bind:state={fileUploadFormState}\n schema={fileUploadSchema}\n onsubmit={handleFileUploadSubmit}\n class=\"max-w-md space-y-4\"\n >\n <FormField name=\"avatar\" label=\"Avatar\" description=\"JPG or PNG, max 5 MB\" required>\n <FileUpload\n bind:value={fileUploadFormState.avatar}\n accept=\"image/*\"\n label=\"Drop avatar here\"\n />\n </FormField>\n\n <FormField name=\"gallery\" label=\"Gallery\" required>\n <FileUpload\n bind:value={fileUploadFormState.gallery}\n multiple\n layout=\"grid\"\n accept=\"image/*\"\n label=\"Drop at least 2 images\"\n />\n </FormField>\n\n <div class=\"flex items-center gap-3\">\n <Button type=\"submit\" loading={fileUploadFormApi?.loading}>Submit</Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n color=\"secondary\"\n onclick={() => fileUploadFormApi?.clear()}\n >\n Clear errors\n </Button>\n </div>\n </Form>\n\n {#if fileUploadSubmitted}\n <div\n class=\"mt-4 rounded-md border border-primary/20 bg-primary-container p-3 text-sm text-on-primary-container\"\n >\n <p class=\"font-medium\">Submitted:</p>\n <pre class=\"mt-1 text-xs\">{fileUploadSubmitted}</pre>\n </div>\n {/if}\n </div>\n </section>\n</div>\n",
151
156
  "pin-input": "<script lang=\"ts\">\n import { PinInput, FormField } from '$lib/index.js'\n\n const colors = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'success',\n 'warning',\n 'error',\n 'info',\n 'surface'\n ] as const\n\n const sizes = ['xs', 'sm', 'md', 'lg', 'xl'] as const\n const variants = ['outline', 'soft', 'subtle', 'ghost', 'none'] as const\n\n let basicValue = $state('')\n let otpValue = $state('')\n let numericValue = $state('')\n let maskedValue = $state('')\n let highlightValue = $state('')\n let formValue = $state('')\n let formFieldValue = $state('')\n let formFieldErrorValue = $state('')\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">Pin Input</h1>\n <p class=\"text-on-surface-variant\">\n Accessible PIN / OTP input built on bits-ui. Supports masking, numeric mode, OTP\n autocomplete, and form integration.\n </p>\n </div>\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-6\">\n <PinInput bind:value={basicValue} />\n <p class=\"text-sm text-on-surface-variant\">Value: \"{basicValue}\"</p>\n </div>\n </section>\n\n <!-- Lengths -->\n <section class=\"space-y-3\">\n <h2 id=\"Length\" class=\"text-lg font-semibold\">\n<a href=\"#Length\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Length\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-6\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">4 cells</p>\n <PinInput length={4} />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">5 cells (default)</p>\n <PinInput />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">6 cells (OTP)</p>\n <PinInput bind:value={otpValue} length={6} otp />\n </div>\n </div>\n </section>\n\n <!-- Type -->\n <section class=\"space-y-3\">\n <h2 id=\"Type\" class=\"text-lg font-semibold\">\n<a href=\"#Type\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Type\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-6\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">\n type=\"number\" — digits only\n </p>\n <PinInput bind:value={numericValue} type=\"number\" length={6} />\n <p class=\"text-sm text-on-surface-variant\">Value: \"{numericValue}\"</p>\n </div>\n </div>\n </section>\n\n <!-- Mask -->\n <section class=\"space-y-3\">\n <h2 id=\"Mask\" class=\"text-lg font-semibold\">\n<a href=\"#Mask\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Mask\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-6\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">\n mask — shows ● instead of characters\n </p>\n <PinInput bind:value={maskedValue} mask />\n <p class=\"text-sm text-on-surface-variant\">Value: \"{maskedValue}\"</p>\n </div>\n </div>\n </section>\n\n <!-- Variants -->\n <section class=\"space-y-3\">\n <h2 id=\"Variants\" class=\"text-lg font-semibold\">\n<a href=\"#Variants\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Variants\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-6\">\n {#each variants as variant (variant)}\n <div class=\"flex items-center gap-4\">\n <span class=\"w-16 text-sm text-on-surface-variant\">{variant}</span>\n <PinInput {variant} />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Colors -->\n <section class=\"space-y-3\">\n <h2 id=\"Colors\" class=\"text-lg font-semibold\">\n<a href=\"#Colors\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Colors\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-6\">\n {#each colors as color (color)}\n <div class=\"flex items-center gap-4\">\n <span class=\"w-20 text-sm text-on-surface-variant\">{color}</span>\n <PinInput {color} />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Sizes -->\n <section class=\"space-y-3\">\n <h2 id=\"Sizes\" class=\"text-lg font-semibold\">\n<a href=\"#Sizes\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Sizes\n </a>\n</h2>\n <div class=\"space-y-5 rounded-lg bg-surface-container-high p-6\">\n {#each sizes as size (size)}\n <div class=\"flex items-center gap-4\">\n <span class=\"w-6 text-xs text-on-surface-variant\">{size}</span>\n <PinInput {size} fixed />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Highlight -->\n <section class=\"space-y-3\">\n <h2 id=\"Highlight\" class=\"text-lg font-semibold\">\n<a href=\"#Highlight\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Highlight\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-6\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">\n highlight — all cells show the color ring (e.g. error state)\n </p>\n <PinInput bind:value={highlightValue} highlight color=\"error\" />\n </div>\n </div>\n </section>\n\n <!-- Placeholder -->\n <section class=\"space-y-3\">\n <h2 id=\"Placeholder\" class=\"text-lg font-semibold\">\n<a href=\"#Placeholder\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Placeholder\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-6\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Default (○)</p>\n <PinInput />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Custom (–)</p>\n <PinInput placeholder=\"–\" />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Digit hint (0)</p>\n <PinInput placeholder=\"0\" type=\"number\" length={6} />\n </div>\n </div>\n </section>\n\n <!-- Disabled -->\n <section class=\"space-y-3\">\n <h2 id=\"Disabled\" class=\"text-lg font-semibold\">\n<a href=\"#Disabled\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Disabled\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-6\">\n <PinInput defaultValue=\"12\" disabled />\n </div>\n </section>\n\n <!-- Form Integration -->\n <section class=\"space-y-3\">\n <h2 id=\"Form-Integration\" class=\"text-lg font-semibold\">\n<a href=\"#Form-Integration\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Form Integration\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-6\">\n <form\n class=\"max-w-sm space-y-4\"\n onsubmit={(e) => {\n e.preventDefault()\n const fd = new FormData(e.currentTarget)\n alert(`otp = ${fd.get('otp')}`)\n }}\n >\n <div class=\"space-y-2\">\n <label class=\"block text-sm font-medium text-on-surface\" for=\"otp-input\"\n >OTP Code</label\n >\n <PinInput\n bind:value={formValue}\n name=\"otp\"\n inputId=\"otp-input\"\n length={6}\n type=\"number\"\n otp\n color=\"success\"\n />\n </div>\n <button\n type=\"submit\"\n class=\"rounded-md bg-primary px-4 py-2 text-sm font-medium text-on-primary\"\n >\n Verify\n </button>\n </form>\n </div>\n </section>\n\n <!-- FormField Integration -->\n <section class=\"space-y-3\">\n <h2 id=\"FormField-Integration\" class=\"text-lg font-semibold\">\n<a href=\"#FormField-Integration\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n FormField Integration\n </a>\n</h2>\n <div class=\"max-w-sm space-y-4 rounded-lg bg-surface-container-high p-6\">\n <FormField label=\"Verification Code\" description=\"Enter the 6-digit code sent to you.\">\n <PinInput bind:value={formFieldValue} length={6} type=\"number\" otp class=\"mt-1\" />\n </FormField>\n\n <FormField\n label=\"PIN\"\n error={formFieldErrorValue.length > 0 && formFieldErrorValue.length < 4\n ? 'PIN must be 4 digits.'\n : undefined}\n >\n <PinInput bind:value={formFieldErrorValue} length={4} class=\"mt-1\" />\n </FormField>\n </div>\n </section>\n\n <!-- Custom UI Slots -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-UI-Slots\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-UI-Slots\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom UI Slots\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-6\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Rounded full cells</p>\n <PinInput color=\"tertiary\" ui={{ base: 'rounded-full' }} />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Large gap</p>\n <PinInput size=\"lg\" color=\"secondary\" ui={{ root: 'gap-3' }} fixed />\n </div>\n </div>\n </section>\n\n <section class=\"space-y-3\">\n <h2 id=\"Loading\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Loading\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Loading\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Pass <code>loading</code> to overlay a spinner and disable input while verifying the\n code (e.g. checking an OTP against the backend). Use <code>loadingIcon</code> to customize\n the spinner.\n </p>\n <div class=\"flex flex-wrap items-center gap-6 rounded-lg bg-surface-container-high p-4\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Loading</p>\n <PinInput loading length={6} />\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Loading + masked</p>\n <PinInput loading mask color=\"primary\" length={4} />\n </div>\n </div>\n </section>\n</div>\n",
152
157
  "rating": "<script lang=\"ts\">\n import { Rating } from '$lib/index.js'\n \n let rating1 = $state(3)\n let rating2 = $state(4.5)\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">Rating</h1>\n <p class=\"text-on-surface-variant\">\n A star rating input component supporting half-stars, read-only mode, and custom icons.\n </p>\n </div>\n\n <!-- Usage -->\n <section class=\"space-y-3\">\n <h2 id=\"Usage\" class=\"text-lg font-semibold\">\n<a href=\"#Usage\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Usage\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1\">bind:value</code> to control the rating.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4 flex flex-col gap-4 max-w-sm\">\n <Rating bind:value={rating1} />\n <div class=\"text-sm text-on-surface-variant\">\n <strong>Current Value:</strong> {rating1}\n </div>\n </div>\n </section>\n\n <!-- Half Stars -->\n <section class=\"space-y-3\">\n <h2 id=\"Half-Stars\" class=\"text-lg font-semibold\">\n<a href=\"#Half-Stars\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Half Stars\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Set <code class=\"rounded bg-surface-container-highest px-1\">allowHalf={`{true}`}</code> to enable half-star precision rating via mouse or keyboard.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4 flex flex-col gap-4 max-w-sm\">\n <Rating bind:value={rating2} allowHalf={true} />\n <div class=\"text-sm text-on-surface-variant\">\n <strong>Current Value:</strong> {rating2}\n </div>\n </div>\n </section>\n\n <!-- Custom Icons -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-Icons\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-Icons\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Icons\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Provide <code class=\"rounded bg-surface-container-highest px-1\">iconFull</code> and <code class=\"rounded bg-surface-container-highest px-1\">iconEmpty</code> to change the shapes.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4 flex flex-col gap-4 max-w-sm\">\n <Rating \n value={4} \n iconFull=\"lucide:heart\" \n iconEmpty=\"lucide:heart\" \n class=\"text-error\" \n />\n <Rating \n value={3} \n iconFull=\"lucide:flame\" \n iconEmpty=\"lucide:flame\" \n class=\"text-warning-500\" \n />\n </div>\n </section>\n</div>\n",
153
158
  "tags-input": "<script lang=\"ts\">\n import { TagsInput, Button } from '$lib/index.js'\n \n let tags1 = $state(['svelte', 'tailwind', 'typescript'])\n let tags2 = $state(['apple', 'banana'])\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">TagsInput</h1>\n <p class=\"text-on-surface-variant\">\n An input component for entering and managing multiple string values as tags (pills).\n </p>\n </div>\n\n <!-- Usage -->\n <section class=\"space-y-3\">\n <h2 id=\"Usage\" class=\"text-lg font-semibold\">\n<a href=\"#Usage\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Usage\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1\">bind:value</code> with an array of strings. Press Enter or comma (,) to add a tag.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4 flex flex-col gap-4 max-w-sm\">\n <TagsInput bind:value={tags1} placeholder=\"Add new skill...\" />\n <div class=\"text-sm text-on-surface-variant\">\n <strong>Current Value:</strong> {tags1.join(', ')}\n </div>\n </div>\n </section>\n\n <!-- Max Tags -->\n <section class=\"space-y-3\">\n <h2 id=\"Max-Tags\" class=\"text-lg font-semibold\">\n<a href=\"#Max-Tags\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Max Tags\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Set the <code class=\"rounded bg-surface-container-highest px-1\">maxTags</code> prop to limit how many tags can be added.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4 flex justify-center\">\n <div class=\"w-full max-w-sm\">\n <TagsInput bind:value={tags2} maxTags={3} placeholder=\"Max 3 tags allowed\" />\n </div>\n </div>\n </section>\n</div>\n",
@@ -158,7 +163,7 @@
158
163
  "chat": "<script lang=\"ts\">\n import { ChatBubble, ChatMessage, ChatInput, Avatar, Button } from '$lib/index.js'\n \n let messageText = $state('')\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">Chat</h1>\n <p class=\"text-on-surface-variant\">\n A comprehensive set of components to build chat interfaces, including bubbles, messages, and inputs.\n </p>\n </div>\n\n <!-- Usage -->\n <section class=\"space-y-3\">\n <h2 id=\"Usage\" class=\"text-lg font-semibold\">\n<a href=\"#Usage\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Usage\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Combine <code class=\"rounded bg-surface-container-highest px-1\">ChatBubble</code>, <code class=\"rounded bg-surface-container-highest px-1\">ChatMessage</code>, and <code class=\"rounded bg-surface-container-highest px-1\">ChatInput</code> to create conversational UI.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4 flex justify-center\">\n <div class=\"w-full max-w-md h-[400px] flex flex-col bg-surface border border-outline-variant rounded-lg overflow-hidden\">\n <div class=\"flex-1 p-4 overflow-y-auto space-y-4\">\n <ChatBubble position=\"start\" name=\"Obi-Wan\" time=\"12:00\">\n {#snippet avatar()}\n <Avatar src=\"https://i.pravatar.cc/150?img=11\" alt=\"Obi\" />\n {/snippet}\n <ChatMessage>Hello there!</ChatMessage>\n </ChatBubble>\n \n <ChatBubble position=\"end\" name=\"Grievous\" time=\"12:01\">\n {#snippet avatar()}\n <Avatar src=\"https://i.pravatar.cc/150?img=12\" alt=\"Grievous\" />\n {/snippet}\n <ChatMessage variant=\"primary\">General Kenobi.</ChatMessage>\n <ChatMessage variant=\"primary\">You are a bold one.</ChatMessage>\n </ChatBubble>\n </div>\n \n <div class=\"p-3 border-t border-outline-variant bg-surface-50 dark:bg-surface-900\">\n <ChatInput bind:value={messageText} placeholder=\"Type a message...\">\n {#snippet trailing()}\n <Button icon=\"lucide:send\" size=\"sm\" color=\"primary\" square disabled={!messageText} />\n {/snippet}\n </ChatInput>\n </div>\n </div>\n </div>\n </section>\n\n <!-- Variants -->\n <section class=\"space-y-3\">\n <h2 id=\"Message-Variants\" class=\"text-lg font-semibold\">\n<a href=\"#Message-Variants\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Message Variants\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use the <code class=\"rounded bg-surface-container-highest px-1\">variant</code> prop on <code class=\"rounded bg-surface-container-highest px-1\">ChatMessage</code> to change its appearance.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4 flex flex-col gap-2 max-w-md\">\n <ChatBubble position=\"start\">\n <ChatMessage variant=\"surface\">Surface (Default)</ChatMessage>\n <ChatMessage variant=\"primary\">Primary</ChatMessage>\n <ChatMessage variant=\"outline\">Outline</ChatMessage>\n </ChatBubble>\n </div>\n </section>\n</div>\n",
159
164
  "marquee": "<script lang=\"ts\">\n import { Marquee, Card } from '$lib/index.js'\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">Marquee</h1>\n <p class=\"text-on-surface-variant\">\n An infinite scrolling marquee component for displaying logos, text, or cards.\n </p>\n </div>\n\n <!-- Usage -->\n <section class=\"space-y-3\">\n <h2 id=\"Horizontal-Marquee\" class=\"text-lg font-semibold\">\n<a href=\"#Horizontal-Marquee\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Horizontal Marquee\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n A standard left-scrolling marquee that pauses on hover.\n </p>\n <div class=\"rounded-lg bg-surface-container-high py-8 overflow-hidden\">\n <Marquee pauseOnHover={true} duration=\"20s\">\n {#each Array(6) as _, i}\n <Card class=\"w-48 p-4 text-center font-semibold\">Brand {i + 1}</Card>\n {/each}\n </Marquee>\n </div>\n </section>\n\n <!-- Vertical -->\n <section class=\"space-y-3\">\n <h2 id=\"Vertical-Marquee\" class=\"text-lg font-semibold\">\n<a href=\"#Vertical-Marquee\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Vertical Marquee\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Set <code class=\"rounded bg-surface-container-highest px-1\">direction=\"up\"</code> or <code class=\"rounded bg-surface-container-highest px-1\">\"down\"</code> for vertical scrolling.\n </p>\n <div class=\"rounded-lg bg-surface-container-high py-8 overflow-hidden flex justify-center\">\n <div class=\"h-[300px] flex gap-4 overflow-hidden\">\n <Marquee direction=\"up\" duration=\"15s\">\n {#each Array(5) as _, i}\n <Card class=\"w-48 p-4 text-center\">Feature A-{i + 1}</Card>\n {/each}\n </Marquee>\n <Marquee direction=\"down\" duration=\"15s\">\n {#each Array(5) as _, i}\n <Card class=\"w-48 p-4 text-center\">Feature B-{i + 1}</Card>\n {/each}\n </Marquee>\n </div>\n </div>\n </section>\n</div>\n",
160
165
  "progress": "<script lang=\"ts\">\n import { Progress, Button } from '$lib/index.js'\n\n const colors = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'success',\n 'warning',\n 'error',\n 'info',\n 'surface'\n ] as const\n const sizes = ['2xs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl'] as const\n const animations = ['carousel', 'carousel-inverse', 'swing', 'elastic'] as const\n\n let value = $state(65)\n let stepValue = $state(2)\n const steps = ['Cart', 'Shipping', 'Payment', 'Confirm']\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">Progress</h1>\n <p class=\"text-on-surface-variant\">\n Visual indicator for task completion or loading status.\n </p>\n </div>\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-4\">\n <Progress value={25} />\n <Progress value={50} />\n <Progress value={75} />\n <Progress value={100} />\n </div>\n </section>\n\n <!-- Interactive -->\n <section class=\"space-y-3\">\n <h2 id=\"Interactive\" class=\"text-lg font-semibold\">\n<a href=\"#Interactive\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Interactive\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-4\">\n <Progress {value} status />\n <div class=\"flex items-center gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n onclick={() => (value = Math.max(0, value - 10))}\n >\n -10\n </Button>\n <input type=\"range\" min=\"0\" max=\"100\" bind:value class=\"flex-1\" />\n <Button\n size=\"sm\"\n variant=\"outline\"\n onclick={() => (value = Math.min(100, value + 10))}\n >\n +10\n </Button>\n </div>\n </div>\n </section>\n\n <!-- Colors -->\n <section class=\"space-y-3\">\n <h2 id=\"Colors\" class=\"text-lg font-semibold\">\n<a href=\"#Colors\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Colors\n </a>\n</h2>\n <div class=\"space-y-3 rounded-lg bg-surface-container-high p-4\">\n {#each colors as color (color)}\n <div class=\"flex items-center gap-3\">\n <span class=\"w-20 text-sm text-on-surface-variant capitalize\">{color}</span>\n <Progress {color} value={70} class=\"flex-1\" />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Sizes -->\n <section class=\"space-y-3\">\n <h2 id=\"Sizes\" class=\"text-lg font-semibold\">\n<a href=\"#Sizes\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Sizes\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-4\">\n {#each sizes as size (size)}\n <div class=\"flex items-center gap-3\">\n <span class=\"w-12 text-sm text-on-surface-variant\">{size}</span>\n <Progress {size} value={60} class=\"flex-1\" />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- With Status -->\n <section class=\"space-y-3\">\n <h2 id=\"With-Status\" class=\"text-lg font-semibold\">\n<a href=\"#With-Status\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n With Status\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Display percentage text alongside the progress bar.\n </p>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-4\">\n <Progress value={33} status />\n <Progress value={66} status color=\"success\" />\n <Progress value={100} status color=\"tertiary\" />\n </div>\n </section>\n\n <!-- Indeterminate -->\n <section class=\"space-y-3\">\n <h2 id=\"Indeterminate\" class=\"text-lg font-semibold\">\n<a href=\"#Indeterminate\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Indeterminate\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n When value is null, the progress shows an animated indeterminate state.\n </p>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-4\">\n <Progress value={null} />\n <Progress value={null} color=\"success\" />\n <Progress value={null} color=\"tertiary\" />\n </div>\n </section>\n\n <!-- Animations -->\n <section class=\"space-y-3\">\n <h2 id=\"Animations\" class=\"text-lg font-semibold\">\n<a href=\"#Animations\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Animations\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Different animation styles for the indeterminate state.\n </p>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-4\">\n {#each animations as animation (animation)}\n <div class=\"flex items-center gap-3\">\n <span class=\"w-32 text-sm text-on-surface-variant\">{animation}</span>\n <Progress {animation} class=\"flex-1\" />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Vertical -->\n <section class=\"space-y-3\">\n <h2 id=\"Vertical\" class=\"text-lg font-semibold\">\n<a href=\"#Vertical\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Vertical\n </a>\n</h2>\n <div class=\"flex flex-wrap gap-8 rounded-lg bg-surface-container-high p-4\">\n <div class=\"flex flex-col items-center gap-2\">\n <div class=\"h-32\">\n <Progress value={75} orientation=\"vertical\" />\n </div>\n <span class=\"text-xs text-on-surface-variant\">75%</span>\n </div>\n <div class=\"flex flex-col items-center gap-2\">\n <div class=\"h-32\">\n <Progress value={50} orientation=\"vertical\" color=\"success\" />\n </div>\n <span class=\"text-xs text-on-surface-variant\">50%</span>\n </div>\n <div class=\"flex flex-col items-center gap-2\">\n <div class=\"h-32\">\n <Progress value={null} orientation=\"vertical\" color=\"tertiary\" />\n </div>\n <span class=\"text-xs text-on-surface-variant\">loading</span>\n </div>\n {#each sizes as size (size)}\n <div class=\"flex flex-col items-center gap-2\">\n <div class=\"h-32\">\n <Progress value={60} orientation=\"vertical\" {size} />\n </div>\n <span class=\"text-xs text-on-surface-variant\">{size}</span>\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Inverted -->\n <section class=\"space-y-3\">\n <h2 id=\"Inverted\" class=\"text-lg font-semibold\">\n<a href=\"#Inverted\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Inverted\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Reverse the fill direction and status position.\n </p>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-4\">\n <div class=\"grid grid-cols-2 gap-4\">\n <div class=\"space-y-2\">\n <span class=\"text-sm text-on-surface-variant\">Normal</span>\n <Progress value={60} status />\n </div>\n <div class=\"space-y-2\">\n <span class=\"text-sm text-on-surface-variant\">Inverted</span>\n <Progress value={60} status inverted />\n </div>\n </div>\n <div class=\"flex gap-8\">\n <div class=\"flex flex-col items-center gap-2\">\n <div class=\"h-32\">\n <Progress value={60} orientation=\"vertical\" />\n </div>\n <span class=\"text-xs text-on-surface-variant\">Normal</span>\n </div>\n <div class=\"flex flex-col items-center gap-2\">\n <div class=\"h-32\">\n <Progress value={60} orientation=\"vertical\" inverted />\n </div>\n <span class=\"text-xs text-on-surface-variant\">Inverted</span>\n </div>\n </div>\n </div>\n </section>\n\n <!-- Steps -->\n <section class=\"space-y-3\">\n <h2 id=\"Steps\" class=\"text-lg font-semibold\">\n<a href=\"#Steps\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Steps\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Pass an array of strings as max to display labeled steps with overlay transitions.\n </p>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-4\">\n <Progress value={stepValue} max={steps} />\n <div class=\"flex items-center justify-center gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n onclick={() => (stepValue = Math.max(0, stepValue - 1))}\n disabled={stepValue === 0}\n >\n Previous\n </Button>\n <span class=\"px-4 text-sm text-on-surface-variant\">\n Step {stepValue + 1} of {steps.length}\n </span>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onclick={() => (stepValue = Math.min(steps.length - 1, stepValue + 1))}\n disabled={stepValue === steps.length - 1}\n >\n Next\n </Button>\n </div>\n\n <Progress value={0} max={['Account', 'Profile', 'Review', 'Done']} color=\"tertiary\" />\n <Progress value={3} max={['Account', 'Profile', 'Review', 'Done']} color=\"success\" />\n </div>\n </section>\n\n <!-- Custom Status Slot -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-Status-Slot\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-Status-Slot\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Status Slot\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Customize the status display with a snippet.</p>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-4\">\n <Progress value={75} status>\n {#snippet statusSlot({ percent })}\n <span class=\"font-medium text-primary\">{percent}% completed</span>\n {/snippet}\n </Progress>\n <Progress value={40} status color=\"warning\">\n {#snippet statusSlot({ percent })}\n <span class=\"text-warning\">Uploading... {percent}%</span>\n {/snippet}\n </Progress>\n </div>\n </section>\n\n <!-- Custom Step Slot -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-Step-Slot\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-Step-Slot\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Step Slot\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Customize individual step labels with a snippet.\n </p>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-4\">\n <Progress value={1} max={['Draft', 'Review', 'Approved', 'Published']}>\n {#snippet stepSlot({ step, index })}\n <span class=\"flex items-center gap-1\">\n <span\n class=\"inline-flex size-5 items-center justify-center rounded-full text-xs\n {index <= 1\n ? 'bg-primary text-on-primary'\n : 'bg-surface-container-highest text-on-surface-variant'}\"\n >\n {index + 1}\n </span>\n {step}\n </span>\n {/snippet}\n </Progress>\n </div>\n </section>\n\n <!-- UI Slot Overrides -->\n <section class=\"space-y-3\">\n <h2 id=\"UI-Slot-Overrides\" class=\"text-lg font-semibold\">\n<a href=\"#UI-Slot-Overrides\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n UI Slot Overrides\n </a>\n</h2>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-4\">\n <Progress\n value={70}\n ui={{\n base: 'rounded-none',\n indicator: 'rounded-none bg-gradient-to-r from-primary to-tertiary'\n }}\n />\n <Progress\n value={50}\n status\n ui={{\n base: 'h-4 rounded-lg',\n indicator: 'rounded-lg',\n status: 'font-bold text-primary'\n }}\n />\n </div>\n </section>\n\n <!-- Real World Examples -->\n <section class=\"space-y-3\">\n <h2 id=\"Real-World-Examples\" class=\"text-lg font-semibold\">\n<a href=\"#Real-World-Examples\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World Examples\n </a>\n</h2>\n <div class=\"space-y-6 rounded-lg bg-surface-container-high p-4\">\n <!-- File Upload -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">File Upload</p>\n <div class=\"rounded-lg border border-outline-variant bg-surface-container p-4\">\n <div class=\"mb-2 flex items-center justify-between\">\n <span class=\"text-sm\">document.pdf</span>\n <span class=\"text-sm text-on-surface-variant\">2.4 MB / 3.2 MB</span>\n </div>\n <Progress value={75} color=\"primary\" size=\"sm\" />\n </div>\n </div>\n\n <!-- Loading State -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Loading State</p>\n <div class=\"rounded-lg border border-outline-variant bg-surface-container p-4\">\n <div class=\"mb-2 flex items-center gap-2\">\n <span class=\"text-sm\">Processing data...</span>\n </div>\n <Progress value={null} color=\"info\" size=\"xs\" />\n </div>\n </div>\n\n <!-- Multi-step Form -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Checkout Flow</p>\n <div class=\"rounded-lg border border-outline-variant bg-surface-container p-4\">\n <Progress\n value={2}\n max={['Cart', 'Shipping', 'Payment', 'Done']}\n color=\"success\"\n />\n </div>\n </div>\n\n <!-- Skill Bars -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Skill Bars</p>\n <div\n class=\"space-y-3 rounded-lg border border-outline-variant bg-surface-container p-4\"\n >\n <div class=\"space-y-1\">\n <div class=\"flex justify-between text-sm\">\n <span>TypeScript</span>\n <span class=\"text-on-surface-variant\">90%</span>\n </div>\n <Progress value={90} color=\"primary\" size=\"sm\" />\n </div>\n <div class=\"space-y-1\">\n <div class=\"flex justify-between text-sm\">\n <span>Svelte</span>\n <span class=\"text-on-surface-variant\">85%</span>\n </div>\n <Progress value={85} color=\"tertiary\" size=\"sm\" />\n </div>\n <div class=\"space-y-1\">\n <div class=\"flex justify-between text-sm\">\n <span>Rust</span>\n <span class=\"text-on-surface-variant\">60%</span>\n </div>\n <Progress value={60} color=\"warning\" size=\"sm\" />\n </div>\n </div>\n </div>\n </div>\n </section>\n\n <!-- Colors x Sizes Matrix -->\n <section class=\"space-y-3\">\n <h2 id=\"Colors-x-Sizes\" class=\"text-lg font-semibold\">\n<a href=\"#Colors-x-Sizes\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Colors x Sizes\n </a>\n</h2>\n <div class=\"overflow-x-auto rounded-lg bg-surface-container-high p-4\">\n <table class=\"w-full\">\n <thead>\n <tr class=\"border-b border-outline-variant\">\n <th class=\"px-3 py-3 text-left text-sm font-medium text-on-surface-variant\"\n >Color</th\n >\n {#each sizes as size (size)}\n <th\n class=\"px-3 py-3 text-center text-sm font-medium text-on-surface-variant\"\n >{size}</th\n >\n {/each}\n </tr>\n </thead>\n <tbody>\n {#each colors as color (color)}\n <tr class=\"border-b border-outline-variant/50\">\n <td\n class=\"px-3 py-3 text-sm font-medium text-on-surface-variant capitalize\"\n >{color}</td\n >\n {#each sizes as size (size)}\n <td class=\"px-3 py-3\">\n <Progress {color} {size} value={65} />\n </td>\n {/each}\n </tr>\n {/each}\n </tbody>\n </table>\n </div>\n </section>\n</div>\n",
161
- "toast": "<script lang=\"ts\">\n import { toast, Toaster } from '$lib/index.js'\n import { Button, Icon } from '$lib/index.js'\n import type { ToasterProps } from '$lib/components/Toast/toast.types.js'\n\n type Variant = NonNullable<ToasterProps['variant']>\n type Position = NonNullable<ToasterProps['position']>\n\n const variants: Variant[] = ['outline', 'soft', 'subtle', 'solid', 'accent']\n const positions: Position[] = [\n 'top-left',\n 'top-center',\n 'top-right',\n 'bottom-left',\n 'bottom-center',\n 'bottom-right'\n ]\n\n let activeVariant: Variant = $state('outline')\n let activePosition: Position = $state('bottom-right')\n let expandToasts = $state(false)\n let showCloseButton = $state(true)\n\n let counter = $state(0)\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">Toast</h1>\n <p class=\"text-on-surface-variant\">\n Non-intrusive notification messages that appear temporarily. Powered by svelte-sonner\n with Svelora semantic color theming.\n </p>\n </div>\n\n <!-- Toaster Configuration -->\n <section class=\"space-y-3\">\n <h2 id=\"Toaster-Configuration\" class=\"text-lg font-semibold\">\n<a href=\"#Toaster-Configuration\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Toaster Configuration\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Adjust the Toaster props to see how they affect all toasts.\n </p>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-4\">\n <!-- Variant -->\n <div class=\"space-y-1.5\">\n <p class=\"text-sm font-medium\">Variant</p>\n <div class=\"flex flex-wrap gap-2\">\n {#each variants as v (v)}\n <Button\n variant={activeVariant === v ? 'solid' : 'outline'}\n size=\"xs\"\n onclick={() => (activeVariant = v)}\n >\n {v}\n </Button>\n {/each}\n </div>\n </div>\n\n <!-- Position -->\n <div class=\"space-y-1.5\">\n <p class=\"text-sm font-medium\">Position</p>\n <div class=\"flex flex-wrap gap-2\">\n {#each positions as p (p)}\n <Button\n variant={activePosition === p ? 'solid' : 'outline'}\n size=\"xs\"\n onclick={() => (activePosition = p)}\n >\n {p}\n </Button>\n {/each}\n </div>\n </div>\n\n <!-- Toggles -->\n <div class=\"flex flex-wrap gap-4\">\n <label class=\"flex items-center gap-2 text-sm\">\n <input type=\"checkbox\" bind:checked={expandToasts} class=\"accent-primary\" />\n Expand\n </label>\n <label class=\"flex items-center gap-2 text-sm\">\n <input type=\"checkbox\" bind:checked={showCloseButton} class=\"accent-primary\" />\n Close Button\n </label>\n </div>\n </div>\n </section>\n\n <!-- Basic Usage -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic-Usage\" class=\"text-lg font-semibold\">\n<a href=\"#Basic-Usage\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic Usage\n </a>\n</h2>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <Button variant=\"outline\" onclick={() => toast('This is a default toast')}>\n Default\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => toast('Title only toast — no description needed')}\n >\n Title Only\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast('Event has been created', {\n description: 'Monday, January 3rd at 6:00 PM'\n })}\n >\n With Description\n </Button>\n </div>\n </section>\n\n <!-- Types -->\n <section class=\"space-y-3\">\n <h2 id=\"Types\" class=\"text-lg font-semibold\">\n<a href=\"#Types\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Types\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Each type automatically maps to a semantic color from the theme.\n </p>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <Button\n color=\"success\"\n variant=\"soft\"\n onclick={() => toast.success('Operation completed successfully')}\n >\n Success\n </Button>\n <Button\n color=\"error\"\n variant=\"soft\"\n onclick={() => toast.error('Something went wrong')}\n >\n Error\n </Button>\n <Button\n color=\"warning\"\n variant=\"soft\"\n onclick={() => toast.warning('Please review before proceeding')}\n >\n Warning\n </Button>\n <Button\n color=\"info\"\n variant=\"soft\"\n onclick={() => toast.info('Here is some useful information')}\n >\n Info\n </Button>\n <Button variant=\"soft\" onclick={() => toast.loading('Loading data...')}>Loading</Button>\n </div>\n </section>\n\n <!-- Types with Description -->\n <section class=\"space-y-3\">\n <h2 id=\"Types-with-Description\" class=\"text-lg font-semibold\">\n<a href=\"#Types-with-Description\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Types with Description\n </a>\n</h2>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <Button\n color=\"success\"\n variant=\"soft\"\n onclick={() =>\n toast.success('Payment Successful', {\n description: 'Your payment of $49.99 has been processed.'\n })}\n >\n Success\n </Button>\n <Button\n color=\"error\"\n variant=\"soft\"\n onclick={() =>\n toast.error('Upload Failed', {\n description: 'The file exceeds the maximum size of 10MB.'\n })}\n >\n Error\n </Button>\n <Button\n color=\"warning\"\n variant=\"soft\"\n onclick={() =>\n toast.warning('Storage Almost Full', {\n description: 'You have used 90% of your storage quota.'\n })}\n >\n Warning\n </Button>\n <Button\n color=\"info\"\n variant=\"soft\"\n onclick={() =>\n toast.info('New Update Available', {\n description: 'Version 2.0 is ready to install.'\n })}\n >\n Info\n </Button>\n </div>\n </section>\n\n <!-- Colors -->\n <section class=\"space-y-3\">\n <h2 id=\"Colors\" class=\"text-lg font-semibold\">\n<a href=\"#Colors\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Colors\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use the <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >color</code\n >\n option to apply any semantic color, beyond the 4 built-in types.\n </p>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <Button\n color=\"primary\"\n variant=\"soft\"\n onclick={() => toast('Primary notification', { color: 'primary' })}\n >\n Primary\n </Button>\n <Button\n color=\"secondary\"\n variant=\"soft\"\n onclick={() => toast('Secondary notification', { color: 'secondary' })}\n >\n Secondary\n </Button>\n <Button\n color=\"tertiary\"\n variant=\"soft\"\n onclick={() => toast('Tertiary notification', { color: 'tertiary' })}\n >\n Tertiary\n </Button>\n <Button\n color=\"success\"\n variant=\"soft\"\n onclick={() =>\n toast('Success notification', {\n color: 'success',\n description: 'Same as toast.success() but via color option'\n })}\n >\n Success\n </Button>\n <Button\n color=\"warning\"\n variant=\"soft\"\n onclick={() => toast('Warning notification', { color: 'warning' })}\n >\n Warning\n </Button>\n <Button\n color=\"error\"\n variant=\"soft\"\n onclick={() => toast('Error notification', { color: 'error' })}\n >\n Error\n </Button>\n <Button\n color=\"info\"\n variant=\"soft\"\n onclick={() => toast('Info notification', { color: 'info' })}\n >\n Info\n </Button>\n </div>\n </section>\n\n <!-- Custom Icons -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-Icons\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-Icons\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Icons\n </a>\n</h2>\n\n <!-- Global icons -->\n <div class=\"space-y-1.5\">\n <p class=\"text-sm font-medium\">Global Icons (via Toaster snippets)</p>\n <p class=\"text-sm text-on-surface-variant\">\n The Toaster below uses Iconify icons instead of sonner's default SVGs. All typed\n toasts inherit them automatically.\n </p>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <Button\n variant=\"outline\"\n color=\"success\"\n onclick={() =>\n toast.success('Deployed to production', {\n description: 'Uses global successIcon snippet.'\n })}\n >\n Success (global)\n </Button>\n <Button\n variant=\"outline\"\n color=\"error\"\n onclick={() =>\n toast.error('Build failed', {\n description: 'Uses global errorIcon snippet.'\n })}\n >\n Error (global)\n </Button>\n <Button\n variant=\"outline\"\n color=\"warning\"\n onclick={() =>\n toast.warning('Disk usage at 92%', {\n description: 'Uses global warningIcon snippet.'\n })}\n >\n Warning (global)\n </Button>\n <Button\n variant=\"outline\"\n color=\"info\"\n onclick={() =>\n toast.info('Maintenance scheduled', {\n description: 'Uses global infoIcon snippet.'\n })}\n >\n Info (global)\n </Button>\n </div>\n </div>\n\n <!-- Per-toast icon override -->\n <div class=\"space-y-1.5\">\n <p class=\"text-sm font-medium\">Per-toast Icon Override</p>\n <p class=\"text-sm text-on-surface-variant\">\n Pass an Iconify icon name string, or\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">null</code>\n to hide the icon.\n </p>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <Button\n variant=\"outline\"\n onclick={() =>\n toast('Rocket launched!', {\n description: 'icon: \"lucide:rocket\"',\n icon: 'lucide:rocket'\n })}\n >\n Rocket\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast.success('Git push complete', {\n description: 'icon: \"lucide:git-branch\"',\n icon: 'lucide:git-branch'\n })}\n >\n Git Branch\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast.warning('Battery low', {\n description: 'icon: \"lucide:battery-low\"',\n icon: 'lucide:battery-low'\n })}\n >\n Battery\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast.error('Server offline', {\n description: 'icon: \"lucide:server-off\"',\n icon: 'lucide:server-off'\n })}\n >\n Server\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast.success('No icon toast', {\n description: 'icon: null',\n icon: null\n })}\n >\n icon: null\n </Button>\n </div>\n </div>\n </section>\n\n <!-- Avatar -->\n <section class=\"space-y-3\">\n <h2 id=\"Avatar\" class=\"text-lg font-semibold\">\n<a href=\"#Avatar\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Avatar\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use the <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >avatar</code\n >\n option to show an avatar in the icon slot.\n </p>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <Button\n variant=\"outline\"\n onclick={() =>\n toast('John commented on your post', {\n description: '\"Great article! Thanks for sharing.\"',\n avatar: {\n src: 'https://i.pravatar.cc/150?img=1',\n alt: 'John'\n }\n })}\n >\n With Photo\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast.info('Jane sent you a message', {\n description: '\"Hey, are you free for a call?\"',\n avatar: {\n src: 'https://i.pravatar.cc/150?img=5',\n alt: 'Jane'\n }\n })}\n >\n Info + Avatar\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast('Alex invited you to a project', {\n description: 'Project: Svelora Design System',\n avatar: { alt: 'Alex' },\n action: {\n label: 'Accept',\n onClick: () => toast.success('Invitation accepted')\n }\n })}\n >\n Initials + Action\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast.success('Team joined!', {\n description: 'You are now a member of the Design team.',\n avatar: { icon: 'lucide:users' }\n })}\n >\n Icon Fallback\n </Button>\n </div>\n </section>\n\n <!-- Action & Cancel -->\n <section class=\"space-y-3\">\n <h2 id=\"Action--Cancel\" class=\"text-lg font-semibold\">\n<a href=\"#Action--Cancel\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Action & Cancel\n </a>\n</h2>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <Button\n variant=\"outline\"\n onclick={() =>\n toast('File deleted', {\n action: {\n label: 'Undo',\n onClick: () => toast.success('File restored')\n }\n })}\n >\n Action Button\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast('Accept cookies?', {\n action: {\n label: 'Accept',\n onClick: () => toast.success('Cookies accepted')\n },\n cancel: {\n label: 'Decline',\n onClick: () => toast('Cookies declined')\n }\n })}\n >\n Action + Cancel\n </Button>\n <Button\n variant=\"outline\"\n color=\"info\"\n onclick={() =>\n toast.info('New version available', {\n action: {\n label: 'Update Now',\n onClick: () => toast.success('Updating...')\n },\n cancel: {\n label: 'Later',\n onClick: () => {}\n }\n })}\n >\n Info with Actions\n </Button>\n <Button\n variant=\"outline\"\n color=\"error\"\n onclick={() =>\n toast.error('Delete this item?', {\n description: 'This action cannot be undone.',\n action: {\n label: 'Delete',\n onClick: () => toast.success('Item deleted')\n },\n cancel: {\n label: 'Keep',\n onClick: () => {}\n }\n })}\n >\n Error with Confirm\n </Button>\n </div>\n </section>\n\n <!-- Promise -->\n <section class=\"space-y-3\">\n <h2 id=\"Promise\" class=\"text-lg font-semibold\">\n<a href=\"#Promise\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Promise\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Automatically transitions between loading, success, and error states.\n </p>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <Button\n variant=\"outline\"\n onclick={() => {\n toast.promise(new Promise((resolve) => setTimeout(resolve, 2000)), {\n loading: 'Saving changes...',\n success: 'Changes saved successfully!',\n error: 'Failed to save changes'\n })\n }}\n >\n Save (Resolves)\n </Button>\n <Button\n variant=\"outline\"\n color=\"error\"\n onclick={() => {\n toast.promise(\n new Promise((_, reject) =>\n setTimeout(() => reject(new Error('timeout')), 2000)\n ),\n {\n loading: 'Connecting to server...',\n success: 'Connected!',\n error: 'Connection failed'\n }\n )\n }}\n >\n Connect (Rejects)\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => {\n toast.promise(new Promise((resolve) => setTimeout(resolve, 3000)), {\n loading: 'Uploading file (0%)...',\n success: 'File uploaded!',\n error: 'Upload failed'\n })\n }}\n >\n Upload File\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => {\n toast.promise(new Promise((resolve) => setTimeout(resolve, 4000)), {\n loading: 'Deploying to production...',\n success: 'Deployed! Live at https://example.com',\n error: 'Deploy failed — check CI logs'\n })\n }}\n >\n Deploy\n </Button>\n </div>\n </section>\n\n <!-- Duration -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-Duration\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-Duration\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Duration\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Control how long a toast stays visible before auto-dismiss.\n </p>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <Button variant=\"outline\" onclick={() => toast('Gone in a flash', { duration: 1000 })}>\n 1 second\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => toast('Standard duration', { duration: 3000 })}\n >\n 3 seconds\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => toast('Longer reading time', { duration: 8000 })}\n >\n 8 seconds\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast('This toast will stay until dismissed', { duration: Infinity })}\n >\n Persistent (Infinity)\n </Button>\n </div>\n </section>\n\n <!-- Update Existing Toast -->\n <section class=\"space-y-3\">\n <h2 id=\"Update-Existing-Toast\" class=\"text-lg font-semibold\">\n<a href=\"#Update-Existing-Toast\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Update Existing Toast\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Reuse the same ID to update an existing toast in-place.\n </p>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <Button\n variant=\"outline\"\n onclick={() => {\n const id = toast.loading('Uploading...', { duration: Infinity })\n setTimeout(() => toast.success('Upload complete!', { id }), 2000)\n }}\n >\n Loading → Success\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => {\n const id = toast.loading('Validating...', { duration: Infinity })\n setTimeout(() => toast.error('Validation failed!', { id }), 2000)\n }}\n >\n Loading → Error\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => {\n const id = toast('Step 1 of 3...', { duration: Infinity })\n setTimeout(() => toast('Step 2 of 3...', { id, duration: Infinity }), 1000)\n setTimeout(() => toast('Step 3 of 3...', { id, duration: Infinity }), 2000)\n setTimeout(() => toast.success('All steps complete!', { id }), 3000)\n }}\n >\n Multi-step Progress\n </Button>\n </div>\n </section>\n\n <!-- Deduplicated -->\n <section class=\"space-y-3\">\n <h2 id=\"Deduplicated-Toasts\" class=\"text-lg font-semibold\">\n<a href=\"#Deduplicated-Toasts\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Deduplicated Toasts\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Pass the same <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >id</code\n >\n to prevent duplicate toasts. Clicking multiple times updates the existing toast instead of\n creating new ones.\n </p>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <Button\n variant=\"outline\"\n onclick={() =>\n toast('You have 3 new messages', {\n id: 'new-messages',\n description: 'Click again — no duplicate!'\n })}\n >\n Same ID (click many times)\n </Button>\n <Button\n variant=\"outline\"\n color=\"warning\"\n onclick={() =>\n toast.warning('Rate limit reached', {\n id: 'rate-limit',\n description: 'Try again in 30 seconds.'\n })}\n >\n Rate Limit (deduplicated)\n </Button>\n </div>\n </section>\n\n <!-- Dismiss -->\n <section class=\"space-y-3\">\n <h2 id=\"Programmatic-Dismiss\" class=\"text-lg font-semibold\">\n<a href=\"#Programmatic-Dismiss\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Programmatic Dismiss\n </a>\n</h2>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <Button\n variant=\"outline\"\n onclick={() => {\n const id = toast('Processing...', { duration: Infinity })\n setTimeout(() => {\n toast.dismiss(id)\n toast.success('Done!')\n }, 2000)\n }}\n >\n Auto Dismiss After 2s\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => {\n toast('Toast 1', { duration: Infinity })\n toast.success('Toast 2', { duration: Infinity })\n toast.error('Toast 3', { duration: Infinity })\n }}\n >\n Create 3 Toasts\n </Button>\n <Button variant=\"outline\" color=\"error\" onclick={() => toast.dismiss()}>\n Dismiss All\n </Button>\n </div>\n </section>\n\n <!-- Stacking -->\n <section class=\"space-y-3\">\n <h2 id=\"Stacking\" class=\"text-lg font-semibold\">\n<a href=\"#Stacking\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Stacking\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Toggle \"Expand\" in the config above, then fire multiple toasts to see stacked vs\n expanded behavior.\n </p>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <Button\n variant=\"outline\"\n onclick={() => {\n counter++\n toast.success(`Notification #${counter}`, {\n description: 'Hover or expand to see all stacked toasts.'\n })\n }}\n >\n Add Toast (#{counter + 1})\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => {\n toast.info('Info toast', { description: 'First in stack' })\n setTimeout(\n () => toast.warning('Warning toast', { description: 'Second in stack' }),\n 300\n )\n setTimeout(\n () => toast.error('Error toast', { description: 'Third in stack' }),\n 600\n )\n }}\n >\n Fire 3 Different Types\n </Button>\n </div>\n </section>\n\n <!-- Non-dismissible -->\n <section class=\"space-y-3\">\n <h2 id=\"Non-dismissible\" class=\"text-lg font-semibold\">\n<a href=\"#Non-dismissible\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Non-dismissible\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n A toast that cannot be swiped away or closed by the user.\n </p>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <Button\n variant=\"outline\"\n onclick={() => {\n const id = toast.warning('Processing payment...', {\n description: 'Please do not close this page.',\n dismissible: false,\n duration: Infinity\n })\n setTimeout(() => {\n toast.success('Payment complete!', { id })\n }, 3000)\n }}\n >\n Non-dismissible (3s)\n </Button>\n </div>\n </section>\n\n <!-- All Types at Once (visual test) -->\n <section class=\"space-y-3\">\n <h2 id=\"All-Types-at-Once\" class=\"text-lg font-semibold\">\n<a href=\"#All-Types-at-Once\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n All Types at Once\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Fire every type simultaneously to visually compare styling.\n </p>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <Button\n variant=\"outline\"\n onclick={() => {\n toast('Default toast', {\n description: 'Neutral surface styling',\n duration: 10000\n })\n toast.success('Success toast', {\n description: 'Operation successful',\n duration: 10000\n })\n toast.error('Error toast', {\n description: 'Something went wrong',\n duration: 10000\n })\n toast.warning('Warning toast', {\n description: 'Proceed with caution',\n duration: 10000\n })\n toast.info('Info toast', {\n description: 'Here is some context',\n duration: 10000\n })\n toast.loading('Loading toast', {\n description: 'Please wait...',\n duration: 10000\n })\n }}\n >\n Fire All Types (10s)\n </Button>\n </div>\n </section>\n\n <!-- Real World Examples -->\n <section class=\"space-y-3\">\n <h2 id=\"Real-World-Examples\" class=\"text-lg font-semibold\">\n<a href=\"#Real-World-Examples\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World Examples\n </a>\n</h2>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <Button\n variant=\"outline\"\n onclick={() =>\n toast.success('Profile Updated', {\n description: 'Your profile changes have been saved.'\n })}\n >\n Save Profile\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast.error('Permission Denied', {\n description: 'You do not have access to this resource.',\n action: {\n label: 'Request Access',\n onClick: () => toast.info('Access request sent to admin')\n }\n })}\n >\n Access Denied\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast.warning('Session Expiring', {\n description: 'Your session will expire in 5 minutes.',\n duration: 8000,\n action: {\n label: 'Extend',\n onClick: () => toast.success('Session extended by 30 minutes')\n }\n })}\n >\n Session Warning\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => {\n toast.promise(new Promise((resolve) => setTimeout(resolve, 3000)), {\n loading: 'Sending email...',\n success: 'Email sent to john@example.com',\n error: 'Failed to send email'\n })\n }}\n >\n Send Email\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => {\n const id = toast('Item moved to trash', {\n action: {\n label: 'Undo',\n onClick: () => toast.success('Item restored')\n },\n cancel: {\n label: 'Delete permanently',\n onClick: () => {\n toast.dismiss(id)\n toast.error('Item permanently deleted')\n }\n }\n })\n }}\n >\n Move to Trash\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => {\n toast.info('New comment on your post', {\n description: '\"Great article! Thanks for sharing.\" — Jane',\n action: {\n label: 'View',\n onClick: () => toast('Opening post...')\n }\n })\n }}\n >\n New Comment\n </Button>\n </div>\n </section>\n</div>\n\n<!-- Dynamic Toaster with configurable props -->\n<Toaster\n variant={activeVariant}\n position={activePosition}\n expand={expandToasts}\n closeButton={showCloseButton}\n>\n {#snippet successIcon()}<Icon name=\"lucide:circle-check\" size=\"18\" />{/snippet}\n {#snippet errorIcon()}<Icon name=\"lucide:circle-x\" size=\"18\" />{/snippet}\n {#snippet warningIcon()}<Icon name=\"lucide:triangle-alert\" size=\"18\" />{/snippet}\n {#snippet infoIcon()}<Icon name=\"lucide:info\" size=\"18\" />{/snippet}\n</Toaster>\n",
166
+ "toast": "<script lang=\"ts\">\n import { toast, notify, confirmDialog, Toaster, Button, Icon, Card } from \"$lib/index.js\";\n import type { ToasterProps } from \"$lib/components/Toast/toast.types.js\";\n import AnimatedIcon from \"$lib/components/Toast/internal/AnimatedIcon.svelte\";\n import type { AnimatedIconType } from \"$lib/components/Toast/internal/animated-icon.types.js\";\n\n const ANIMATED_ICON_TYPES: AnimatedIconType[] = [\n \"success\",\n \"error\",\n \"warning\",\n \"info\",\n \"question\",\n \"loading\",\n \"processing\",\n ];\n\n let replayKey = $state(0);\n\n function replayAnimations() {\n replayKey++;\n }\n\n function showNotifySuccess() {\n notify.success(\"Operation successful\", {\n description: \"Your changes have been saved.\",\n });\n }\n\n function showNotifyError() {\n notify.error(\"Something went wrong\", {\n description: \"Please try again or contact support.\",\n });\n }\n\n function showNotifyWarning() {\n notify.warning(\"Please review\", {\n description: \"Some fields need your attention before continuing.\",\n });\n }\n\n function showNotifyInfo() {\n notify.info(\"Did you know?\", {\n description: \"You can use notify.* for animated icons and promise helpers.\",\n });\n }\n\n function showNotifyDefault() {\n notify.default(\"Default notification\", {\n description: \"A neutral toast without a semantic type.\",\n });\n }\n\n function showNotifyLoading() {\n notify.loading(\"Loading data…\", {\n description: \"Fetching the latest records from the server.\",\n });\n }\n\n function showNotifyProcessing() {\n notify.processing(\"Processing…\", {\n description: \"Your request is being handled in the background.\",\n });\n }\n\n async function testNotifyPromise() {\n const myPromise = new Promise<{ name: string }>((resolve, reject) => {\n setTimeout(() => {\n if (Math.random() > 0.3) resolve({ name: \"Success\" });\n else reject(new Error(\"Request failed\"));\n }, 2000);\n });\n\n notify.promise(myPromise, {\n loading: \"Saving changes…\",\n success: \"Changes saved successfully\",\n error: \"Failed to save changes\",\n });\n }\n\n async function testConfirm() {\n const ok = await confirmDialog.show({\n title: \"Save changes?\",\n message: \"Your edits will be applied immediately.\",\n icon: \"question\",\n confirmText: \"Save\",\n });\n\n if (ok) {\n notify.success(\"Changes saved\");\n }\n }\n\n async function testDelete() {\n const ok = await confirmDialog.delete({\n title: \"Delete this item?\",\n message: \"This action is permanent and cannot be undone.\",\n });\n\n if (ok) {\n notify.error(\"Item deleted\");\n }\n }\n\n async function testSuccessAlert() {\n await confirmDialog.show({\n title: \"Payment received\",\n message: \"Your transaction was completed successfully.\",\n icon: \"success\",\n confirmText: \"Got it\",\n confirmColor: \"success\",\n showCancel: false,\n });\n }\n\n async function testWarningConfirm() {\n const ok = await confirmDialog.show({\n title: \"Grant permission?\",\n message: \"This app wants to access your camera and microphone.\",\n icon: \"warning\",\n confirmText: \"Allow\",\n confirmColor: \"warning\",\n });\n\n if (ok) {\n notify.warning(\"Permission granted\");\n }\n }\n\n type Variant = NonNullable<ToasterProps[\"variant\"]>;\n type Position = NonNullable<ToasterProps[\"position\"]>;\n type Theme = NonNullable<ToasterProps[\"theme\"]>;\n\n const variants: Variant[] = [\n \"outline\",\n \"soft\",\n \"subtle\",\n \"solid\",\n \"accent\",\n ];\n const positions: Position[] = [\n \"top-left\",\n \"top-center\",\n \"top-right\",\n \"bottom-left\",\n \"bottom-center\",\n \"bottom-right\",\n ];\n const themes: Theme[] = [\"light\", \"dark\", \"system\"];\n const gaps = [8, 14, 24] as const;\n\n const toastOptionsReference = [\n {\n name: \"description\",\n type: \"string\",\n description: \"Secondary text below the title.\",\n },\n {\n name: \"duration\",\n type: \"number\",\n description: \"Auto-dismiss delay in ms. Use Infinity to persist.\",\n },\n {\n name: \"dismissible\",\n type: \"boolean\",\n description: \"When false, persists and hides the close button.\",\n },\n {\n name: \"closeButton\",\n type: \"boolean\",\n description:\n \"Show close button on this toast (overrides Toaster default).\",\n },\n {\n name: \"id\",\n type: \"string\",\n description: \"Reuse to update or deduplicate an existing toast.\",\n },\n {\n name: \"color\",\n type: \"ToastColor\",\n description:\n \"Semantic color override (primary, success, error, …).\",\n },\n {\n name: \"icon\",\n type: \"string | Component | null\",\n description: \"Iconify name, custom component, or null to hide.\",\n },\n {\n name: \"avatar\",\n type: \"AvatarProps\",\n description: \"Avatar in the icon slot (takes priority over icon).\",\n },\n {\n name: \"action\",\n type: \"{ label, onClick }\",\n description: \"Primary action button.\",\n },\n {\n name: \"cancel\",\n type: \"{ label, onClick }\",\n description: \"Secondary cancel button.\",\n },\n {\n name: \"position\",\n type: \"ToastPosition\",\n description: \"Override Toaster position for this toast.\",\n },\n {\n name: \"class\",\n type: \"string\",\n description: \"Extra CSS class on the toast element.\",\n },\n ] as const;\n\n const toasterPropsReference = [\n {\n name: \"variant\",\n type: \"'outline' | 'soft' | …\",\n description: \"Visual style. Default: outline.\",\n },\n {\n name: \"position\",\n type: \"ToastPosition\",\n description: \"Screen position. Default: bottom-right.\",\n },\n {\n name: \"duration\",\n type: \"number\",\n description: \"Default auto-dismiss for all toasts.\",\n },\n {\n name: \"closeButton\",\n type: \"boolean\",\n description: \"Show close button on every toast by default.\",\n },\n {\n name: \"gap\",\n type: \"number\",\n description: \"Vertical spacing between stacked toasts in px.\",\n },\n {\n name: \"theme\",\n type: \"'light' | 'dark' | 'system'\",\n description: \"Toaster theme attribute for styling.\",\n },\n {\n name: \"reverseOrder\",\n type: \"boolean\",\n description: \"Reverse stack offset calculation.\",\n },\n {\n name: \"successIcon\",\n type: \"Snippet\",\n description: \"Global icon snippet for success toasts.\",\n },\n {\n name: \"errorIcon\",\n type: \"Snippet\",\n description: \"Global icon snippet for error toasts.\",\n },\n {\n name: \"warningIcon\",\n type: \"Snippet\",\n description: \"Global icon snippet for warning toasts.\",\n },\n {\n name: \"infoIcon\",\n type: \"Snippet\",\n description: \"Global icon snippet for info toasts.\",\n },\n ] as const;\n\n const toastMethodsReference = [\n { name: \"toast(message, opts?)\", description: \"Show a default toast.\" },\n {\n name: 'toast.success / .error / .warning / .info / .loading / .processing',\n description: 'Typed toasts with animated icons and semantic styling.'\n },\n {\n name: 'notify.*',\n description: 'Convenience API with animated icons, promise min-duration, and checkSuccess.'\n },\n {\n name: 'confirmDialog.show / .delete',\n description: 'Imperative confirmations — see Confirm Dialog docs. Requires <ConfirmDialog /> in layout.'\n },\n {\n name: \"toast.promise(promise, msgs, opts?)\",\n description: \"Loading → success/error lifecycle.\",\n },\n {\n name: \"toast.dismiss(id?)\",\n description: \"Dismiss one toast by id, or all when id is omitted.\",\n },\n {\n name: \"toast.custom(component, opts?)\",\n description: \"Render a custom Svelte component as the toast body.\",\n },\n {\n name: \"toast.message(message, opts?)\",\n description: \"Alias for toast().\",\n },\n ] as const;\n\n let activeVariant: Variant = $state(\"outline\");\n let activePosition: Position = $state(\"bottom-right\");\n let activeTheme: Theme = $state(\"light\");\n let activeGap = $state(14);\n let showCloseButton = $state(true);\n\n let counter = $state(0);\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">Toast</h1>\n <p class=\"text-on-surface-variant\">\n Non-intrusive notification messages that appear temporarily. with\n Svelora semantic color theming.\n </p>\n </div>\n\n <!-- Notification Playground (notify + confirmDialog + Animated Icons) -->\n <section class=\"space-y-6\">\n <div class=\"grid grid-cols-1 gap-3 md:grid-cols-2\">\n <Card class=\"space-y-4 p-4\">\n <div class=\"flex items-center gap-3 border-b border-outline-variant/30 pb-4\">\n <div class=\"rounded-lg bg-primary/10 p-2\">\n <Icon name=\"lucide:bell\" size=\"24\" class=\"text-primary\" />\n </div>\n <h2 class=\"text-xl font-bold\">Toast</h2>\n </div>\n\n <div class=\"flex flex-col gap-3\">\n <div class=\"flex flex-wrap gap-2\">\n <Button\n color=\"success\"\n variant=\"soft\"\n size=\"sm\"\n leadingIcon=\"lucide:check-circle\"\n label=\"Success\"\n onclick={showNotifySuccess}\n />\n <Button\n color=\"error\"\n variant=\"soft\"\n size=\"sm\"\n leadingIcon=\"lucide:x-circle\"\n label=\"Error\"\n onclick={showNotifyError}\n />\n <Button\n color=\"warning\"\n variant=\"soft\"\n size=\"sm\"\n leadingIcon=\"lucide:triangle-alert\"\n label=\"Warning\"\n onclick={showNotifyWarning}\n />\n <Button\n color=\"info\"\n variant=\"soft\"\n size=\"sm\"\n leadingIcon=\"lucide:info\"\n label=\"Info\"\n onclick={showNotifyInfo}\n />\n </div>\n\n <p class=\"text-center text-xs text-on-surface-variant\">Advanced toasts</p>\n\n <div class=\"flex flex-wrap gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n leadingIcon=\"lucide:loader-2\"\n label=\"Promise\"\n onclick={testNotifyPromise}\n />\n <Button\n variant=\"outline\"\n size=\"sm\"\n leadingIcon=\"lucide:message-square\"\n label=\"Default\"\n onclick={showNotifyDefault}\n />\n <Button\n variant=\"outline\"\n size=\"sm\"\n leadingIcon=\"lucide:loader\"\n label=\"Loading\"\n onclick={showNotifyLoading}\n />\n <Button\n variant=\"outline\"\n size=\"sm\"\n leadingIcon=\"lucide:refresh-cw\"\n label=\"Processing\"\n onclick={showNotifyProcessing}\n />\n </div>\n </div>\n </Card>\n\n <Card class=\"space-y-4 p-4\">\n <div class=\"flex items-center gap-3 border-b border-outline-variant/30 pb-4\">\n <div class=\"rounded-lg bg-secondary/10 p-2\">\n <Icon name=\"lucide:messages-square\" size=\"24\" class=\"text-secondary\" />\n </div>\n <h2 class=\"text-xl font-bold\">Dialog</h2>\n </div>\n\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-high px-1\">confirmDialog</code> for\n imperative confirmations. A toast follows the outcome. See\n <a href=\"/docs/components/confirm-dialog\" class=\"text-primary underline\">Confirm Dialog docs</a>.\n </p>\n\n <div class=\"grid grid-cols-2 gap-2\">\n <Button\n color=\"secondary\"\n variant=\"soft\"\n size=\"sm\"\n leadingIcon=\"lucide:circle-help\"\n label=\"Confirm\"\n onclick={testConfirm}\n />\n <Button\n color=\"error\"\n variant=\"soft\"\n size=\"sm\"\n leadingIcon=\"lucide:trash-2\"\n label=\"Delete\"\n onclick={testDelete}\n />\n <Button\n color=\"success\"\n variant=\"soft\"\n size=\"sm\"\n leadingIcon=\"lucide:check\"\n label=\"Success alert\"\n onclick={testSuccessAlert}\n />\n <Button\n color=\"warning\"\n variant=\"soft\"\n size=\"sm\"\n leadingIcon=\"lucide:shield-alert\"\n label=\"Warning choice\"\n onclick={testWarningConfirm}\n />\n </div>\n </Card>\n </div>\n\n <section class=\"space-y-3\">\n <div class=\"flex items-center justify-between border-b border-outline-variant/30 pb-4\">\n <div class=\"flex items-center gap-3\">\n <div class=\"rounded-lg bg-tertiary/10 p-2\">\n <Icon name=\"lucide:sparkles\" size=\"24\" class=\"text-tertiary\" />\n </div>\n <h2 class=\"text-2xl font-bold\">Animated Icons</h2>\n </div>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n leadingIcon=\"lucide:refresh-cw\"\n label=\"Replay all\"\n onclick={replayAnimations}\n />\n </div>\n\n {#key replayKey}\n <div\n class=\"grid grid-cols-2 gap-3 rounded-xl border border-dashed border-outline-variant/40 bg-surface-container-high/50 p-6 sm:grid-cols-3 md:grid-cols-7\"\n >\n {#each ANIMATED_ICON_TYPES as type (type)}\n <div class=\"group flex flex-col items-center gap-3\">\n <div\n class=\"rounded-xl border border-outline-variant/30 bg-surface p-3 shadow-sm transition-transform duration-300 group-hover:scale-110\"\n >\n <AnimatedIcon {type} size={80} forceMotion />\n </div>\n <span class=\"text-sm font-medium text-on-surface-variant\">{type}</span>\n </div>\n {/each}\n </div>\n {/key}\n </section>\n\n <div\n class=\"flex items-start gap-3 rounded-xl border border-info/20 bg-info/10 p-4 text-info\"\n >\n <Icon name=\"lucide:info\" size=\"24\" class=\"mt-0.5 shrink-0\" />\n <div class=\"space-y-1\">\n <p class=\"font-bold\">Pro tip</p>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-high px-1\">notify</code> for\n animated icons, promise helpers, and consistent defaults. Pair with\n <code class=\"rounded bg-surface-container-high px-1\">confirmDialog</code> for\n confirmations — see\n <a href=\"/docs/components/confirm-dialog\" class=\"text-primary underline\">Confirm Dialog</a>\n setup and\n <code class=\"rounded bg-surface-container-high px-1\">&lt;ConfirmDialog /&gt;</code>\n in your root layout.\n </p>\n </div>\n </div>\n </section>\n\n <!-- Toaster Configuration -->\n <section class=\"space-y-3\">\n <h2 id=\"Toaster-Configuration\" class=\"text-lg font-semibold\">\n <a\n href=\"#Toaster-Configuration\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n Toaster Configuration\n </a>\n </h2>\n <p class=\"text-sm text-on-surface-variant\">\n Adjust the Toaster props to see how they affect all toasts.\n </p>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-4\">\n <!-- Variant -->\n <div class=\"space-y-1.5\">\n <p class=\"text-sm font-medium\">Variant</p>\n <div class=\"flex flex-wrap gap-2\">\n {#each variants as v (v)}\n <Button\n variant={activeVariant === v ? \"solid\" : \"outline\"}\n size=\"xs\"\n onclick={() => (activeVariant = v)}\n >\n {v}\n </Button>\n {/each}\n </div>\n </div>\n\n <!-- Position -->\n <div class=\"space-y-1.5\">\n <p class=\"text-sm font-medium\">Position</p>\n <div class=\"flex flex-wrap gap-2\">\n {#each positions as p (p)}\n <Button\n variant={activePosition === p ? \"solid\" : \"outline\"}\n size=\"xs\"\n onclick={() => (activePosition = p)}\n >\n {p}\n </Button>\n {/each}\n </div>\n </div>\n\n <!-- Toggles & layout -->\n <div class=\"flex flex-wrap gap-4\">\n <label class=\"flex items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n bind:checked={showCloseButton}\n class=\"accent-primary\"\n />\n Close Button (global)\n </label>\n </div>\n\n <div class=\"space-y-1.5\">\n <p class=\"text-sm font-medium\">Theme</p>\n <div class=\"flex flex-wrap gap-2\">\n {#each themes as t (t)}\n <Button\n variant={activeTheme === t ? \"solid\" : \"outline\"}\n size=\"xs\"\n onclick={() => (activeTheme = t)}\n >\n {t}\n </Button>\n {/each}\n </div>\n </div>\n\n <div class=\"space-y-1.5\">\n <p class=\"text-sm font-medium\">Gap</p>\n <div class=\"flex flex-wrap gap-2\">\n {#each gaps as g (g)}\n <Button\n variant={activeGap === g ? \"solid\" : \"outline\"}\n size=\"xs\"\n onclick={() => (activeGap = g)}\n >\n {g}px\n </Button>\n {/each}\n </div>\n </div>\n </div>\n </section>\n\n <!-- Basic Usage -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic-Usage\" class=\"text-lg font-semibold\">\n <a\n href=\"#Basic-Usage\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n Basic Usage\n </a>\n </h2>\n <div\n class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\"\n >\n <Button\n variant=\"outline\"\n onclick={() => toast(\"This is a default toast\")}\n >\n Default\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast(\"Title only toast — no description needed\")}\n >\n Title Only\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast(\"Event has been created\", {\n description: \"Monday, January 3rd at 6:00 PM\",\n })}\n >\n With Description\n </Button>\n </div>\n </section>\n\n <!-- Types -->\n <section class=\"space-y-3\">\n <h2 id=\"Types\" class=\"text-lg font-semibold\">\n <a\n href=\"#Types\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n Types\n </a>\n </h2>\n <p class=\"text-sm text-on-surface-variant\">\n Each type automatically maps to a semantic color from the theme.\n </p>\n <div\n class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\"\n >\n <Button\n color=\"success\"\n variant=\"soft\"\n onclick={() =>\n toast.success(\"Operation completed successfully\")}\n >\n Success\n </Button>\n <Button\n color=\"error\"\n variant=\"soft\"\n onclick={() => toast.error(\"Something went wrong\")}\n >\n Error\n </Button>\n <Button\n color=\"warning\"\n variant=\"soft\"\n onclick={() => toast.warning(\"Please review before proceeding\")}\n >\n Warning\n </Button>\n <Button\n color=\"info\"\n variant=\"soft\"\n onclick={() => toast.info(\"Here is some useful information\")}\n >\n Info\n </Button>\n <Button\n variant=\"soft\"\n onclick={() => toast.loading('Loading data...')}\n >\n Loading\n </Button>\n <Button\n variant=\"soft\"\n onclick={() => toast.processing('Processing data...')}\n >\n Processing\n </Button>\n </div>\n </section>\n\n <!-- Types with Description -->\n <section class=\"space-y-3\">\n <h2 id=\"Types-with-Description\" class=\"text-lg font-semibold\">\n <a\n href=\"#Types-with-Description\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n Types with Description\n </a>\n </h2>\n <div\n class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\"\n >\n <Button\n color=\"success\"\n variant=\"soft\"\n onclick={() =>\n toast.success(\"Payment Successful\", {\n description:\n \"Your payment of $49.99 has been processed.\",\n })}\n >\n Success\n </Button>\n <Button\n color=\"error\"\n variant=\"soft\"\n onclick={() =>\n toast.error(\"Upload Failed\", {\n description:\n \"The file exceeds the maximum size of 10MB.\",\n })}\n >\n Error\n </Button>\n <Button\n color=\"warning\"\n variant=\"soft\"\n onclick={() =>\n toast.warning(\"Storage Almost Full\", {\n description: \"You have used 90% of your storage quota.\",\n })}\n >\n Warning\n </Button>\n <Button\n color=\"info\"\n variant=\"soft\"\n onclick={() =>\n toast.info(\"New Update Available\", {\n description: \"Version 2.0 is ready to install.\",\n })}\n >\n Info\n </Button>\n </div>\n </section>\n\n <!-- Colors -->\n <section class=\"space-y-3\">\n <h2 id=\"Colors\" class=\"text-lg font-semibold\">\n <a\n href=\"#Colors\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n Colors\n </a>\n </h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use the <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >color</code\n >\n option to apply any semantic color, beyond the 4 built-in types.\n </p>\n <div\n class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\"\n >\n <Button\n color=\"primary\"\n variant=\"soft\"\n onclick={() =>\n toast(\"Primary notification\", { color: \"primary\" })}\n >\n Primary\n </Button>\n <Button\n color=\"secondary\"\n variant=\"soft\"\n onclick={() =>\n toast(\"Secondary notification\", { color: \"secondary\" })}\n >\n Secondary\n </Button>\n <Button\n color=\"tertiary\"\n variant=\"soft\"\n onclick={() =>\n toast(\"Tertiary notification\", { color: \"tertiary\" })}\n >\n Tertiary\n </Button>\n <Button\n color=\"success\"\n variant=\"soft\"\n onclick={() =>\n toast(\"Success notification\", {\n color: \"success\",\n description:\n \"Same as toast.success() but via color option\",\n })}\n >\n Success\n </Button>\n <Button\n color=\"warning\"\n variant=\"soft\"\n onclick={() =>\n toast(\"Warning notification\", { color: \"warning\" })}\n >\n Warning\n </Button>\n <Button\n color=\"error\"\n variant=\"soft\"\n onclick={() => toast(\"Error notification\", { color: \"error\" })}\n >\n Error\n </Button>\n <Button\n color=\"info\"\n variant=\"soft\"\n onclick={() => toast(\"Info notification\", { color: \"info\" })}\n >\n Info\n </Button>\n </div>\n </section>\n\n <!-- Custom Icons -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-Icons\" class=\"text-lg font-semibold\">\n <a\n href=\"#Custom-Icons\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n Custom Icons\n </a>\n </h2>\n\n <!-- Global icons -->\n <div class=\"space-y-1.5\">\n <p class=\"text-sm font-medium\">Icon Overrides (Toaster snippets)</p>\n <p class=\"text-sm text-on-surface-variant\">\n By default, typed toasts use animated SVG icons. Pass Toaster icon\n snippets below to override globally, or pass\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">icon</code>\n per toast.\n </p>\n <div\n class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\"\n >\n <Button\n variant=\"outline\"\n color=\"success\"\n onclick={() =>\n toast.success(\"Deployed to production\", {\n description: 'Uses the default animated success icon.',\n })}\n >\n Success (global)\n </Button>\n <Button\n variant=\"outline\"\n color=\"error\"\n onclick={() =>\n toast.error(\"Build failed\", {\n description: 'Uses the default animated error icon.',\n })}\n >\n Error (global)\n </Button>\n <Button\n variant=\"outline\"\n color=\"warning\"\n onclick={() =>\n toast.warning(\"Disk usage at 92%\", {\n description: 'Uses the default animated warning icon.',\n })}\n >\n Warning (global)\n </Button>\n <Button\n variant=\"outline\"\n color=\"info\"\n onclick={() =>\n toast.info(\"Maintenance scheduled\", {\n description: 'Uses the default animated info icon.',\n })}\n >\n Info (global)\n </Button>\n </div>\n </div>\n\n <!-- Per-toast icon override -->\n <div class=\"space-y-1.5\">\n <p class=\"text-sm font-medium\">Per-toast Icon Override</p>\n <p class=\"text-sm text-on-surface-variant\">\n Pass an Iconify icon name string, or\n <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >null</code\n >\n to hide the icon.\n </p>\n <div\n class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\"\n >\n <Button\n variant=\"outline\"\n onclick={() =>\n toast(\"Rocket launched!\", {\n description: 'icon: \"lucide:rocket\"',\n icon: \"lucide:rocket\",\n })}\n >\n Rocket\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast.success(\"Git push complete\", {\n description: 'icon: \"lucide:git-branch\"',\n icon: \"lucide:git-branch\",\n })}\n >\n Git Branch\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast.warning(\"Battery low\", {\n description: 'icon: \"lucide:battery-low\"',\n icon: \"lucide:battery-low\",\n })}\n >\n Battery\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast.error(\"Server offline\", {\n description: 'icon: \"lucide:server-off\"',\n icon: \"lucide:server-off\",\n })}\n >\n Server\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast.success(\"No icon toast\", {\n description: \"icon: null\",\n icon: null,\n })}\n >\n icon: null\n </Button>\n </div>\n </div>\n </section>\n\n <!-- Avatar -->\n <section class=\"space-y-3\">\n <h2 id=\"Avatar\" class=\"text-lg font-semibold\">\n <a\n href=\"#Avatar\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n Avatar\n </a>\n </h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use the <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >avatar</code\n >\n option to show an avatar in the icon slot.\n </p>\n <div\n class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\"\n >\n <Button\n variant=\"outline\"\n onclick={() =>\n toast(\"John commented on your post\", {\n description: '\"Great article! Thanks for sharing.\"',\n avatar: {\n src: \"https://i.pravatar.cc/150?img=1\",\n alt: \"John\",\n },\n })}\n >\n With Photo\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast.info(\"Jane sent you a message\", {\n description: '\"Hey, are you free for a call?\"',\n avatar: {\n src: \"https://i.pravatar.cc/150?img=5\",\n alt: \"Jane\",\n },\n })}\n >\n Info + Avatar\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast(\"Alex invited you to a project\", {\n description: \"Project: Svelora Design System\",\n avatar: { alt: \"Alex\" },\n action: {\n label: \"Accept\",\n onClick: () => toast.success(\"Invitation accepted\"),\n },\n })}\n >\n Initials + Action\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast.success(\"Team joined!\", {\n description: \"You are now a member of the Design team.\",\n avatar: { icon: \"lucide:users\" },\n })}\n >\n Icon Fallback\n </Button>\n </div>\n </section>\n\n <!-- Action & Cancel -->\n <section class=\"space-y-3\">\n <h2 id=\"Action--Cancel\" class=\"text-lg font-semibold\">\n <a\n href=\"#Action--Cancel\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n Action & Cancel\n </a>\n </h2>\n <div\n class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\"\n >\n <Button\n variant=\"outline\"\n onclick={() =>\n toast(\"File deleted\", {\n action: {\n label: \"Undo\",\n onClick: () => toast.success(\"File restored\"),\n },\n })}\n >\n Action Button\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast(\"Accept cookies?\", {\n action: {\n label: \"Accept\",\n onClick: () => toast.success(\"Cookies accepted\"),\n },\n cancel: {\n label: \"Decline\",\n onClick: () => toast(\"Cookies declined\"),\n },\n })}\n >\n Action + Cancel\n </Button>\n <Button\n variant=\"outline\"\n color=\"info\"\n onclick={() =>\n toast.info(\"New version available\", {\n action: {\n label: \"Update Now\",\n onClick: () => toast.success(\"Updating...\"),\n },\n cancel: {\n label: \"Later\",\n onClick: () => {},\n },\n })}\n >\n Info with Actions\n </Button>\n <Button\n variant=\"outline\"\n color=\"error\"\n onclick={() =>\n toast.error(\"Delete this item?\", {\n description: \"This action cannot be undone.\",\n action: {\n label: \"Delete\",\n onClick: () => toast.success(\"Item deleted\"),\n },\n cancel: {\n label: \"Keep\",\n onClick: () => {},\n },\n })}\n >\n Error with Confirm\n </Button>\n </div>\n </section>\n\n <!-- Promise -->\n <section class=\"space-y-3\">\n <h2 id=\"Promise\" class=\"text-lg font-semibold\">\n <a\n href=\"#Promise\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n Promise\n </a>\n </h2>\n <p class=\"text-sm text-on-surface-variant\">\n Automatically transitions between loading, success, and error\n states.\n </p>\n <div\n class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\"\n >\n <Button\n variant=\"outline\"\n onclick={() => {\n toast.promise(\n new Promise((resolve) => setTimeout(resolve, 2000)),\n {\n loading: \"Saving changes...\",\n success: \"Changes saved successfully!\",\n error: \"Failed to save changes\",\n },\n );\n }}\n >\n Save (Resolves)\n </Button>\n <Button\n variant=\"outline\"\n color=\"error\"\n onclick={() => {\n toast.promise(\n new Promise((_, reject) =>\n setTimeout(\n () => reject(new Error(\"timeout\")),\n 2000,\n ),\n ),\n {\n loading: \"Connecting to server...\",\n success: \"Connected!\",\n error: \"Connection failed\",\n },\n );\n }}\n >\n Connect (Rejects)\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => {\n toast.promise(\n new Promise((resolve) => setTimeout(resolve, 3000)),\n {\n loading: \"Uploading file (0%)...\",\n success: \"File uploaded!\",\n error: \"Upload failed\",\n },\n );\n }}\n >\n Upload File\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => {\n toast.promise(\n new Promise((resolve) => setTimeout(resolve, 4000)),\n {\n loading: \"Deploying to production...\",\n success: \"Deployed! Live at https://example.com\",\n error: \"Deploy failed — check CI logs\",\n },\n );\n }}\n >\n Deploy\n </Button>\n <Button\n variant=\"outline\"\n color=\"success\"\n onclick={() => {\n toast.promise(\n new Promise((resolve) => setTimeout(resolve, 2500)),\n {\n loading: \"Publishing...\",\n success: \"Published!\",\n error: \"Publish failed\",\n },\n { color: \"success\" },\n );\n }}\n >\n Promise + color\n </Button>\n </div>\n </section>\n\n <!-- Close Button -->\n <section class=\"space-y-3\">\n <h2 id=\"Close-Button\" class=\"text-lg font-semibold\">\n <a\n href=\"#Close-Button\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n Close Button\n </a>\n </h2>\n <p class=\"text-sm text-on-surface-variant\">\n Enable globally via\n <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >&lt;Toaster closeButton /&gt;</code\n >\n or per toast with\n <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >closeButton: true</code\n >.\n <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >dismissible: false</code\n >\n always hides the close button.\n </p>\n <div\n class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\"\n >\n <Button\n variant=\"outline\"\n onclick={() =>\n toast(\"With close button\", {\n description: \"closeButton: true on this toast\",\n closeButton: true,\n duration: Infinity,\n })}\n >\n Force close button\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast(\"No close button\", {\n description:\n \"closeButton: false overrides global toggle\",\n closeButton: false,\n duration: Infinity,\n })}\n >\n Hide close button\n </Button>\n </div>\n </section>\n\n <!-- Duration -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-Duration\" class=\"text-lg font-semibold\">\n <a\n href=\"#Custom-Duration\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n Custom Duration\n </a>\n </h2>\n <p class=\"text-sm text-on-surface-variant\">\n Control how long a toast stays visible before auto-dismiss.\n </p>\n <div\n class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\"\n >\n <Button\n variant=\"outline\"\n onclick={() => toast(\"Gone in a flash\", { duration: 1000 })}\n >\n 1 second\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => toast(\"Standard duration\", { duration: 3000 })}\n >\n 3 seconds\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => toast(\"Longer reading time\", { duration: 8000 })}\n >\n 8 seconds\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast(\"This toast will stay until dismissed\", {\n duration: Infinity,\n })}\n >\n Persistent (Infinity)\n </Button>\n </div>\n </section>\n\n <!-- Update Existing Toast -->\n <section class=\"space-y-3\">\n <h2 id=\"Update-Existing-Toast\" class=\"text-lg font-semibold\">\n <a\n href=\"#Update-Existing-Toast\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n Update Existing Toast\n </a>\n </h2>\n <p class=\"text-sm text-on-surface-variant\">\n Reuse the same ID to update an existing toast in-place.\n </p>\n <div\n class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\"\n >\n <Button\n variant=\"outline\"\n onclick={() => {\n const id = toast.loading(\"Uploading...\", {\n duration: Infinity,\n });\n setTimeout(\n () => toast.success(\"Upload complete!\", { id }),\n 2000,\n );\n }}\n >\n Loading → Success\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => {\n const id = toast.loading(\"Validating...\", {\n duration: Infinity,\n });\n setTimeout(\n () => toast.error(\"Validation failed!\", { id }),\n 2000,\n );\n }}\n >\n Loading → Error\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => {\n const id = toast(\"Step 1 of 3...\", { duration: Infinity });\n setTimeout(\n () =>\n toast(\"Step 2 of 3...\", { id, duration: Infinity }),\n 1000,\n );\n setTimeout(\n () =>\n toast(\"Step 3 of 3...\", { id, duration: Infinity }),\n 2000,\n );\n setTimeout(\n () => toast.success(\"All steps complete!\", { id }),\n 3000,\n );\n }}\n >\n Multi-step Progress\n </Button>\n </div>\n </section>\n\n <!-- Deduplicated -->\n <section class=\"space-y-3\">\n <h2 id=\"Deduplicated-Toasts\" class=\"text-lg font-semibold\">\n <a\n href=\"#Deduplicated-Toasts\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n Deduplicated Toasts\n </a>\n </h2>\n <p class=\"text-sm text-on-surface-variant\">\n Pass the same <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >id</code\n >\n to prevent duplicate toasts. Clicking multiple times updates the existing\n toast instead of creating new ones.\n </p>\n <div\n class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\"\n >\n <Button\n variant=\"outline\"\n onclick={() =>\n toast(\"You have 3 new messages\", {\n id: \"new-messages\",\n description: \"Click again — no duplicate!\",\n })}\n >\n Same ID (click many times)\n </Button>\n <Button\n variant=\"outline\"\n color=\"warning\"\n onclick={() =>\n toast.warning(\"Rate limit reached\", {\n id: \"rate-limit\",\n description: \"Try again in 30 seconds.\",\n })}\n >\n Rate Limit (deduplicated)\n </Button>\n </div>\n </section>\n\n <!-- Dismiss -->\n <section class=\"space-y-3\">\n <h2 id=\"Programmatic-Dismiss\" class=\"text-lg font-semibold\">\n <a\n href=\"#Programmatic-Dismiss\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n Programmatic Dismiss\n </a>\n </h2>\n <div\n class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\"\n >\n <Button\n variant=\"outline\"\n onclick={() => {\n const id = toast(\"Processing...\", { duration: Infinity });\n setTimeout(() => {\n toast.dismiss(id);\n toast.success(\"Done!\");\n }, 2000);\n }}\n >\n Auto Dismiss After 2s\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => {\n toast(\"Toast 1\", { duration: Infinity });\n toast.success(\"Toast 2\", { duration: Infinity });\n toast.error(\"Toast 3\", { duration: Infinity });\n }}\n >\n Create 3 Toasts\n </Button>\n <Button\n variant=\"outline\"\n color=\"error\"\n onclick={() => toast.dismiss()}\n >\n Dismiss All\n </Button>\n </div>\n </section>\n\n <!-- Stacking -->\n <section class=\"space-y-3\">\n <h2 id=\"Stacking\" class=\"text-lg font-semibold\">\n <a\n href=\"#Stacking\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n Stacking\n </a>\n </h2>\n <p class=\"text-sm text-on-surface-variant\">\n Fire multiple toasts to see them stack. Hover the toaster area to\n pause auto-dismiss timers while you read.\n </p>\n <div\n class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\"\n >\n <Button\n variant=\"outline\"\n onclick={() => {\n counter++;\n toast.success(`Notification #${counter}`, {\n description: \"Hover the toaster to pause dismissal.\",\n });\n }}\n >\n Add Toast (#{counter + 1})\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => {\n toast.info(\"Info toast\", { description: \"First in stack\" });\n setTimeout(\n () =>\n toast.warning(\"Warning toast\", {\n description: \"Second in stack\",\n }),\n 300,\n );\n setTimeout(\n () =>\n toast.error(\"Error toast\", {\n description: \"Third in stack\",\n }),\n 600,\n );\n }}\n >\n Fire 3 Different Types\n </Button>\n </div>\n </section>\n\n <!-- Non-dismissible -->\n <section class=\"space-y-3\">\n <h2 id=\"Non-dismissible\" class=\"text-lg font-semibold\">\n <a\n href=\"#Non-dismissible\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n Non-dismissible\n </a>\n </h2>\n <p class=\"text-sm text-on-surface-variant\">\n Keep a toast on screen until it is updated or dismissed\n programmatically. Use\n <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >dismissible: false</code\n >\n (hides close button + infinite duration) or\n <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >duration: Infinity</code\n >\n alone.\n </p>\n <div\n class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\"\n >\n <Button\n variant=\"outline\"\n onclick={() => {\n const id = toast.warning(\"Processing payment...\", {\n description: \"Please do not close this page.\",\n dismissible: false,\n duration: Infinity,\n });\n setTimeout(() => {\n toast.success(\"Payment complete!\", { id });\n }, 3000);\n }}\n >\n Non-dismissible (3s)\n </Button>\n </div>\n </section>\n\n <!-- All Types at Once (visual test) -->\n <section class=\"space-y-3\">\n <h2 id=\"All-Types-at-Once\" class=\"text-lg font-semibold\">\n <a\n href=\"#All-Types-at-Once\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n All Types at Once\n </a>\n </h2>\n <p class=\"text-sm text-on-surface-variant\">\n Fire every type simultaneously to visually compare styling.\n </p>\n <div\n class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\"\n >\n <Button\n variant=\"outline\"\n onclick={() => {\n toast(\"Default toast\", {\n description: \"Neutral surface styling\",\n duration: 10000,\n });\n toast.success(\"Success toast\", {\n description: \"Operation successful\",\n duration: 10000,\n });\n toast.error(\"Error toast\", {\n description: \"Something went wrong\",\n duration: 10000,\n });\n toast.warning(\"Warning toast\", {\n description: \"Proceed with caution\",\n duration: 10000,\n });\n toast.info(\"Info toast\", {\n description: \"Here is some context\",\n duration: 10000,\n });\n toast.loading(\"Loading toast\", {\n description: \"Please wait...\",\n duration: 10000,\n });\n }}\n >\n Fire All Types (10s)\n </Button>\n </div>\n </section>\n\n <!-- Real World Examples -->\n <section class=\"space-y-3\">\n <h2 id=\"Real-World-Examples\" class=\"text-lg font-semibold\">\n <a\n href=\"#Real-World-Examples\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n Real World Examples\n </a>\n </h2>\n <div\n class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\"\n >\n <Button\n variant=\"outline\"\n onclick={() =>\n toast.success(\"Profile Updated\", {\n description: \"Your profile changes have been saved.\",\n })}\n >\n Save Profile\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast.error(\"Permission Denied\", {\n description: \"You do not have access to this resource.\",\n action: {\n label: \"Request Access\",\n onClick: () =>\n toast.info(\"Access request sent to admin\"),\n },\n })}\n >\n Access Denied\n </Button>\n <Button\n variant=\"outline\"\n onclick={() =>\n toast.warning(\"Session Expiring\", {\n description: \"Your session will expire in 5 minutes.\",\n duration: 8000,\n action: {\n label: \"Extend\",\n onClick: () =>\n toast.success(\"Session extended by 30 minutes\"),\n },\n })}\n >\n Session Warning\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => {\n toast.promise(\n new Promise((resolve) => setTimeout(resolve, 3000)),\n {\n loading: \"Sending email...\",\n success: \"Email sent to john@example.com\",\n error: \"Failed to send email\",\n },\n );\n }}\n >\n Send Email\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => {\n const id = toast(\"Item moved to trash\", {\n action: {\n label: \"Undo\",\n onClick: () => toast.success(\"Item restored\"),\n },\n cancel: {\n label: \"Delete permanently\",\n onClick: () => {\n toast.dismiss(id);\n toast.error(\"Item permanently deleted\");\n },\n },\n });\n }}\n >\n Move to Trash\n </Button>\n <Button\n variant=\"outline\"\n onclick={() => {\n toast.info(\"New comment on your post\", {\n description:\n '\"Great article! Thanks for sharing.\" — Jane',\n action: {\n label: \"View\",\n onClick: () => toast(\"Opening post...\"),\n },\n });\n }}\n >\n New Comment\n </Button>\n </div>\n </section>\n\n <!-- API Reference -->\n <section class=\"space-y-3\">\n <h2 id=\"API-Reference\" class=\"text-lg font-semibold\">\n <a\n href=\"#API-Reference\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n API Reference\n </a>\n </h2>\n <div class=\"grid gap-4 lg:grid-cols-2\">\n <Card class=\"border border-outline-variant/70\">\n <div class=\"space-y-4\">\n <h3 class=\"text-base font-semibold\">toast() methods</h3>\n <div class=\"space-y-3\">\n {#each toastMethodsReference as item (item.name)}\n <div\n class=\"border-b border-outline-variant/60 pb-3 last:border-b-0 last:pb-0\"\n >\n <p class=\"font-mono text-xs text-primary\">\n {item.name}\n </p>\n <p class=\"mt-1 text-sm text-on-surface-variant\">\n {item.description}\n </p>\n </div>\n {/each}\n </div>\n </div>\n </Card>\n <Card class=\"border border-outline-variant/70\">\n <div class=\"space-y-4\">\n <h3 class=\"text-base font-semibold\">Toast options</h3>\n <div class=\"space-y-3\">\n {#each toastOptionsReference as item (item.name)}\n <div\n class=\"border-b border-outline-variant/60 pb-3 last:border-b-0 last:pb-0\"\n >\n <p\n class=\"font-mono text-xs text-on-surface-variant\"\n >\n {item.name}\n </p>\n <p class=\"mt-1 text-sm font-medium\">\n {item.type}\n </p>\n <p class=\"mt-1 text-sm text-on-surface-variant\">\n {item.description}\n </p>\n </div>\n {/each}\n </div>\n </div>\n </Card>\n </div>\n <Card class=\"border border-outline-variant/70\">\n <div class=\"space-y-4\">\n <h3 class=\"text-base font-semibold\">Toaster props</h3>\n <div class=\"grid gap-3 md:grid-cols-2\">\n {#each toasterPropsReference as item (item.name)}\n <div\n class=\"rounded-xl border border-outline-variant/60 p-4\"\n >\n <p\n class=\"font-mono text-xs text-on-surface-variant\"\n >\n {item.name}\n </p>\n <p class=\"mt-1 text-sm font-medium\">{item.type}</p>\n <p class=\"mt-1 text-sm text-on-surface-variant\">\n {item.description}\n </p>\n </div>\n {/each}\n </div>\n </div>\n </Card>\n </section>\n</div>\n\n<!-- Dynamic Toaster with configurable props -->\n<Toaster\n variant={activeVariant}\n position={activePosition}\n theme={activeTheme}\n gap={activeGap}\n closeButton={showCloseButton}\n/>\n",
162
167
  "spotlight": "<script lang=\"ts\">\n import { Spotlight } from '$lib/index.js'\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">Spotlight</h1>\n <p class=\"text-on-surface-variant\">\n A hover effect that follows your mouse cursor around the container.\n </p>\n </div>\n\n <!-- Usage -->\n <section class=\"space-y-3\">\n <h2 id=\"Usage\" class=\"text-lg font-semibold\">\n<a href=\"#Usage\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Usage\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Wrap your content in a <code class=\"rounded bg-surface-container-highest px-1\">Spotlight</code> component. Hover over the card to see the effect.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4 flex justify-center\">\n <Spotlight class=\"w-full max-w-sm rounded-xl border border-outline bg-surface-50 dark:bg-surface-900 p-8\">\n <h3 class=\"text-xl font-bold mb-2\">Interactive Card</h3>\n <p class=\"text-surface-500\">\n The spotlight follows your cursor, creating a subtle, premium look for modern interfaces.\n </p>\n </Spotlight>\n </div>\n </section>\n \n <!-- Custom Color -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-Color--Size\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-Color--Size\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Color & Size\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1\">color</code> and <code class=\"rounded bg-surface-container-highest px-1\">size</code> props to adjust the spotlight appearance.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4 flex gap-4 justify-center flex-wrap\">\n <Spotlight \n color=\"rgba(59, 130, 246, 0.2)\" \n size={300}\n class=\"w-full max-w-[200px] rounded-xl border border-outline bg-surface-900 text-white p-6\"\n >\n <div class=\"font-semibold text-primary-300\">Blue Spotlight</div>\n </Spotlight>\n \n <Spotlight \n color=\"rgba(16, 185, 129, 0.2)\" \n size={500}\n class=\"w-full max-w-[200px] rounded-xl border border-outline bg-surface-900 text-white p-6\"\n >\n <div class=\"font-semibold text-success-400\">Large Green Spotlight</div>\n </Spotlight>\n </div>\n </section>\n</div>\n",
163
168
  "breadcrumb": "<script lang=\"ts\">\n import { Breadcrumb, Icon, Link, Separator } from '$lib/index.js'\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">Breadcrumb</h1>\n <p class=\"text-on-surface-variant\">\n Display a hierarchy of navigation links to show the user's current location within a\n site.\n </p>\n </div>\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Breadcrumb\n items={[\n { label: 'Home', href: '/' },\n { label: 'Products', href: '/products' },\n { label: 'Laptops', href: '/products/laptops' },\n { label: 'MacBook Pro' }\n ]}\n />\n </div>\n </section>\n\n <!-- With Icons -->\n <section class=\"space-y-3\">\n <h2 id=\"With-Icons\" class=\"text-lg font-semibold\">\n<a href=\"#With-Icons\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n With Icons\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Breadcrumb\n items={[\n { label: 'Home', href: '/', icon: 'lucide:home' },\n { label: 'Settings', href: '/settings', icon: 'lucide:settings' },\n { label: 'Profile', icon: 'lucide:user' }\n ]}\n />\n </div>\n </section>\n\n <!-- Separator Icon -->\n <section class=\"space-y-3\">\n <h2 id=\"Separator-Icon\" class=\"text-lg font-semibold\">\n<a href=\"#Separator-Icon\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Separator Icon\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Customize the separator between items via the\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >separatorIcon</code\n > prop.\n </p>\n <div class=\"grid gap-4 sm:grid-cols-2\">\n {#each [{ icon: 'lucide:chevron-right', name: 'Chevron (default)' }, { icon: 'lucide:slash', name: 'Slash' }, { icon: 'lucide:arrow-right', name: 'Arrow' }, { icon: 'lucide:dot', name: 'Dot' }] as sep (sep.icon)}\n <div class=\"space-y-1 rounded-lg bg-surface-container-high p-4\">\n <p class=\"text-xs font-medium text-on-surface-variant\">{sep.name}</p>\n <Breadcrumb\n separatorIcon={sep.icon}\n items={[\n { label: 'Home', href: '/' },\n { label: 'Docs', href: '/docs' },\n { label: 'API' }\n ]}\n />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Separator Snippet -->\n <section class=\"space-y-3\">\n <h2 id=\"Separator-Snippet\" class=\"text-lg font-semibold\">\n<a href=\"#Separator-Snippet\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Separator Snippet\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use the\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">separator</code\n > snippet for fully custom separator content.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Breadcrumb\n items={[\n { label: 'Home', href: '/' },\n { label: 'Category', href: '/category' },\n { label: 'Current Page' }\n ]}\n >\n {#snippet separator()}\n <span class=\"text-sm text-on-surface-variant/40\">/</span>\n {/snippet}\n </Breadcrumb>\n </div>\n </section>\n\n <!-- Disabled Items -->\n <section class=\"space-y-3\">\n <h2 id=\"Disabled-Items\" class=\"text-lg font-semibold\">\n<a href=\"#Disabled-Items\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Disabled Items\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Breadcrumb\n items={[\n { label: 'Home', href: '/' },\n { label: 'Archived', href: '/archived', disabled: true },\n { label: 'Old Post', href: '/archived/old-post', disabled: true },\n { label: 'Detail' }\n ]}\n />\n </div>\n </section>\n\n <!-- Custom Item Snippet -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-Item-Snippet\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-Item-Snippet\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Item Snippet\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use the\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">item</code>\n snippet for fully custom item rendering.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Breadcrumb\n items={[\n { label: 'Home', href: '/', icon: 'lucide:home' },\n { label: 'Projects', href: '/projects', icon: 'lucide:folder' },\n { label: 'svelora', icon: 'lucide:package' }\n ]}\n >\n {#snippet item({ item: crumb, active })}\n {#if active}\n <span\n class=\"inline-flex items-center gap-1.5 rounded-full bg-primary/10 px-3 py-1 text-sm font-semibold text-primary\"\n >\n {#if crumb.icon}\n <Icon name={crumb.icon} size=\"14\" />\n {/if}\n {crumb.label}\n </span>\n {:else}\n <Link\n href={crumb.href ?? ''}\n raw\n class=\"inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-sm text-on-surface-variant transition-colors hover:bg-surface-container-highest\"\n >\n {#if crumb.icon}\n <Icon name={crumb.icon} size=\"14\" />\n {/if}\n {crumb.label}\n </Link>\n {/if}\n {/snippet}\n </Breadcrumb>\n </div>\n </section>\n\n <!-- UI Slot Overrides -->\n <section class=\"space-y-3\">\n <h2 id=\"UI-Slot-Overrides\" class=\"text-lg font-semibold\">\n<a href=\"#UI-Slot-Overrides\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n UI Slot Overrides\n </a>\n</h2>\n <div class=\"grid gap-4 lg:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Custom active color</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Breadcrumb\n ui={{ link: 'text-on-surface font-semibold' }}\n items={[\n { label: 'Dashboard', href: '/' },\n { label: 'Analytics', href: '/analytics' },\n { label: 'Revenue' }\n ]}\n />\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Larger separator</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Breadcrumb\n ui={{ separatorIcon: 'size-6 text-primary/40' }}\n items={[\n { label: 'Home', href: '/' },\n { label: 'Blog', href: '/blog' },\n { label: 'Latest Post' }\n ]}\n />\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">With background container</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Breadcrumb\n ui={{ root: 'bg-surface-container rounded-lg px-4 py-2' }}\n items={[\n { label: 'Home', href: '/' },\n { label: 'Shop', href: '/shop' },\n { label: 'Electronics', href: '/shop/electronics' },\n { label: 'Phones' }\n ]}\n />\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Wider gap</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Breadcrumb\n ui={{ list: 'gap-3' }}\n items={[\n { label: 'Home', href: '/' },\n { label: 'Team', href: '/team' },\n { label: 'Members' }\n ]}\n />\n </div>\n </div>\n </div>\n </section>\n\n <Separator />\n\n <!-- Real World Examples -->\n <section class=\"space-y-6\">\n <h2 id=\"Real-World-Examples\" class=\"text-lg font-semibold\">\n<a href=\"#Real-World-Examples\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World Examples\n </a>\n</h2>\n\n <div class=\"grid gap-4 md:grid-cols-2\">\n <!-- File Browser -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium\">File Browser</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Breadcrumb\n items={[\n { label: 'Root', href: '/', icon: 'lucide:hard-drive' },\n { label: 'Users', href: '/users', icon: 'lucide:users' },\n { label: 'Documents', href: '/documents', icon: 'lucide:folder' },\n { label: 'README.md', icon: 'lucide:file-text' }\n ]}\n />\n </div>\n </div>\n\n <!-- E-commerce -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium\">E-commerce</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Breadcrumb\n items={[\n { label: 'Store', href: '/' },\n { label: 'Electronics', href: '/electronics' },\n { label: 'Laptops', href: '/electronics/laptops' },\n { label: 'ASUS ROG Strix' }\n ]}\n />\n </div>\n </div>\n\n <!-- Admin Dashboard -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium\">Admin Dashboard</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Breadcrumb\n ui={{ root: 'bg-surface-container rounded-lg px-4 py-2.5' }}\n items={[\n { label: 'Admin', href: '/admin', icon: 'lucide:shield' },\n { label: 'Users', href: '/admin/users', icon: 'lucide:users' },\n { label: 'Permissions', icon: 'lucide:lock' }\n ]}\n />\n </div>\n </div>\n\n <!-- Documentation -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium\">Documentation</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Breadcrumb\n separatorIcon=\"lucide:slash\"\n items={[\n { label: 'Docs', href: '/docs', icon: 'lucide:book-open' },\n { label: 'Components', href: '/docs/components' },\n { label: 'Breadcrumb' }\n ]}\n />\n </div>\n </div>\n </div>\n </section>\n</div>\n",
164
169
  "pagination": "<script lang=\"ts\">\n import { Pagination, Button, Separator } from '$lib/index.js'\n\n const colors = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'success',\n 'warning',\n 'error',\n 'info',\n 'surface'\n ] as const\n const sizes = ['xs', 'sm', 'md', 'lg', 'xl'] as const\n const activeVariants = ['solid', 'outline', 'soft', 'subtle', 'ghost'] as const\n const navVariants = ['ghost', 'outline', 'soft', 'subtle', 'solid', 'link'] as const\n\n let controlledPage = $state(5)\n let callbackPage = $state(1)\n let callbackLog = $state('')\n</script>\n\n<div class=\"space-y-8\">\n <h1 class=\"text-2xl font-bold text-on-surface\">Pagination</h1>\n\n <!-- Basic -->\n <section class=\"space-y-4\">\n <h2 id=\"Basic-Usage\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Basic-Usage\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic Usage\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Simple pagination with previous/next controls and page numbers.\n </p>\n <Pagination total={100} itemsPerPage={10} />\n </section>\n\n <!-- Default Page -->\n <section class=\"space-y-4\">\n <h2 id=\"Default-Page\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Default-Page\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Default Page\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Set the initial page with <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">defaultPage</code\n >.\n </p>\n <Pagination total={100} itemsPerPage={10} defaultPage={5} />\n </section>\n\n <!-- Show Edges -->\n <section class=\"space-y-4\">\n <h2 id=\"Show-Edges\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Show-Edges\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Show Edges\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Display first/last page buttons with <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">showEdges</code\n >.\n </p>\n <Pagination total={100} itemsPerPage={10} showEdges />\n </section>\n\n <!-- Without Controls -->\n <section class=\"space-y-4\">\n <h2 id=\"Without-Controls\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Without-Controls\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Without Controls\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Hide prev/next buttons with <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >showControls={'{false}'}</code\n >.\n </p>\n <div class=\"space-y-3\">\n <div>\n <p class=\"mb-1 text-xs text-on-surface-variant\">Pages only</p>\n <Pagination total={100} itemsPerPage={10} showControls={false} />\n </div>\n <div>\n <p class=\"mb-1 text-xs text-on-surface-variant\">With edges, no prev/next</p>\n <Pagination total={100} itemsPerPage={10} showEdges showControls={false} />\n </div>\n </div>\n </section>\n\n <!-- Sibling Count -->\n <section class=\"space-y-4\">\n <h2 id=\"Sibling-Count\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Sibling-Count\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Sibling Count\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Control visible siblings around current page with <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >siblingCount</code\n >.\n </p>\n <div class=\"space-y-3\">\n {#each [0, 1, 2] as count (count)}\n <div>\n <p class=\"mb-1 text-xs text-on-surface-variant\">\n siblingCount={count}{count === 1 ? ' (default)' : ''}\n </p>\n <Pagination total={100} itemsPerPage={10} page={5} siblingCount={count} />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Items Per Page -->\n <section class=\"space-y-4\">\n <h2 id=\"Items-Per-Page\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Items-Per-Page\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Items Per Page\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Same total (100) with different <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >itemsPerPage</code\n > values.\n </p>\n <div class=\"space-y-3\">\n {#each [5, 10, 25, 50] as perPage (perPage)}\n <div>\n <p class=\"mb-1 text-xs text-on-surface-variant\">\n {perPage} items/page ({Math.ceil(100 / perPage)} pages)\n </p>\n <Pagination total={100} itemsPerPage={perPage} />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Controlled State -->\n <section class=\"space-y-4\">\n <h2 id=\"Controlled-State\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Controlled-State\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Controlled State\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >bind:page</code\n >\n for two-way binding. Current page:\n <strong class=\"text-on-surface\">{controlledPage}</strong>\n </p>\n <Pagination total={200} itemsPerPage={10} bind:page={controlledPage} showEdges />\n <div class=\"flex gap-2\">\n {#each [1, 10, 20] as p (p)}\n <Button\n variant=\"solid\"\n color=\"primary\"\n size=\"sm\"\n label=\"Go to page {p}\"\n onclick={() => (controlledPage = p)}\n />\n {/each}\n </div>\n </section>\n\n <!-- Active Colors -->\n <section class=\"space-y-4\">\n <h2 id=\"Active-Colors\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Active-Colors\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Active Colors\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Customize the active page color with <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">activeColor</code\n >.\n </p>\n <div class=\"space-y-3\">\n {#each colors as color (color)}\n <div class=\"flex items-center gap-4\">\n <span class=\"w-20 text-sm text-on-surface-variant\">{color}</span>\n <Pagination total={100} itemsPerPage={10} page={3} activeColor={color} />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Active Variant -->\n <section class=\"space-y-4\">\n <h2 id=\"Active-Variant\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Active-Variant\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Active Variant\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Change the selected page style with <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >activeVariant</code\n >.\n </p>\n <div class=\"space-y-3\">\n {#each activeVariants as av (av)}\n <div class=\"flex items-center gap-4\">\n <span class=\"w-16 text-sm text-on-surface-variant\">{av}</span>\n <Pagination total={100} itemsPerPage={10} page={3} activeVariant={av} />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Nav Button Variant -->\n <section class=\"space-y-4\">\n <h2 id=\"Nav-Button-Variant\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Nav-Button-Variant\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Nav Button Variant\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Change navigation button style with <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">variant</code\n >\n and\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">color</code>.\n </p>\n <div class=\"space-y-3\">\n {#each navVariants as v (v)}\n <div class=\"flex items-center gap-4\">\n <span class=\"w-16 text-sm text-on-surface-variant\">{v}</span>\n <Pagination\n total={100}\n itemsPerPage={10}\n page={3}\n variant={v}\n color=\"primary\"\n showEdges\n />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Sizes -->\n <section class=\"space-y-4\">\n <h2 id=\"Sizes\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Sizes\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Sizes\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Control the pagination size with <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">size</code\n >.\n </p>\n <div class=\"space-y-4\">\n {#each sizes as size (size)}\n <div class=\"flex items-center gap-4\">\n <span class=\"w-8 text-sm text-on-surface-variant\">{size}</span>\n <Pagination total={100} itemsPerPage={10} {size} showEdges />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Disabled -->\n <section class=\"space-y-4\">\n <h2 id=\"Disabled\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Disabled\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Disabled\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Disable all controls with <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">disabled</code\n >.\n </p>\n <Pagination total={100} itemsPerPage={10} page={3} disabled showEdges />\n </section>\n\n <!-- Page Change Callback -->\n <section class=\"space-y-4\">\n <h2 id=\"Page-Change-Callback\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Page-Change-Callback\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Page Change Callback\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Listen for page changes with <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >onPageChange</code\n >. Current page: <strong class=\"text-on-surface\">{callbackPage}</strong>\n </p>\n <Pagination\n total={100}\n itemsPerPage={10}\n bind:page={callbackPage}\n onPageChange={(p) => (callbackLog = `Navigated to page ${p}`)}\n showEdges\n />\n {#if callbackLog}\n <p class=\"text-xs text-on-surface-variant\">{callbackLog}</p>\n {/if}\n </section>\n\n <!-- Custom Icons -->\n <section class=\"space-y-4\">\n <h2 id=\"Custom-Icons\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Custom-Icons\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Icons\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Customize navigation icons with icon props.</p>\n <Pagination\n total={100}\n itemsPerPage={10}\n page={5}\n showEdges\n prevIcon=\"lucide:arrow-left\"\n nextIcon=\"lucide:arrow-right\"\n ellipsisIcon=\"lucide:more-horizontal\"\n />\n </section>\n\n <!-- UI Slot Overrides -->\n <section class=\"space-y-4\">\n <h2 id=\"UI-Slot-Overrides\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#UI-Slot-Overrides\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n UI Slot Overrides\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Customize individual parts with <code\n class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">ui</code\n >.\n </p>\n <div class=\"space-y-4\">\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Rounded buttons</p>\n <Pagination\n total={100}\n itemsPerPage={10}\n page={3}\n showEdges\n ui={{\n item: 'rounded-full',\n first: 'rounded-full',\n prev: 'rounded-full',\n next: 'rounded-full',\n last: 'rounded-full'\n }}\n />\n </div>\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Custom gap &amp; root</p>\n <Pagination\n total={100}\n itemsPerPage={10}\n page={3}\n class=\"rounded-lg border border-outline-variant bg-surface-container p-3\"\n ui={{ list: 'gap-2' }}\n />\n </div>\n </div>\n </section>\n\n <!-- Custom Snippet Slots -->\n <section class=\"space-y-4\">\n <h2 id=\"Custom-Snippet-Slots\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Custom-Snippet-Slots\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Snippet Slots\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Override individual parts with snippet slots.</p>\n <div class=\"space-y-4\">\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Custom item slot (rounded)</p>\n <Pagination total={50} itemsPerPage={10} page={2}>\n {#snippet itemSlot({ page, selected })}\n <span\n class=\"inline-flex size-9 items-center justify-center rounded-full text-sm font-bold {selected\n ? 'bg-primary text-on-primary'\n : 'text-on-surface-variant'}\"\n >\n {page}\n </span>\n {/snippet}\n </Pagination>\n </div>\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Custom prev/next text</p>\n <Pagination total={100} itemsPerPage={10} page={3}>\n {#snippet prevSlot({ disabled })}\n <span\n class=\"text-sm font-medium {disabled\n ? 'text-on-surface-variant/50'\n : 'text-primary'}\"\n >\n Previous\n </span>\n {/snippet}\n {#snippet nextSlot({ disabled })}\n <span\n class=\"text-sm font-medium {disabled\n ? 'text-on-surface-variant/50'\n : 'text-primary'}\"\n >\n Next\n </span>\n {/snippet}\n </Pagination>\n </div>\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Custom ellipsis</p>\n <Pagination total={100} itemsPerPage={10} page={5}>\n {#snippet ellipsisSlot()}\n <span class=\"text-on-surface-variant\">---</span>\n {/snippet}\n </Pagination>\n </div>\n </div>\n </section>\n\n <!-- Edge Cases -->\n <section class=\"space-y-4\">\n <h2 id=\"Edge-Cases\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Edge-Cases\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Edge Cases\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Handling special scenarios.</p>\n <div class=\"space-y-3\">\n <div>\n <p class=\"mb-1 text-xs text-on-surface-variant\">Single page (5 items, 10/page)</p>\n <Pagination total={5} itemsPerPage={10} showEdges />\n </div>\n <div>\n <p class=\"mb-1 text-xs text-on-surface-variant\">Few pages (30 items, 10/page)</p>\n <Pagination total={30} itemsPerPage={10} showEdges />\n </div>\n <div>\n <p class=\"mb-1 text-xs text-on-surface-variant\">Many pages (1000 items, 10/page)</p>\n <Pagination total={1000} itemsPerPage={10} page={50} showEdges />\n </div>\n <div>\n <p class=\"mb-1 text-xs text-on-surface-variant\">Zero total</p>\n <Pagination total={0} itemsPerPage={10} />\n </div>\n </div>\n </section>\n\n <Separator />\n\n <!-- Real World Example -->\n <section class=\"space-y-4\">\n <h2 id=\"Real-World-Examples\" class=\"text-lg font-semibold text-on-surface\">\n<a href=\"#Real-World-Examples\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World Examples\n </a>\n</h2>\n\n <div class=\"space-y-6\">\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Table-style pagination</p>\n <div class=\"rounded-lg border border-outline-variant bg-surface-container-low p-4\">\n <div class=\"mb-3 text-sm text-on-surface-variant\">\n Showing <strong class=\"text-on-surface\">41-50</strong> of\n <strong class=\"text-on-surface\">200</strong> results\n </div>\n <Pagination\n total={200}\n itemsPerPage={10}\n page={5}\n showEdges\n size=\"sm\"\n activeColor=\"primary\"\n />\n </div>\n </div>\n\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Minimal blog pagination</p>\n <div class=\"flex items-center justify-center\">\n <Pagination\n total={50}\n itemsPerPage={5}\n page={3}\n showControls={false}\n activeColor=\"surface\"\n ui={{ item: 'rounded-full' }}\n />\n </div>\n </div>\n\n <div>\n <p class=\"mb-2 text-xs text-on-surface-variant\">Combined features</p>\n <Pagination\n total={200}\n itemsPerPage={10}\n bind:page={controlledPage}\n activeColor=\"success\"\n size=\"lg\"\n showEdges\n siblingCount={2}\n ui={{\n item: 'rounded-full',\n first: 'rounded-full',\n prev: 'rounded-full',\n next: 'rounded-full',\n last: 'rounded-full',\n list: 'gap-1.5'\n }}\n />\n <p class=\"mt-2 text-xs text-on-surface-variant\">\n Page <strong class=\"text-on-surface\">{controlledPage}</strong> | Size: lg | Color:\n success | Edges | Siblings: 2 | Rounded\n </p>\n </div>\n </div>\n </section>\n</div>\n",
@@ -168,6 +173,7 @@
168
173
  "collapsible": "<script lang=\"ts\">\n import { Collapsible, Button, Icon, Badge, Separator } from '$lib/index.js'\n\n let basicOpen = $state(false)\n let controlledOpen = $state(true)\n</script>\n\n<div class=\"mx-auto max-w-3xl space-y-12 p-8\">\n <div>\n <h1 class=\"text-2xl font-bold\">Collapsible</h1>\n <p class=\"mt-1 text-on-surface-variant\">\n An interactive component that expands/collapses content.\n </p>\n </div>\n\n <Separator />\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <Collapsible>\n {#snippet trigger({ open, props })}\n <Button\n {...props}\n variant=\"subtle\"\n trailingIcon={open ? 'lucide:chevron-up' : 'lucide:chevron-down'}\n >\n {open ? 'Hide' : 'Show'} content\n </Button>\n {/snippet}\n {#snippet content()}\n <div class=\"mt-2 rounded-lg border border-outline-variant p-4 text-sm\">\n <p>This is the collapsible content. It can contain any elements you need.</p>\n </div>\n {/snippet}\n </Collapsible>\n </section>\n\n <Separator />\n\n <!-- Initially Open -->\n <section class=\"space-y-3\">\n <h2 id=\"Initially-Open\" class=\"text-lg font-semibold\">\n<a href=\"#Initially-Open\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Initially Open\n </a>\n</h2>\n <Collapsible open>\n {#snippet trigger({ open, props })}\n <Button\n {...props}\n variant=\"outline\"\n trailingIcon={open ? 'lucide:minus' : 'lucide:plus'}\n >\n Toggle section\n </Button>\n {/snippet}\n {#snippet content()}\n <div class=\"mt-2 rounded-lg bg-surface-container p-4 text-sm\">\n <p>This section is open by default when the page loads.</p>\n </div>\n {/snippet}\n </Collapsible>\n </section>\n\n <Separator />\n\n <!-- Controlled -->\n <section class=\"space-y-3\">\n <h2 id=\"Controlled-bindopen\" class=\"text-lg font-semibold\">\n<a href=\"#Controlled-bindopen\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Controlled (bind:open)\n </a>\n</h2>\n <div class=\"flex items-center gap-2\">\n <Badge\n label={controlledOpen ? 'Open' : 'Closed'}\n color={controlledOpen ? 'success' : 'error'}\n />\n <Button variant=\"ghost\" size=\"xs\" onclick={() => (controlledOpen = !controlledOpen)}>\n Toggle externally\n </Button>\n </div>\n <Collapsible bind:open={controlledOpen}>\n {#snippet trigger({ open, props })}\n <Button\n {...props}\n variant=\"soft\"\n color=\"primary\"\n trailingIcon={open ? 'lucide:chevron-up' : 'lucide:chevron-down'}\n >\n Controlled collapsible\n </Button>\n {/snippet}\n {#snippet content()}\n <div\n class=\"mt-2 rounded-lg border border-primary/30 bg-primary-container/20 p-4 text-sm\"\n >\n <p>\n This collapsible is controlled externally via <code\n class=\"rounded bg-surface-container px-1\">bind:open</code\n >.\n </p>\n </div>\n {/snippet}\n </Collapsible>\n </section>\n\n <Separator />\n\n <!-- onOpenChange callback -->\n <section class=\"space-y-3\">\n <h2 id=\"onOpenChange-Callback\" class=\"text-lg font-semibold\">\n<a href=\"#onOpenChange-Callback\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n onOpenChange Callback\n </a>\n</h2>\n <!-- eslint-disable-next-line no-console -->\n <Collapsible bind:open={basicOpen} onOpenChange={(v) => console.log('open changed:', v)}>\n {#snippet trigger({ open, props })}\n <Button\n {...props}\n variant=\"outline\"\n color=\"secondary\"\n trailingIcon={open ? 'lucide:eye-off' : 'lucide:eye'}\n >\n {open ? 'Collapse' : 'Expand'} (check console)\n </Button>\n {/snippet}\n {#snippet content()}\n <div class=\"mt-2 rounded-lg border border-outline-variant p-4 text-sm\">\n <p>\n Open the console to see the <code class=\"rounded bg-surface-container px-1\"\n >onOpenChange</code\n > callback firing.\n </p>\n </div>\n {/snippet}\n </Collapsible>\n </section>\n\n <Separator />\n\n <!-- Disabled -->\n <section class=\"space-y-3\">\n <h2 id=\"Disabled\" class=\"text-lg font-semibold\">\n<a href=\"#Disabled\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Disabled\n </a>\n</h2>\n <Collapsible disabled>\n {#snippet trigger({ props })}\n <Button {...props} variant=\"outline\" disabled trailingIcon=\"lucide:chevron-down\">\n Cannot toggle (disabled)\n </Button>\n {/snippet}\n {#snippet content()}\n <div class=\"mt-2 rounded-lg border border-outline-variant p-4 text-sm\">\n <p>You should never see this content.</p>\n </div>\n {/snippet}\n </Collapsible>\n </section>\n\n <Separator />\n\n <!-- Custom trigger (non-Button) -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-Trigger\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-Trigger\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Trigger\n </a>\n</h2>\n <Collapsible>\n {#snippet trigger({ open, props })}\n <div\n {...props}\n class=\"flex cursor-pointer items-center gap-2 rounded-lg border border-outline-variant px-4 py-3 transition-colors hover:bg-surface-container\"\n >\n <Icon name=\"lucide:settings\" class=\"size-5\" />\n <span class=\"flex-1 text-sm font-medium\">Advanced Settings</span>\n <Icon\n name=\"lucide:chevron-down\"\n class=\"size-4 transition-transform duration-200 {open ? 'rotate-180' : ''}\"\n />\n </div>\n {/snippet}\n {#snippet content()}\n <div class=\"mt-1 space-y-3 rounded-lg border border-outline-variant p-4\">\n <div class=\"flex items-center justify-between\">\n <span class=\"text-sm\">Enable notifications</span>\n <Badge label=\"On\" color=\"success\" variant=\"soft\" />\n </div>\n <div class=\"flex items-center justify-between\">\n <span class=\"text-sm\">Auto-save drafts</span>\n <Badge label=\"Off\" color=\"error\" variant=\"soft\" />\n </div>\n <div class=\"flex items-center justify-between\">\n <span class=\"text-sm\">Dark mode</span>\n <Badge label=\"System\" color=\"info\" variant=\"soft\" />\n </div>\n </div>\n {/snippet}\n </Collapsible>\n </section>\n\n <Separator />\n\n <!-- Custom UI Slots -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-UI-Slots\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-UI-Slots\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom UI Slots\n </a>\n</h2>\n <Collapsible\n ui={{\n root: 'rounded-xl border border-outline-variant p-4',\n content: 'mt-3 border-t border-outline-variant pt-3'\n }}\n >\n {#snippet trigger({ open, props })}\n <Button\n {...props}\n variant=\"ghost\"\n size=\"sm\"\n trailingIcon={open ? 'lucide:chevron-up' : 'lucide:chevron-down'}\n >\n Styled with ui prop\n </Button>\n {/snippet}\n {#snippet content()}\n <p class=\"text-sm text-on-surface-variant\">\n The root has a rounded border and padding. The content has a top border\n separator.\n </p>\n {/snippet}\n </Collapsible>\n </section>\n\n <Separator />\n\n <!-- Multiple collapsibles -->\n <section class=\"space-y-3\">\n <h2 id=\"Multiple-Collapsibles\" class=\"text-lg font-semibold\">\n<a href=\"#Multiple-Collapsibles\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Multiple Collapsibles\n </a>\n</h2>\n <div class=\"divide-y divide-outline-variant rounded-lg border border-outline-variant\">\n {#each ['Getting Started', 'Installation', 'Configuration'] as title (title)}\n <Collapsible ui={{ root: 'px-4' }}>\n {#snippet trigger({ open, props })}\n <div\n {...props}\n class=\"flex w-full cursor-pointer items-center justify-between py-3\"\n >\n <span class=\"text-sm font-medium\">{title}</span>\n <Icon\n name=\"lucide:chevron-down\"\n class=\"size-4 transition-transform duration-200 {open\n ? 'rotate-180'\n : ''}\"\n />\n </div>\n {/snippet}\n {#snippet content()}\n <div class=\"pb-3 text-sm text-on-surface-variant\">\n Content for the \"{title}\" section. Each collapsible operates\n independently.\n </div>\n {/snippet}\n </Collapsible>\n {/each}\n </div>\n </section>\n</div>\n",
169
174
  "command": "<script lang=\"ts\">\n import { Command, Separator, Badge, Kbd, Button, Popover, Modal, Drawer } from '$lib/index.js'\n import type { CommandGroup } from '$lib/index.js'\n\n // --- Basic groups ---\n const basicGroups: CommandGroup[] = [\n {\n id: 'suggestions',\n label: 'Suggestions',\n items: [\n { value: 'calendar', label: 'Calendar', icon: 'lucide:calendar' },\n { value: 'search-emoji', label: 'Search Emoji', icon: 'lucide:smile' },\n { value: 'calculator', label: 'Calculator', icon: 'lucide:calculator' }\n ]\n },\n {\n id: 'settings',\n label: 'Settings',\n items: [\n {\n value: 'profile',\n label: 'Profile',\n icon: 'lucide:user',\n description: 'Manage your profile settings'\n },\n {\n value: 'billing',\n label: 'Billing',\n icon: 'lucide:credit-card',\n description: 'View billing information'\n },\n {\n value: 'preferences',\n label: 'Preferences',\n icon: 'lucide:settings',\n description: 'Configure app preferences'\n }\n ]\n }\n ]\n\n // --- Groups with disabled items ---\n const disabledGroups: CommandGroup[] = [\n {\n id: 'actions',\n label: 'Actions',\n items: [\n { value: 'new-file', label: 'New File', icon: 'lucide:file-plus' },\n { value: 'new-folder', label: 'New Folder', icon: 'lucide:folder-plus' },\n {\n value: 'delete',\n label: 'Delete (disabled)',\n icon: 'lucide:trash-2',\n disabled: true\n },\n {\n value: 'archive',\n label: 'Archive (disabled)',\n icon: 'lucide:archive',\n disabled: true\n }\n ]\n }\n ]\n\n // --- Keywords for search ---\n const keywordGroups: CommandGroup[] = [\n {\n id: 'navigation',\n label: 'Navigation',\n items: [\n {\n value: 'home',\n label: 'Home',\n icon: 'lucide:home',\n keywords: ['dashboard', 'main', 'start']\n },\n {\n value: 'docs',\n label: 'Documentation',\n icon: 'lucide:book-open',\n keywords: ['help', 'guide', 'manual']\n },\n {\n value: 'components',\n label: 'Components',\n icon: 'lucide:blocks',\n keywords: ['ui', 'elements', 'widgets']\n }\n ]\n }\n ]\n\n // --- External filtering ---\n let externalSearch = $state('')\n const allCountries = [\n { value: 'us', label: 'United States', icon: 'circle-flags:us' },\n { value: 'gb', label: 'United Kingdom', icon: 'circle-flags:gb' },\n { value: 'fr', label: 'France', icon: 'circle-flags:fr' },\n { value: 'de', label: 'Germany', icon: 'circle-flags:de' },\n { value: 'jp', label: 'Japan', icon: 'circle-flags:jp' },\n { value: 'kr', label: 'South Korea', icon: 'circle-flags:kr' },\n { value: 'vn', label: 'Vietnam', icon: 'circle-flags:vn' },\n { value: 'br', label: 'Brazil', icon: 'circle-flags:br' },\n { value: 'au', label: 'Australia', icon: 'circle-flags:au' },\n { value: 'ca', label: 'Canada', icon: 'circle-flags:ca' }\n ]\n const filteredCountries = $derived(\n externalSearch\n ? allCountries.filter((c) =>\n c.label.toLowerCase().includes(externalSearch.toLowerCase())\n )\n : allCountries\n )\n const externalGroups = $derived<CommandGroup[]>([\n {\n id: 'countries',\n label: externalSearch\n ? `Results for \"${externalSearch}\" (${filteredCountries.length})`\n : `All countries (${allCountries.length})`,\n items: filteredCountries\n }\n ])\n\n // --- onSelect callback ---\n let lastSelected = $state('')\n const callbackGroups: CommandGroup[] = [\n {\n id: 'fruits',\n label: 'Fruits',\n items: [\n {\n value: 'apple',\n label: 'Apple',\n icon: 'lucide:apple',\n onSelect: () => (lastSelected = 'Apple')\n },\n {\n value: 'banana',\n label: 'Banana',\n icon: 'lucide:banana',\n onSelect: () => (lastSelected = 'Banana')\n },\n {\n value: 'grape',\n label: 'Grape',\n icon: 'lucide:grape',\n onSelect: () => (lastSelected = 'Grape')\n }\n ]\n }\n ]\n\n // --- Multiple groups ---\n const multiGroups: CommandGroup[] = [\n {\n id: 'general',\n label: 'General',\n items: [\n {\n value: 'appearance',\n label: 'Appearance',\n icon: 'lucide:paintbrush',\n description: 'Change theme and colors'\n },\n {\n value: 'language',\n label: 'Language',\n icon: 'lucide:globe',\n description: 'Select preferred language'\n }\n ]\n },\n {\n id: 'account',\n label: 'Account',\n items: [\n {\n value: 'security',\n label: 'Security',\n icon: 'lucide:shield',\n description: 'Password and 2FA'\n },\n {\n value: 'notifications',\n label: 'Notifications',\n icon: 'lucide:bell',\n description: 'Email and push alerts'\n }\n ]\n },\n {\n id: 'developer',\n label: 'Developer',\n items: [\n {\n value: 'api-keys',\n label: 'API Keys',\n icon: 'lucide:key',\n description: 'Manage API credentials'\n },\n {\n value: 'webhooks',\n label: 'Webhooks',\n icon: 'lucide:webhook',\n description: 'Configure webhook endpoints'\n }\n ]\n }\n ]\n</script>\n\n<div class=\"mx-auto max-w-3xl space-y-12 p-8\">\n <div>\n <h1 class=\"text-2xl font-bold\">Command</h1>\n <p class=\"mt-1 text-on-surface-variant\">\n A command menu with search filtering and keyboard navigation.\n </p>\n </div>\n\n <Separator />\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <div class=\"rounded-lg border border-outline-variant shadow-md\">\n <Command groups={basicGroups} placeholder=\"Type a command or search...\" />\n </div>\n </section>\n\n <Separator />\n\n <!-- With descriptions -->\n <section class=\"space-y-3\">\n <h2 id=\"Items-with-Descriptions\" class=\"text-lg font-semibold\">\n<a href=\"#Items-with-Descriptions\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Items with Descriptions\n </a>\n</h2>\n <div class=\"rounded-lg border border-outline-variant shadow-md\">\n <Command groups={multiGroups} placeholder=\"Search settings...\" />\n </div>\n </section>\n\n <Separator />\n\n <!-- Disabled items -->\n <section class=\"space-y-3\">\n <h2 id=\"Disabled-Items\" class=\"text-lg font-semibold\">\n<a href=\"#Disabled-Items\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Disabled Items\n </a>\n</h2>\n <div class=\"rounded-lg border border-outline-variant shadow-md\">\n <Command groups={disabledGroups} placeholder=\"Search actions...\" />\n </div>\n </section>\n\n <Separator />\n\n <!-- Keywords search -->\n <section class=\"space-y-3\">\n <h2 id=\"Keywords-search-help-ui-dashboard\" class=\"text-lg font-semibold\">\n<a href=\"#Keywords-search-help-ui-dashboard\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Keywords (search \"help\", \"ui\", \"dashboard\")\n </a>\n</h2>\n <div class=\"rounded-lg border border-outline-variant shadow-md\">\n <Command groups={keywordGroups} placeholder=\"Try searching help or dashboard...\" />\n </div>\n </section>\n\n <Separator />\n\n <!-- onSelect callback -->\n <section class=\"space-y-3\">\n <h2 id=\"onSelect-Callback\" class=\"text-lg font-semibold\">\n<a href=\"#onSelect-Callback\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n onSelect Callback\n </a>\n</h2>\n <div class=\"flex items-center gap-2\">\n <span class=\"text-sm text-on-surface-variant\">Last selected:</span>\n <Badge label={lastSelected || 'None'} color={lastSelected ? 'success' : 'surface'} />\n </div>\n <div class=\"rounded-lg border border-outline-variant shadow-md\">\n <Command groups={callbackGroups} placeholder=\"Select a fruit...\" />\n </div>\n </section>\n\n <Separator />\n\n <!-- Loading -->\n <section class=\"space-y-3\">\n <h2 id=\"Loading-State\" class=\"text-lg font-semibold\">\n<a href=\"#Loading-State\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Loading State\n </a>\n</h2>\n <div class=\"rounded-lg border border-outline-variant shadow-md\">\n <Command groups={[]} loading placeholder=\"Fetching results...\" />\n </div>\n </section>\n\n <Separator />\n\n <!-- Empty state -->\n <section class=\"space-y-3\">\n <h2 id=\"Empty-State\" class=\"text-lg font-semibold\">\n<a href=\"#Empty-State\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Empty State\n </a>\n</h2>\n <div class=\"rounded-lg border border-outline-variant shadow-md\">\n <Command\n groups={[{ id: 'empty', items: [] }]}\n emptyText=\"Nothing here yet.\"\n placeholder=\"Search...\"\n />\n </div>\n </section>\n\n <Separator />\n\n <!-- Custom empty slot -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-Empty-Slot\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-Empty-Slot\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Empty Slot\n </a>\n</h2>\n <div class=\"rounded-lg border border-outline-variant shadow-md\">\n <Command groups={basicGroups} placeholder=\"Search something that doesn't exist...\">\n {#snippet empty({ search })}\n <div class=\"flex flex-col items-center gap-2 py-6\">\n <span class=\"text-2xl\">🔍</span>\n <span class=\"text-sm text-on-surface-variant\">\n {search ? `No results for \"${search}\"` : 'Start typing to search'}\n </span>\n </div>\n {/snippet}\n </Command>\n </div>\n </section>\n\n <Separator />\n\n <!-- Sizes -->\n <section class=\"space-y-3\">\n <h2 id=\"Sizes\" class=\"text-lg font-semibold\">\n<a href=\"#Sizes\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Sizes\n </a>\n</h2>\n {#each ['xs', 'sm', 'md', 'lg', 'xl'] as const as s (s)}\n <div>\n <p class=\"mb-1 text-xs font-medium text-on-surface-variant\">{s}</p>\n <div class=\"rounded-lg border border-outline-variant shadow-md\">\n <Command\n size={s}\n groups={[\n {\n id: 'size-demo',\n label: 'Actions',\n items: [\n { value: 'copy', label: 'Copy', icon: 'lucide:copy' },\n { value: 'paste', label: 'Paste', icon: 'lucide:clipboard' }\n ]\n }\n ]}\n placeholder=\"Size {s}...\"\n />\n </div>\n </div>\n {/each}\n </section>\n\n <Separator />\n\n <!-- Custom UI slots -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-UI-Slots\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-UI-Slots\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom UI Slots\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Available ui slots: root, input, list, empty, loading, group, groupHeading, groupItems,\n separator, item, itemIcon, itemLabel, itemDescription, itemTrailing\n </p>\n <div class=\"space-y-4\">\n <div>\n <p class=\"mb-1 text-xs font-medium text-on-surface-variant\">\n Custom background + rounded items + uppercase headings\n </p>\n <Command\n groups={basicGroups}\n placeholder=\"Styled command...\"\n ui={{\n root: 'rounded-lg border border-outline-variant shadow-md bg-surface-container-low',\n item: 'rounded-lg',\n groupHeading: 'uppercase tracking-wider text-primary'\n }}\n />\n </div>\n <div>\n <p class=\"mb-1 text-xs font-medium text-on-surface-variant\">\n Custom selected item color + item icon color\n </p>\n <div class=\"rounded-lg border border-outline-variant shadow-md\">\n <Command\n groups={basicGroups}\n placeholder=\"Custom highlight...\"\n ui={{\n item: 'data-[selected]:bg-primary/10 data-[selected]:text-primary',\n itemIcon: 'text-primary'\n }}\n />\n </div>\n </div>\n <div>\n <p class=\"mb-1 text-xs font-medium text-on-surface-variant\">\n Custom list max height (scrollable)\n </p>\n <div class=\"rounded-lg border border-outline-variant shadow-md\">\n <Command\n groups={multiGroups}\n placeholder=\"Scroll to see more...\"\n ui={{\n list: 'max-h-48'\n }}\n />\n </div>\n </div>\n </div>\n </section>\n\n <Separator />\n\n <!-- Footer slot -->\n <section class=\"space-y-3\">\n <h2 id=\"Footer-Slot\" class=\"text-lg font-semibold\">\n<a href=\"#Footer-Slot\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Footer Slot\n </a>\n</h2>\n <div class=\"rounded-lg border border-outline-variant shadow-md\">\n <Command groups={basicGroups} placeholder=\"Search...\">\n {#snippet footer()}\n <div\n class=\"flex items-center justify-between px-3 py-2 text-xs text-on-surface-variant\"\n >\n <div class=\"flex items-center gap-2\">\n <span class=\"flex items-center gap-1\"\n ><Kbd value=\"↑\" size=\"sm\" /><Kbd value=\"↓\" size=\"sm\" /> Navigate</span\n >\n <span class=\"flex items-center gap-1\"\n ><Kbd value=\"↵\" size=\"sm\" /> Select</span\n >\n </div>\n <span class=\"flex items-center gap-1\"\n ><Kbd value=\"Esc\" size=\"sm\" /> Close</span\n >\n </div>\n {/snippet}\n </Command>\n </div>\n </section>\n\n <Separator />\n\n <!-- shouldFilter=false -->\n <section class=\"space-y-3\">\n <h2 id=\"External-Filtering-shouldFilterfalse\" class=\"text-lg font-semibold\">\n<a href=\"#External-Filtering-shouldFilterfalse\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n External Filtering (shouldFilter=false)\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Built-in filter is disabled. The search term is bound via\n <code class=\"rounded bg-surface-container-highest px-1 py-0.5 text-xs\">bind:search</code\n >\n and filtering is handled externally.\n </p>\n <div class=\"rounded-lg border border-outline-variant shadow-md\">\n <Command\n shouldFilter={false}\n bind:search={externalSearch}\n groups={externalGroups}\n placeholder=\"Search countries...\"\n />\n </div>\n </section>\n\n <Separator />\n\n <!-- Inside Popover -->\n <section class=\"space-y-3\">\n <h2 id=\"Inside-Popover\" class=\"text-lg font-semibold\">\n<a href=\"#Inside-Popover\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Inside Popover\n </a>\n</h2>\n <Popover>\n <Button variant=\"outline\" leadingIcon=\"lucide:search\">Open Command...</Button>\n {#snippet content()}\n <Command\n groups={basicGroups}\n placeholder=\"Search...\"\n ui={{ list: 'max-h-64' }}\n class=\"w-80\"\n />\n {/snippet}\n </Popover>\n </section>\n\n <Separator />\n\n <!-- Inside Modal -->\n <section class=\"space-y-3\">\n <h2 id=\"Inside-Modal\" class=\"text-lg font-semibold\">\n<a href=\"#Inside-Modal\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Inside Modal\n </a>\n</h2>\n <Modal>\n <Button variant=\"outline\" leadingIcon=\"lucide:search\">\n Search...\n <Kbd value=\"meta\" size=\"sm\" />\n <Kbd value=\"K\" size=\"sm\" />\n </Button>\n {#snippet content()}\n <Command groups={multiGroups} placeholder=\"Search settings...\" />\n {/snippet}\n </Modal>\n </section>\n\n <Separator />\n\n <!-- Inside Drawer -->\n <section class=\"space-y-3\">\n <h2 id=\"Inside-Drawer\" class=\"text-lg font-semibold\">\n<a href=\"#Inside-Drawer\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Inside Drawer\n </a>\n</h2>\n <Drawer handle={false}>\n <Button variant=\"outline\" leadingIcon=\"lucide:terminal\">Open Command Drawer</Button>\n {#snippet content()}\n <Command groups={multiGroups} placeholder=\"Search settings...\" />\n {/snippet}\n </Drawer>\n </section>\n</div>\n",
170
175
  "context-menu": "<script lang=\"ts\">\n import {\n ContextMenu,\n Separator,\n type ContextMenuItem,\n type ContextMenuRadioGroup\n } from '$lib/index.js'\n\n let showStatusBar = $state(true)\n let showActivityBar = $state(false)\n let showPanel = $state(true)\n let selectedTheme = $state('system')\n\n const basicItems: ContextMenuItem[] = [\n { label: 'Back', icon: 'lucide:arrow-left', kbds: ['alt', 'left'] },\n { label: 'Forward', icon: 'lucide:arrow-right', kbds: ['alt', 'right'], disabled: true },\n { label: 'Reload', icon: 'lucide:refresh-cw', kbds: ['meta', 'r'] },\n { type: 'separator' },\n { label: 'Save As...', icon: 'lucide:save', kbds: ['meta', 's'] },\n { label: 'Print...', icon: 'lucide:printer', kbds: ['meta', 'p'] }\n ]\n\n const coloredItems: ContextMenuItem[] = [\n { label: 'Edit', icon: 'lucide:pencil' },\n { label: 'Duplicate', icon: 'lucide:copy' },\n { type: 'separator' },\n { label: 'Archive', icon: 'lucide:archive', color: 'warning' },\n { label: 'Delete', icon: 'lucide:trash-2', color: 'error' }\n ]\n\n const checkboxItems: ContextMenuItem[] = $derived([\n { type: 'label', label: 'Appearance' },\n {\n type: 'checkbox',\n label: 'Status Bar',\n checked: showStatusBar,\n closeOnSelect: false,\n onCheckedChange: (v: boolean) => (showStatusBar = v)\n },\n {\n type: 'checkbox',\n label: 'Activity Bar',\n checked: showActivityBar,\n closeOnSelect: false,\n onCheckedChange: (v: boolean) => (showActivityBar = v)\n },\n {\n type: 'checkbox',\n label: 'Panel',\n checked: showPanel,\n closeOnSelect: false,\n onCheckedChange: (v: boolean) => (showPanel = v)\n }\n ])\n\n const radioItems: ContextMenuItem[] = [\n { type: 'label', label: 'Theme' },\n { type: 'radio', label: 'Light', value: 'light' },\n { type: 'radio', label: 'Dark', value: 'dark' },\n { type: 'radio', label: 'System', value: 'system' }\n ]\n\n const radioGroups: ContextMenuRadioGroup[] = $derived([\n {\n name: 'theme',\n value: selectedTheme,\n onValueChange: (v: string) => (selectedTheme = v)\n }\n ])\n\n const submenuItems: ContextMenuItem[] = [\n { label: 'Cut', icon: 'lucide:scissors', kbds: ['meta', 'x'] },\n { label: 'Copy', icon: 'lucide:copy', kbds: ['meta', 'c'] },\n { label: 'Paste', icon: 'lucide:clipboard', kbds: ['meta', 'v'] },\n { type: 'separator' },\n {\n type: 'sub',\n label: 'More Tools',\n icon: 'lucide:wrench',\n items: [\n { label: 'Save Page As...', kbds: ['meta', 's'] },\n { label: 'Create Shortcut...' },\n { label: 'Name Window...' },\n { type: 'separator' },\n { label: 'Developer Tools', kbds: ['meta', 'alt', 'i'] }\n ]\n },\n {\n type: 'sub',\n label: 'Share',\n icon: 'lucide:share-2',\n items: [\n { label: 'Email', icon: 'lucide:mail' },\n { label: 'Message', icon: 'lucide:message-square' },\n { type: 'separator' },\n { label: 'Copy Link', icon: 'lucide:link' }\n ]\n }\n ]\n\n const fullItems: ContextMenuItem[] = $derived([\n { label: 'Cut', icon: 'lucide:scissors', kbds: ['meta', 'x'] },\n { label: 'Copy', icon: 'lucide:copy', kbds: ['meta', 'c'] },\n { label: 'Paste', icon: 'lucide:clipboard', kbds: ['meta', 'v'] },\n { type: 'separator' },\n { type: 'label', label: 'View' },\n {\n type: 'checkbox',\n label: 'Show Bookmarks',\n checked: showStatusBar,\n onCheckedChange: (v: boolean) => (showStatusBar = v)\n },\n {\n type: 'checkbox',\n label: 'Show Full URLs',\n checked: showActivityBar,\n onCheckedChange: (v: boolean) => (showActivityBar = v)\n },\n { type: 'separator' },\n {\n type: 'sub',\n label: 'More Tools',\n icon: 'lucide:wrench',\n items: [\n { label: 'Save Page As...', kbds: ['meta', 's'] },\n { label: 'Create Shortcut...' },\n { type: 'separator' },\n { label: 'Developer Tools', kbds: ['meta', 'alt', 'i'] }\n ]\n },\n { type: 'separator' },\n { label: 'Delete', icon: 'lucide:trash-2', color: 'error', kbds: ['delete'] }\n ])\n\n const triggerClass =\n 'flex items-center justify-center rounded-lg border-2 border-dashed border-outline-variant bg-surface-container-low text-on-surface-variant transition-colors hover:border-primary/50 hover:bg-surface-container cursor-context-menu select-none'\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">ContextMenu</h1>\n <p class=\"text-on-surface-variant\">\n Display a menu of actions or options triggered by right-click. Built on bits-ui\n ContextMenu primitive.\n </p>\n </div>\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Right-click on the area below to open a context menu with icons and keyboard shortcuts.\n </p>\n <ContextMenu items={basicItems}>\n <div class=\"{triggerClass} h-36 w-full\">\n <span class=\"text-sm\">Right-click here</span>\n </div>\n </ContextMenu>\n </section>\n\n <!-- Colored Items -->\n <section class=\"space-y-3\">\n <h2 id=\"Colored-Items\" class=\"text-lg font-semibold\">\n<a href=\"#Colored-Items\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Colored Items\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use the\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">color</code>\n prop on items for semantic coloring.\n </p>\n <ContextMenu items={coloredItems}>\n <div class=\"{triggerClass} h-36 w-full\">\n <span class=\"text-sm\">Right-click for colored items</span>\n </div>\n </ContextMenu>\n </section>\n\n <!-- Checkbox Items -->\n <section class=\"space-y-3\">\n <h2 id=\"Checkbox-Items\" class=\"text-lg font-semibold\">\n<a href=\"#Checkbox-Items\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Checkbox Items\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Toggle boolean states with checkbox items.</p>\n <div class=\"flex items-start gap-4 rounded-lg bg-surface-container-high p-4\">\n <ContextMenu items={checkboxItems} class=\"flex-1\">\n <div class=\"{triggerClass} h-36 w-full\">\n <span class=\"text-sm\">Right-click for checkboxes</span>\n </div>\n </ContextMenu>\n <div class=\"text-sm text-on-surface-variant\">\n <p>Status Bar: <code class=\"text-primary\">{showStatusBar}</code></p>\n <p>Activity Bar: <code class=\"text-primary\">{showActivityBar}</code></p>\n <p>Panel: <code class=\"text-primary\">{showPanel}</code></p>\n </div>\n </div>\n </section>\n\n <!-- Radio Items -->\n <section class=\"space-y-3\">\n <h2 id=\"Radio-Items\" class=\"text-lg font-semibold\">\n<a href=\"#Radio-Items\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Radio Items\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Single selection with radio items and groups.</p>\n <div class=\"flex items-start gap-4 rounded-lg bg-surface-container-high p-4\">\n <ContextMenu items={radioItems} {radioGroups} class=\"flex-1\">\n <div class=\"{triggerClass} h-36 w-full\">\n <span class=\"text-sm\">Right-click for radio selection</span>\n </div>\n </ContextMenu>\n <div class=\"text-sm text-on-surface-variant\">\n Selected: <code class=\"text-primary\">{selectedTheme}</code>\n </div>\n </div>\n </section>\n\n <!-- Submenus -->\n <section class=\"space-y-3\">\n <h2 id=\"Submenus\" class=\"text-lg font-semibold\">\n<a href=\"#Submenus\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Submenus\n </a>\n</h2>\n <ContextMenu items={submenuItems}>\n <div class=\"{triggerClass} h-36 w-full\">\n <span class=\"text-sm\">Right-click for submenus</span>\n </div>\n </ContextMenu>\n </section>\n\n <!-- Sizes -->\n <section class=\"space-y-3\">\n <h2 id=\"Sizes\" class=\"text-lg font-semibold\">\n<a href=\"#Sizes\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Sizes\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Control the menu size with the\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">size</code>\n prop.\n </p>\n <div class=\"grid gap-4 md:grid-cols-3\">\n {#each [{ size: 'xs' as const, label: 'XS' }, { size: 'sm' as const, label: 'SM' }, { size: 'md' as const, label: 'MD (default)' }, { size: 'lg' as const, label: 'LG' }, { size: 'xl' as const, label: 'XL' }] as item (item.size)}\n <ContextMenu items={basicItems.slice(0, 5)} size={item.size}>\n <div class=\"{triggerClass} h-28\">\n <span class=\"text-xs\">{item.label}</span>\n </div>\n </ContextMenu>\n {/each}\n </div>\n </section>\n\n <!-- Custom Header/Footer -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-HeaderFooter\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-HeaderFooter\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Header/Footer\n </a>\n</h2>\n <ContextMenu items={basicItems.slice(0, 4)}>\n <div class=\"{triggerClass} h-36 w-full\">\n <span class=\"text-sm\">Right-click for header/footer</span>\n </div>\n {#snippet header()}\n <div class=\"px-3 py-2\">\n <p class=\"text-sm font-medium\">John Doe</p>\n <p class=\"text-xs text-on-surface-variant\">john@example.com</p>\n </div>\n <Separator />\n {/snippet}\n {#snippet footer({ close })}\n <Separator />\n <div class=\"p-1\">\n <button\n class=\"flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-sm text-error hover:bg-error-container hover:text-on-error-container\"\n onclick={close}\n >\n Log out\n </button>\n </div>\n {/snippet}\n </ContextMenu>\n </section>\n\n <!-- Custom Content -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-Content\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-Content\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Content\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use the\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">content</code>\n slot for fully custom context menu content.\n </p>\n <ContextMenu>\n <div class=\"{triggerClass} h-36 w-full\">\n <span class=\"text-sm\">Right-click for custom content</span>\n </div>\n {#snippet content({ close })}\n <div class=\"p-4\">\n <p class=\"mb-3 text-sm font-medium\">Choose a color</p>\n <div class=\"grid grid-cols-5 gap-2\">\n {#each ['bg-red-500', 'bg-orange-500', 'bg-yellow-500', 'bg-green-500', 'bg-teal-500', 'bg-blue-500', 'bg-indigo-500', 'bg-purple-500', 'bg-pink-500', 'bg-gray-500'] as color (color)}\n <button\n class=\"size-8 rounded-full {color} transition-all hover:ring-2 hover:ring-outline hover:ring-offset-2\"\n aria-label=\"Select {color\n .replace('bg-', '')\n .replace('-500', '')} color\"\n onclick={close}\n ></button>\n {/each}\n </div>\n </div>\n {/snippet}\n </ContextMenu>\n </section>\n\n <!-- UI Overrides -->\n <section class=\"space-y-3\">\n <h2 id=\"UI-Overrides\" class=\"text-lg font-semibold\">\n<a href=\"#UI-Overrides\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n UI Overrides\n </a>\n</h2>\n <div class=\"grid gap-4 sm:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Primary theme</p>\n <ContextMenu\n items={basicItems.slice(0, 4)}\n ui={{\n content: 'bg-primary text-on-primary ring-primary/50',\n item: 'data-[highlighted]:bg-on-primary/20',\n separator: 'bg-on-primary/20'\n }}\n >\n <div class=\"{triggerClass} h-28\">\n <span class=\"text-sm\">Primary Style</span>\n </div>\n </ContextMenu>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Custom rounding</p>\n <ContextMenu\n items={basicItems.slice(0, 4)}\n ui={{ content: 'rounded-xl shadow-2xl' }}\n >\n <div class=\"{triggerClass} h-28\">\n <span class=\"text-sm\">Custom Rounding</span>\n </div>\n </ContextMenu>\n </div>\n </div>\n </section>\n\n <Separator />\n\n <!-- Real World Examples -->\n <section class=\"space-y-6\">\n <h2 id=\"Real-World-Examples\" class=\"text-lg font-semibold\">\n<a href=\"#Real-World-Examples\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World Examples\n </a>\n</h2>\n\n <div class=\"space-y-4\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium\">Browser Context Menu</p>\n <ContextMenu items={fullItems}>\n <div class=\"{triggerClass} h-48 w-full\">\n <div class=\"text-center\">\n <p class=\"text-sm font-medium text-on-surface\">Interactive Area</p>\n <p class=\"mt-1 text-xs text-on-surface-variant\">\n Right-click anywhere in this area\n </p>\n </div>\n </div>\n </ContextMenu>\n </div>\n </div>\n </section>\n</div>\n",
176
+ "confirm-dialog": "<script lang=\"ts\">\n import { Button, Card, Link, Toaster, confirmDialog, notify } from '$lib/index.js'\n\n const confirmMethodsReference = [\n {\n name: 'confirmDialog.show(options?)',\n description: 'Opens a confirmation dialog. Resolves to true when confirmed, false when cancelled.'\n },\n {\n name: 'confirmDialog.delete(options?)',\n description:\n 'Shorthand for destructive confirmations — warning icon, error confirm button, delete copy.'\n },\n {\n name: 'registerConfirmDialog(showFn)',\n description:\n 'Wire the singleton to a mounted <ConfirmDialog />. Call once in your root layout onMount.'\n }\n ] as const\n\n const confirmOptionsReference = [\n { name: 'title', type: 'string', description: 'Dialog heading.' },\n { name: 'message', type: 'string', description: 'Supporting description below the title.' },\n { name: 'confirmText', type: 'string', description: 'Primary action label. Default: Confirm.' },\n { name: 'cancelText', type: 'string', description: 'Secondary action label. Default: Cancel.' },\n {\n name: 'icon',\n type: \"'success' | 'error' | 'warning' | 'info' | 'question'\",\n description: 'Animated icon shown above the title.'\n },\n {\n name: 'confirmColor',\n type: 'ButtonColor',\n description: 'Semantic color for the confirm button. Default: primary.'\n },\n {\n name: 'showCancel',\n type: 'boolean',\n description: 'When false, only the confirm button is shown (alert-style). Default: true.'\n },\n {\n name: 'dismissible',\n type: 'boolean',\n description: 'When false, Escape and backdrop click do not dismiss. Default: true.'\n },\n {\n name: 'inputPlaceholder',\n type: 'string',\n description: 'Shows a text input or textarea for prompt-style dialogs.'\n },\n { name: 'inputValue', type: 'string', description: 'Initial value for the prompt input.' },\n {\n name: 'inputType',\n type: \"'text' | 'password' | 'email' | 'number' | 'textarea'\",\n description: 'Input control type when inputPlaceholder is set.'\n },\n {\n name: 'inputChoices',\n type: 'string[]',\n description: 'Quick-pick chips above the prompt input.'\n },\n {\n name: 'onConfirm',\n type: '(value?: string) => void',\n description: 'Callback when the user confirms. Receives prompt value when applicable.'\n },\n { name: 'onCancel', type: '() => void', description: 'Callback when the user cancels.' }\n ] as const\n\n const confirmDialogPropsReference = [\n {\n name: 'confirmText',\n type: 'string',\n description: 'Default confirm label when options.confirmText is omitted.'\n },\n {\n name: 'cancelText',\n type: 'string',\n description: 'Default cancel label when options.cancelText is omitted.'\n }\n ] as const\n\n async function testBasicConfirm() {\n const ok = await confirmDialog.show({\n title: 'Save changes?',\n message: 'Your edits will be applied immediately.',\n icon: 'question',\n confirmText: 'Save'\n })\n\n if (ok) {\n notify.success('Changes saved')\n }\n }\n\n async function testDelete() {\n const ok = await confirmDialog.delete({\n title: 'Delete this item?',\n message: 'This action is permanent and cannot be undone.'\n })\n\n if (ok) {\n notify.error('Item deleted')\n }\n }\n\n async function testSuccessAlert() {\n await confirmDialog.show({\n title: 'Payment received',\n message: 'Your transaction was completed successfully.',\n icon: 'success',\n confirmText: 'Got it',\n confirmColor: 'success',\n showCancel: false\n })\n }\n\n async function testWarningChoice() {\n const ok = await confirmDialog.show({\n title: 'Grant permission?',\n message: 'This app wants to access your camera and microphone.',\n icon: 'warning',\n confirmText: 'Allow',\n confirmColor: 'warning'\n })\n\n if (ok) {\n notify.warning('Permission granted')\n }\n }\n\n async function testPrompt() {\n const ok = await confirmDialog.show({\n title: 'Rename project',\n message: 'Enter a new name for this project.',\n icon: 'info',\n confirmText: 'Rename',\n inputPlaceholder: 'Project name',\n inputValue: 'My project',\n inputChoices: ['Alpha', 'Beta', 'Production']\n })\n\n if (ok) {\n notify.info('Project renamed')\n }\n }\n\n async function testNonDismissible() {\n const ok = await confirmDialog.show({\n title: 'Accept terms?',\n message: 'You must accept the terms to continue.',\n icon: 'info',\n confirmText: 'I accept',\n dismissible: false\n })\n\n if (ok) {\n notify.success('Terms accepted')\n }\n }\n\n const setupExample = `<!-- +layout.svelte -->\n<script lang=\"ts\">\n import { ConfirmDialog, registerConfirmDialog } from 'svelora'\n import { onMount } from 'svelte'\n\n let confirmDialogRef\n\n onMount(() => {\n registerConfirmDialog((options) => confirmDialogRef.show(options))\n })\n<` + `/script>\n\n<ConfirmDialog bind:this={confirmDialogRef} />\n{@render children()}`\n\n const notifyExample = `const ok = await confirmDialog.delete()\nif (ok) notify.success('Deleted')`\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">Confirm Dialog</h1>\n <p class=\"text-on-surface-variant\">\n Imperative confirmation dialogs with animated icons. Call\n <code class=\"rounded bg-surface-container-high px-1\">confirmDialog.show()</code> from\n anywhere after mounting\n <code class=\"rounded bg-surface-container-high px-1\">&lt;ConfirmDialog /&gt;</code> in\n your root layout. Pairs naturally with\n <Link href=\"/docs/components/toast\" class=\"text-primary underline\">Toast</Link> and\n <code class=\"rounded bg-surface-container-high px-1\">notify</code>.\n </p>\n </div>\n\n <!-- Setup -->\n <section class=\"space-y-3\">\n <h2 id=\"Setup\" class=\"text-lg font-semibold\">\n <a\n href=\"#Setup\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n Setup\n </a>\n </h2>\n <p class=\"text-sm text-on-surface-variant\">\n Register the singleton once in your root layout. This docs site already mounts\n <code class=\"rounded bg-surface-container-high px-1\">ConfirmDialog</code> globally, so\n the demos below work out of the box.\n </p>\n <Card class=\"border border-outline-variant/70 bg-surface-container-high/50 p-4\">\n <pre class=\"overflow-x-auto text-xs leading-relaxed text-on-surface-variant\"><code\n >{setupExample}</code\n ></pre>\n </Card>\n </section>\n\n <!-- vs Modal -->\n <section class=\"space-y-3\">\n <h2 id=\"ConfirmDialog-vs-Modal\" class=\"text-lg font-semibold\">\n <a\n href=\"#ConfirmDialog-vs-Modal\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n ConfirmDialog vs Modal\n </a>\n </h2>\n <div class=\"grid gap-3 md:grid-cols-2\">\n <Card class=\"space-y-2 border border-outline-variant/70 p-4\">\n <p class=\"font-semibold text-on-surface\">Use ConfirmDialog when…</p>\n <ul class=\"list-inside list-disc space-y-1 text-sm text-on-surface-variant\">\n <li>You need yes/no or acknowledge flows</li>\n <li>You call it imperatively from handlers or stores</li>\n <li>You want animated icons and preset delete styling</li>\n </ul>\n </Card>\n <Card class=\"space-y-2 border border-outline-variant/70 p-4\">\n <p class=\"font-semibold text-on-surface\">Use <Link href=\"/docs/components/modal\" class=\"text-primary underline\">Modal</Link> when…</p>\n <ul class=\"list-inside list-disc space-y-1 text-sm text-on-surface-variant\">\n <li>You need custom body, forms, or multi-step content</li>\n <li>You control open state declaratively with <code>bind:open</code></li>\n <li>You need fullscreen, scrollable, or slot-heavy layouts</li>\n </ul>\n </Card>\n </div>\n </section>\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic-Usage\" class=\"text-lg font-semibold\">\n <a\n href=\"#Basic-Usage\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n Basic Usage\n </a>\n </h2>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <Button variant=\"outline\" label=\"Confirm\" onclick={testBasicConfirm} />\n <Button\n color=\"error\"\n variant=\"soft\"\n label=\"Delete\"\n leadingIcon=\"lucide:trash-2\"\n onclick={testDelete}\n />\n </div>\n </section>\n\n <!-- Icons & alert -->\n <section class=\"space-y-3\">\n <h2 id=\"Icons-and-Alerts\" class=\"text-lg font-semibold\">\n <a\n href=\"#Icons-and-Alerts\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n Icons &amp; Alerts\n </a>\n </h2>\n <p class=\"text-sm text-on-surface-variant\">\n Set <code class=\"rounded bg-surface-container-high px-1\">icon</code> to\n <code class=\"rounded bg-surface-container-high px-1\">success</code>,\n <code class=\"rounded bg-surface-container-high px-1\">error</code>,\n <code class=\"rounded bg-surface-container-high px-1\">warning</code>,\n <code class=\"rounded bg-surface-container-high px-1\">info</code>, or\n <code class=\"rounded bg-surface-container-high px-1\">question</code>. Use\n <code class=\"rounded bg-surface-container-high px-1\">showCancel: false</code> for\n single-button alerts.\n </p>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <Button\n color=\"success\"\n variant=\"soft\"\n label=\"Success alert\"\n leadingIcon=\"lucide:check\"\n onclick={testSuccessAlert}\n />\n <Button\n color=\"warning\"\n variant=\"soft\"\n label=\"Warning choice\"\n leadingIcon=\"lucide:shield-alert\"\n onclick={testWarningChoice}\n />\n </div>\n </section>\n\n <!-- Prompt -->\n <section class=\"space-y-3\">\n <h2 id=\"Prompt-Input\" class=\"text-lg font-semibold\">\n <a\n href=\"#Prompt-Input\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n Prompt Input\n </a>\n </h2>\n <p class=\"text-sm text-on-surface-variant\">\n Pass <code class=\"rounded bg-surface-container-high px-1\">inputPlaceholder</code> to\n collect text. Optional <code class=\"rounded bg-surface-container-high px-1\">inputChoices</code>\n render quick-pick chips.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Button variant=\"outline\" label=\"Rename project\" onclick={testPrompt} />\n </div>\n </section>\n\n <!-- Non-dismissible -->\n <section class=\"space-y-3\">\n <h2 id=\"Non-dismissible\" class=\"text-lg font-semibold\">\n <a\n href=\"#Non-dismissible\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n Non-dismissible\n </a>\n </h2>\n <p class=\"text-sm text-on-surface-variant\">\n Set <code class=\"rounded bg-surface-container-high px-1\">dismissible: false</code> to\n disable Escape and backdrop dismiss — the user must choose an action.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Button variant=\"outline\" label=\"Accept terms\" onclick={testNonDismissible} />\n </div>\n </section>\n\n <!-- With notify -->\n <section class=\"space-y-3\">\n <h2 id=\"With-notify\" class=\"text-lg font-semibold\">\n <a\n href=\"#With-notify\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n With notify\n </a>\n </h2>\n <Card class=\"border border-info/20 bg-info/10 p-4\">\n <p class=\"text-sm text-on-surface-variant\">\n After confirmation, show feedback with\n <Link href=\"/docs/components/toast\" class=\"text-primary underline\">notify</Link>:\n </p>\n <pre class=\"mt-3 overflow-x-auto text-xs text-on-surface-variant\"><code\n >{notifyExample}</code\n ></pre>\n </Card>\n </section>\n\n <!-- API Reference -->\n <section class=\"space-y-3\">\n <h2 id=\"API-Reference\" class=\"text-lg font-semibold\">\n <a\n href=\"#API-Reference\"\n class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\"\n >\n <span\n class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\"\n aria-hidden=\"true\">#</span\n >\n API Reference\n </a>\n </h2>\n <div class=\"grid gap-4 lg:grid-cols-2\">\n <Card class=\"border border-outline-variant/70\">\n <div class=\"space-y-4\">\n <h3 class=\"text-base font-semibold\">confirmDialog methods</h3>\n <div class=\"space-y-3\">\n {#each confirmMethodsReference as item (item.name)}\n <div\n class=\"border-b border-outline-variant/60 pb-3 last:border-b-0 last:pb-0\"\n >\n <p class=\"font-mono text-xs text-primary\">{item.name}</p>\n <p class=\"mt-1 text-sm text-on-surface-variant\">\n {item.description}\n </p>\n </div>\n {/each}\n </div>\n </div>\n </Card>\n <Card class=\"border border-outline-variant/70\">\n <div class=\"space-y-4\">\n <h3 class=\"text-base font-semibold\">ConfirmOptions</h3>\n <div class=\"space-y-3\">\n {#each confirmOptionsReference as item (item.name)}\n <div\n class=\"border-b border-outline-variant/60 pb-3 last:border-b-0 last:pb-0\"\n >\n <p class=\"font-mono text-xs text-on-surface-variant\">\n {item.name}\n </p>\n <p class=\"mt-1 text-sm font-medium\">{item.type}</p>\n <p class=\"mt-1 text-sm text-on-surface-variant\">\n {item.description}\n </p>\n </div>\n {/each}\n </div>\n </div>\n </Card>\n </div>\n <Card class=\"border border-outline-variant/70\">\n <div class=\"space-y-4\">\n <h3 class=\"text-base font-semibold\">ConfirmDialog props</h3>\n <div class=\"grid gap-3 md:grid-cols-2\">\n {#each confirmDialogPropsReference as item (item.name)}\n <div class=\"rounded-xl border border-outline-variant/60 p-4\">\n <p class=\"font-mono text-xs text-on-surface-variant\">{item.name}</p>\n <p class=\"mt-1 text-sm font-medium\">{item.type}</p>\n <p class=\"mt-1 text-sm text-on-surface-variant\">{item.description}</p>\n </div>\n {/each}\n </div>\n </div>\n </Card>\n </section>\n</div>\n\n<Toaster />\n",
171
177
  "drawer": "<script lang=\"ts\">\n import { Drawer, Button, Badge, Icon, Separator } from '$lib/index.js'\n\n let basicOpen = $state(false)\n let directionOpen = $state<Record<string, boolean>>({\n top: false,\n right: false,\n bottom: false,\n left: false\n })\n let insetOpen = $state<Record<string, boolean>>({\n top: false,\n right: false,\n bottom: false,\n left: false\n })\n let noHandleOpen = $state(false)\n let handleOnlyOpen = $state(false)\n let noOverlayOpen = $state(false)\n let scaleBackgroundOpen = $state(false)\n let nonDismissibleOpen = $state(false)\n let snapOpen = $state(false)\n let snapControlledOpen = $state(false)\n let activeSnap = $state<number | string | null>(0.25)\n let nestedOuterOpen = $state(false)\n let nestedInnerOpen = $state(false)\n let slotsOpen = $state(false)\n let customContentOpen = $state(false)\n let callbacksOpen = $state(false)\n let callbackLog = $state<string[]>([])\n let uiOverrideOpen = $state(false)\n let programmaticOpen = $state(false)\n\n let settingsOpen = $state(false)\n let notificationsOpen = $state(false)\n let filterOpen = $state(false)\n let confirmOpen = $state(false)\n\n const directions = ['top', 'right', 'bottom', 'left'] as const\n\n function logCallback(name: string) {\n callbackLog = [...callbackLog.slice(-4), `${new Date().toLocaleTimeString()} — ${name}`]\n }\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">Drawer</h1>\n <p class=\"text-on-surface-variant\">\n A draggable drawer component built on vaul-svelte. Supports swipe-to-dismiss, snap\n points, nested drawers, all 4 directions, and full slot customization.\n </p>\n </div>\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Drawer\n bind:open={basicOpen}\n title=\"Basic Drawer\"\n description=\"This is a basic drawer with title, description, body and footer.\"\n >\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\" label=\"Open Drawer\" />\n {/snippet}\n {#snippet body()}\n <p class=\"text-on-surface-variant\">\n Slides up from the bottom by default. Swipe down or click the overlay to\n close.\n </p>\n {/snippet}\n {#snippet footer()}\n <Button label=\"Close\" variant=\"outline\" onclick={() => (basicOpen = false)} />\n {/snippet}\n </Drawer>\n </div>\n </section>\n\n <!-- Directions -->\n <section class=\"space-y-3\">\n <h2 id=\"Directions\" class=\"text-lg font-semibold\">\n<a href=\"#Directions\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Directions\n </a>\n</h2>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n {#each directions as dir (dir)}\n <Drawer\n bind:open={directionOpen[dir]}\n direction={dir}\n title=\"{dir.charAt(0).toUpperCase() + dir.slice(1)} Drawer\"\n description=\"Opens from the {dir}.\"\n >\n {#snippet children({ props })}\n <Button\n {...props}\n variant=\"outline\"\n label={dir.charAt(0).toUpperCase() + dir.slice(1)}\n />\n {/snippet}\n {#snippet body()}\n <p class=\"text-on-surface-variant\">\n Drawer from the <strong>{dir}</strong>. Swipe towards the edge to\n dismiss.\n </p>\n {/snippet}\n {#snippet footer()}\n <Button\n label=\"Close\"\n variant=\"outline\"\n onclick={() => (directionOpen[dir] = false)}\n />\n {/snippet}\n </Drawer>\n {/each}\n </div>\n </section>\n\n <!-- Inset -->\n <section class=\"space-y-3\">\n <h2 id=\"Inset\" class=\"text-lg font-semibold\">\n<a href=\"#Inset\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Inset\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Inset drawers have rounded corners and are offset from screen edges.\n </p>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n {#each directions as dir (dir)}\n <Drawer\n bind:open={insetOpen[dir]}\n inset\n direction={dir}\n title=\"Inset {dir.charAt(0).toUpperCase() + dir.slice(1)}\"\n description=\"Inset + {dir} direction.\"\n >\n {#snippet children({ props })}\n <Button\n {...props}\n variant=\"soft\"\n size=\"sm\"\n label=\"Inset {dir.charAt(0).toUpperCase() + dir.slice(1)}\"\n />\n {/snippet}\n {#snippet body()}\n <p class=\"text-on-surface-variant\">\n Inset mode combined with <strong>{dir}</strong> direction.\n </p>\n {/snippet}\n {#snippet footer()}\n <Button\n label=\"Close\"\n variant=\"outline\"\n onclick={() => (insetOpen[dir] = false)}\n />\n {/snippet}\n </Drawer>\n {/each}\n </div>\n </section>\n\n <!-- Handle Options -->\n <section class=\"space-y-3\">\n <h2 id=\"Handle-Options\" class=\"text-lg font-semibold\">\n<a href=\"#Handle-Options\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Handle Options\n </a>\n</h2>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <Drawer\n bind:open={noHandleOpen}\n handle={false}\n title=\"No Handle\"\n description=\"Handle is hidden.\"\n >\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\" label=\"No Handle\" />\n {/snippet}\n {#snippet body()}\n <p class=\"text-on-surface-variant\">No drag handle visible. Still swipeable.</p>\n {/snippet}\n {#snippet footer()}\n <Button\n label=\"Close\"\n variant=\"outline\"\n onclick={() => (noHandleOpen = false)}\n />\n {/snippet}\n </Drawer>\n\n <Drawer\n bind:open={handleOnlyOpen}\n handleOnly\n title=\"Handle Only\"\n description=\"Only the handle can be dragged.\"\n >\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\" label=\"Handle Only\" />\n {/snippet}\n {#snippet body()}\n <p class=\"text-on-surface-variant\">\n Content area is not draggable — only the handle responds to drag.\n </p>\n {/snippet}\n {#snippet footer()}\n <Button\n label=\"Close\"\n variant=\"outline\"\n onclick={() => (handleOnlyOpen = false)}\n />\n {/snippet}\n </Drawer>\n </div>\n </section>\n\n <!-- Overlay & Modal -->\n <section class=\"space-y-3\">\n <h2 id=\"Overlay--Modal\" class=\"text-lg font-semibold\">\n<a href=\"#Overlay--Modal\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Overlay & Modal\n </a>\n</h2>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <Drawer\n bind:open={noOverlayOpen}\n overlay={false}\n modal={false}\n title=\"Non-Modal\"\n description=\"No overlay, background is interactive.\"\n >\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\" label=\"Non-Modal\" />\n {/snippet}\n {#snippet body()}\n <p class=\"text-on-surface-variant\">\n No overlay. You can still interact with the page behind.\n </p>\n {/snippet}\n {#snippet footer()}\n <Button\n label=\"Close\"\n variant=\"outline\"\n onclick={() => (noOverlayOpen = false)}\n />\n {/snippet}\n </Drawer>\n\n <Drawer\n bind:open={scaleBackgroundOpen}\n shouldScaleBackground\n title=\"Scale Background\"\n description=\"Background scales down when opened.\"\n >\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\" label=\"Scale Background\" />\n {/snippet}\n {#snippet body()}\n <p class=\"text-on-surface-variant\">\n The page content behind scales down, creating a layered depth effect.\n </p>\n {/snippet}\n {#snippet footer()}\n <Button\n label=\"Close\"\n variant=\"outline\"\n onclick={() => (scaleBackgroundOpen = false)}\n />\n {/snippet}\n </Drawer>\n </div>\n </section>\n\n <!-- Non-Dismissible -->\n <section class=\"space-y-3\">\n <h2 id=\"Non-Dismissible\" class=\"text-lg font-semibold\">\n<a href=\"#Non-Dismissible\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Non-Dismissible\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Drawer\n bind:open={nonDismissibleOpen}\n dismissible={false}\n title=\"Non-Dismissible\"\n description=\"You must use the close button.\"\n >\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\" label=\"Open (Non-Dismissible)\" />\n {/snippet}\n {#snippet body()}\n <p class=\"text-on-surface-variant\">\n Try swiping or clicking outside — the drawer will not close.\n </p>\n {/snippet}\n {#snippet footer()}\n <Button\n label=\"Close Drawer\"\n variant=\"solid\"\n color=\"primary\"\n onclick={() => (nonDismissibleOpen = false)}\n />\n {/snippet}\n </Drawer>\n </div>\n </section>\n\n <!-- Snap Points -->\n <section class=\"space-y-3\">\n <h2 id=\"Snap-Points\" class=\"text-lg font-semibold\">\n<a href=\"#Snap-Points\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Snap Points\n </a>\n</h2>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <Drawer\n bind:open={snapOpen}\n snapPoints={[0.25, 0.5, 1]}\n fadeFromIndex={1}\n title=\"Snap Points\"\n description=\"Snaps at 25%, 50%, 100%.\"\n >\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\" label=\"3 Snap Points\" />\n {/snippet}\n {#snippet body()}\n <div class=\"space-y-2\">\n <p class=\"text-on-surface-variant\">\n Drag between 25%, 50%, and 100% heights.\n </p>\n {#each Array.from({ length: 12 }, (_, i) => i) as i (i)}\n <div class=\"rounded-md bg-surface-container p-3\">\n <p class=\"text-sm text-on-surface-variant\">Item {i + 1}</p>\n </div>\n {/each}\n </div>\n {/snippet}\n </Drawer>\n\n <Drawer\n bind:open={snapControlledOpen}\n snapPoints={[0.25, 0.5, 1]}\n fadeFromIndex={1}\n bind:activeSnapPoint={activeSnap}\n title=\"Controlled Snap\"\n description=\"Active: {activeSnap}\"\n >\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\" label=\"Controlled Snap\" />\n {/snippet}\n {#snippet body()}\n <div class=\"space-y-3\">\n <p class=\"text-on-surface-variant\">\n Active snap point: <strong>{activeSnap}</strong>\n </p>\n <div class=\"flex gap-2\">\n <Button\n size=\"sm\"\n variant=\"soft\"\n label=\"25%\"\n onclick={() => (activeSnap = 0.25)}\n />\n <Button\n size=\"sm\"\n variant=\"soft\"\n label=\"50%\"\n onclick={() => (activeSnap = 0.5)}\n />\n <Button\n size=\"sm\"\n variant=\"soft\"\n label=\"100%\"\n onclick={() => (activeSnap = 1)}\n />\n </div>\n </div>\n {/snippet}\n </Drawer>\n </div>\n </section>\n\n <!-- Nested Drawers -->\n <section class=\"space-y-3\">\n <h2 id=\"Nested-Drawers\" class=\"text-lg font-semibold\">\n<a href=\"#Nested-Drawers\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Nested Drawers\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Drawer\n bind:open={nestedOuterOpen}\n title=\"Outer Drawer\"\n description=\"This is the parent drawer.\"\n >\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\" label=\"Open Nested\" />\n {/snippet}\n {#snippet body()}\n <p class=\"text-on-surface-variant\">\n Click below to open a child drawer on top of this one.\n </p>\n <div class=\"mt-3\">\n <Drawer\n bind:open={nestedInnerOpen}\n nested\n title=\"Inner Drawer\"\n description=\"Nested child drawer.\"\n >\n {#snippet children({ props })}\n <Button\n {...props}\n variant=\"solid\"\n color=\"primary\"\n label=\"Open Inner Drawer\"\n />\n {/snippet}\n {#snippet body()}\n <p class=\"text-on-surface-variant\">\n This is the nested drawer. Closing it returns to the outer\n drawer.\n </p>\n {/snippet}\n {#snippet footer()}\n <Button\n label=\"Close Inner\"\n variant=\"outline\"\n onclick={() => (nestedInnerOpen = false)}\n />\n {/snippet}\n </Drawer>\n </div>\n {/snippet}\n {#snippet footer()}\n <Button\n label=\"Close\"\n variant=\"outline\"\n onclick={() => (nestedOuterOpen = false)}\n />\n {/snippet}\n </Drawer>\n </div>\n </section>\n\n <!-- Slots -->\n <section class=\"space-y-3\">\n <h2 id=\"Slots\" class=\"text-lg font-semibold\">\n<a href=\"#Slots\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Slots\n </a>\n</h2>\n <div class=\"flex flex-wrap gap-3 rounded-lg bg-surface-container-high p-4\">\n <!-- Custom header -->\n <Drawer bind:open={slotsOpen}>\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\" label=\"Custom Header\" />\n {/snippet}\n {#snippet header()}\n <div class=\"flex items-center gap-3\">\n <div\n class=\"flex size-10 items-center justify-center rounded-full bg-primary text-on-primary\"\n >\n <Icon name=\"lucide:settings\" size=\"20\" />\n </div>\n <div>\n <h3 class=\"font-semibold text-on-surface\">Custom Header</h3>\n <p class=\"text-sm text-on-surface-variant\">With icon and layout</p>\n </div>\n </div>\n {/snippet}\n {#snippet body()}\n <p class=\"text-on-surface-variant\">\n The header slot replaces the default title + description.\n </p>\n {/snippet}\n {#snippet footer()}\n <div class=\"flex gap-2\">\n <Button\n label=\"Cancel\"\n variant=\"outline\"\n class=\"flex-1\"\n onclick={() => (slotsOpen = false)}\n />\n <Button\n label=\"Confirm\"\n variant=\"solid\"\n color=\"primary\"\n class=\"flex-1\"\n onclick={() => (slotsOpen = false)}\n />\n </div>\n {/snippet}\n </Drawer>\n\n <!-- Full content slot -->\n <Drawer bind:open={customContentOpen} title=\"Custom Content\">\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\" label=\"Content Slot\" />\n {/snippet}\n {#snippet content()}\n <div class=\"flex flex-col items-center gap-4 p-6\">\n <div\n class=\"flex size-16 items-center justify-center rounded-full bg-success/10\"\n >\n <Icon name=\"lucide:check-circle\" size=\"32\" class=\"text-success\" />\n </div>\n <h3 class=\"text-xl font-semibold text-on-surface\">Payment Successful</h3>\n <p class=\"text-center text-on-surface-variant\">\n The content slot replaces the entire inner layout.\n </p>\n <Button\n label=\"Done\"\n variant=\"solid\"\n color=\"primary\"\n onclick={() => (customContentOpen = false)}\n />\n </div>\n {/snippet}\n </Drawer>\n </div>\n </section>\n\n <!-- Lifecycle Callbacks -->\n <section class=\"space-y-3\">\n <h2 id=\"Lifecycle-Callbacks\" class=\"text-lg font-semibold\">\n<a href=\"#Lifecycle-Callbacks\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Lifecycle Callbacks\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <div class=\"mb-3 space-y-1\">\n {#if callbackLog.length === 0}\n <p class=\"text-sm text-on-surface-variant italic\">\n Open/close the drawer to see callback logs...\n </p>\n {/if}\n {#each callbackLog as log, i (i)}\n <p class=\"font-mono text-xs text-on-surface-variant\">{log}</p>\n {/each}\n </div>\n <Drawer\n bind:open={callbacksOpen}\n onOpenChange={(v) => logCallback(`onOpenChange(${v})`)}\n onClose={() => logCallback('onClose()')}\n onAnimationEnd={(v) => logCallback(`onAnimationEnd(open=${v})`)}\n title=\"Callback Demo\"\n description=\"Check the log above.\"\n >\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\" label=\"Open (With Callbacks)\" />\n {/snippet}\n {#snippet body()}\n <p class=\"text-on-surface-variant\">\n Open, close, and watch the callback log update.\n </p>\n {/snippet}\n {#snippet footer()}\n <Button\n label=\"Close\"\n variant=\"outline\"\n onclick={() => (callbacksOpen = false)}\n />\n {/snippet}\n </Drawer>\n </div>\n </section>\n\n <!-- Programmatic Control -->\n <section class=\"space-y-3\">\n <h2 id=\"Programmatic-Control\" class=\"text-lg font-semibold\">\n<a href=\"#Programmatic-Control\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Programmatic Control\n </a>\n</h2>\n <div class=\"flex gap-3 rounded-lg bg-surface-container-high p-4\">\n <Button\n variant=\"outline\"\n label=\"Open Programmatically\"\n onclick={() => (programmaticOpen = true)}\n />\n <Drawer\n bind:open={programmaticOpen}\n title=\"Programmatic Drawer\"\n description=\"Opened without a trigger slot.\"\n >\n {#snippet body()}\n <p class=\"text-on-surface-variant\">\n This drawer has no trigger. It was opened by setting open = true externally.\n </p>\n {/snippet}\n {#snippet footer()}\n <Button\n label=\"Close\"\n variant=\"outline\"\n onclick={() => (programmaticOpen = false)}\n />\n {/snippet}\n </Drawer>\n </div>\n </section>\n\n <!-- UI Overrides -->\n <section class=\"space-y-3\">\n <h2 id=\"UI-Slot-Overrides\" class=\"text-lg font-semibold\">\n<a href=\"#UI-Slot-Overrides\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n UI Slot Overrides\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Drawer\n bind:open={uiOverrideOpen}\n title=\"Styled Drawer\"\n description=\"Custom styles via the ui prop.\"\n ui={{\n content: 'bg-primary-container',\n title: 'text-on-primary-container',\n description: 'text-on-primary-container/70',\n handle: 'bg-on-primary-container/40',\n container: 'gap-3'\n }}\n >\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\" label=\"Open Styled Drawer\" />\n {/snippet}\n {#snippet body()}\n <p class=\"text-on-primary-container/80\">\n This drawer overrides content, title, description, handle, and container\n slot classes.\n </p>\n {/snippet}\n {#snippet footer()}\n <Button\n label=\"Close\"\n variant=\"outline\"\n onclick={() => (uiOverrideOpen = false)}\n />\n {/snippet}\n </Drawer>\n </div>\n </section>\n\n <Separator />\n\n <!-- Real World Examples -->\n <section class=\"space-y-3\">\n <h2 id=\"Real-World-Examples\" class=\"text-lg font-semibold\">\n<a href=\"#Real-World-Examples\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World Examples\n </a>\n</h2>\n <div class=\"grid gap-4 sm:grid-cols-2\">\n <!-- Mobile Settings -->\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <p class=\"mb-3 text-sm font-medium\">Mobile Settings Panel</p>\n <Drawer bind:open={settingsOpen} title=\"Settings\">\n {#snippet children({ props })}\n <Button\n {...props}\n variant=\"outline\"\n leadingIcon=\"lucide:settings\"\n label=\"Settings\"\n class=\"w-full\"\n />\n {/snippet}\n {#snippet body()}\n <div class=\"space-y-4\">\n {#each [{ icon: 'lucide:user', label: 'Account', desc: 'Manage your profile' }, { icon: 'lucide:bell', label: 'Notifications', desc: 'Push, email, SMS' }, { icon: 'lucide:shield', label: 'Privacy', desc: 'Data and permissions' }, { icon: 'lucide:palette', label: 'Appearance', desc: 'Theme and display' }, { icon: 'lucide:globe', label: 'Language', desc: 'English (US)' }] as item (item.label)}\n <button\n class=\"flex w-full items-center gap-3 rounded-lg p-2 text-left transition-colors hover:bg-surface-container\"\n >\n <div\n class=\"flex size-10 items-center justify-center rounded-full bg-surface-container-highest\"\n >\n <Icon\n name={item.icon}\n size=\"18\"\n class=\"text-on-surface-variant\"\n />\n </div>\n <div class=\"flex-1\">\n <p class=\"text-sm font-medium text-on-surface\">\n {item.label}\n </p>\n <p class=\"text-xs text-on-surface-variant\">{item.desc}</p>\n </div>\n <Icon\n name=\"lucide:chevron-right\"\n size=\"16\"\n class=\"text-on-surface-variant\"\n />\n </button>\n {/each}\n </div>\n {/snippet}\n {#snippet footer()}\n <Button\n label=\"Sign Out\"\n variant=\"soft\"\n color=\"error\"\n leadingIcon=\"lucide:log-out\"\n class=\"w-full\"\n onclick={() => (settingsOpen = false)}\n />\n {/snippet}\n </Drawer>\n </div>\n\n <!-- Notifications -->\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <p class=\"mb-3 text-sm font-medium\">Notifications Drawer</p>\n <Drawer bind:open={notificationsOpen} direction=\"right\">\n {#snippet children({ props })}\n <Button\n {...props}\n variant=\"outline\"\n leadingIcon=\"lucide:bell\"\n label=\"Notifications\"\n class=\"w-full\"\n />\n {/snippet}\n {#snippet header()}\n <div class=\"flex items-center justify-between\">\n <h3 class=\"text-lg font-semibold text-on-surface\">Notifications</h3>\n <Badge variant=\"solid\" color=\"error\" size=\"xs\" label=\"3 new\" />\n </div>\n {/snippet}\n {#snippet body()}\n <div class=\"space-y-3\">\n {#each [{ title: 'New message', desc: 'John sent you a message', time: '2m ago', unread: true }, { title: 'Build completed', desc: 'Pipeline #482 passed', time: '15m ago', unread: true }, { title: 'Review requested', desc: 'PR #31 needs review', time: '1h ago', unread: true }, { title: 'Deploy finished', desc: 'v2.4.0 deployed to prod', time: '3h ago', unread: false }] as n (n.title)}\n <div\n class=\"flex gap-3 rounded-lg p-2 {n.unread\n ? 'bg-primary/5'\n : ''}\"\n >\n <div\n class=\"mt-0.5 size-2 shrink-0 rounded-full {n.unread\n ? 'bg-primary'\n : 'bg-transparent'}\"\n ></div>\n <div class=\"flex-1\">\n <p class=\"text-sm font-medium text-on-surface\">{n.title}</p>\n <p class=\"text-xs text-on-surface-variant\">{n.desc}</p>\n <p class=\"mt-1 text-xs text-on-surface-variant/60\">\n {n.time}\n </p>\n </div>\n </div>\n {/each}\n </div>\n {/snippet}\n {#snippet footer()}\n <Button\n label=\"Mark all as read\"\n variant=\"ghost\"\n color=\"primary\"\n class=\"w-full\"\n onclick={() => (notificationsOpen = false)}\n />\n {/snippet}\n </Drawer>\n </div>\n\n <!-- Filter Drawer -->\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <p class=\"mb-3 text-sm font-medium\">Filter (Snap Points)</p>\n <Drawer\n bind:open={filterOpen}\n snapPoints={[0.4, 0.75, 1]}\n fadeFromIndex={1}\n title=\"Filters\"\n description=\"Refine your search results.\"\n >\n {#snippet children({ props })}\n <Button\n {...props}\n variant=\"outline\"\n leadingIcon=\"lucide:filter\"\n label=\"Filters\"\n class=\"w-full\"\n />\n {/snippet}\n {#snippet body()}\n <div class=\"space-y-4\">\n <div>\n <p class=\"mb-2 text-sm font-medium text-on-surface\">Category</p>\n <div class=\"flex flex-wrap gap-2\">\n {#each ['All', 'Electronics', 'Clothing', 'Books', 'Home'] as cat (cat)}\n <Button\n size=\"xs\"\n variant={cat === 'All' ? 'solid' : 'outline'}\n color={cat === 'All' ? 'primary' : 'secondary'}\n label={cat}\n />\n {/each}\n </div>\n </div>\n <Separator />\n <div>\n <p class=\"mb-2 text-sm font-medium text-on-surface\">Price Range</p>\n <div class=\"flex flex-wrap gap-2\">\n {#each ['Under $25', '$25-$50', '$50-$100', '$100+'] as price (price)}\n <Button\n size=\"xs\"\n variant=\"outline\"\n color=\"secondary\"\n label={price}\n />\n {/each}\n </div>\n </div>\n </div>\n {/snippet}\n {#snippet footer()}\n <div class=\"flex gap-2\">\n <Button\n label=\"Reset\"\n variant=\"outline\"\n class=\"flex-1\"\n onclick={() => (filterOpen = false)}\n />\n <Button\n label=\"Apply Filters\"\n variant=\"solid\"\n color=\"primary\"\n class=\"flex-1\"\n onclick={() => (filterOpen = false)}\n />\n </div>\n {/snippet}\n </Drawer>\n </div>\n\n <!-- Confirmation Drawer -->\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <p class=\"mb-3 text-sm font-medium\">Confirmation (Non-Dismissible)</p>\n <Drawer bind:open={confirmOpen} dismissible={false} handle={false}>\n {#snippet children({ props })}\n <Button\n {...props}\n variant=\"outline\"\n leadingIcon=\"lucide:trash-2\"\n label=\"Delete Account\"\n color=\"error\"\n class=\"w-full\"\n />\n {/snippet}\n {#snippet content()}\n <div class=\"flex flex-col items-center gap-4 p-6 text-center\">\n <div\n class=\"flex size-14 items-center justify-center rounded-full bg-error/10\"\n >\n <Icon name=\"lucide:alert-triangle\" size=\"28\" class=\"text-error\" />\n </div>\n <h3 class=\"text-lg font-semibold text-on-surface\">Delete Account?</h3>\n <p class=\"text-sm text-on-surface-variant\">\n This action is permanent and cannot be undone.\n </p>\n <div class=\"flex w-full gap-2\">\n <Button\n label=\"Cancel\"\n variant=\"outline\"\n class=\"flex-1\"\n onclick={() => (confirmOpen = false)}\n />\n <Button\n label=\"Delete\"\n variant=\"solid\"\n color=\"error\"\n class=\"flex-1\"\n onclick={() => (confirmOpen = false)}\n />\n </div>\n </div>\n {/snippet}\n </Drawer>\n </div>\n </div>\n </section>\n</div>\n",
172
178
  "dropdown-menu": "<script lang=\"ts\">\n import {\n DropdownMenu,\n Button,\n Separator,\n Badge,\n Icon,\n type DropdownMenuItem,\n type DropdownMenuRadioGroup\n } from '$lib/index.js'\n\n let controlledOpen = $state(false)\n let showStatusBar = $state(true)\n let showActivityBar = $state(false)\n let showPanel = $state(true)\n let selectedTheme = $state('system')\n let clickCount = $state(0)\n let option1 = $state(false)\n let option2 = $state(false)\n let option3 = $state(false)\n\n const notificationItems: DropdownMenuItem[] = [\n { type: 'label', label: 'Notifications' },\n { type: 'separator' },\n { label: 'Svelora v3.0.11 is out!', icon: 'lucide:rocket' },\n { label: 'New component: BentoGrid', icon: 'lucide:layout-grid' },\n { label: 'System maintenance scheduled', icon: 'lucide:wrench' },\n { type: 'separator' },\n { label: 'Mark all as read', icon: 'lucide:check-check' }\n ]\n\n const basicItems: DropdownMenuItem[] = [\n { label: 'New File', icon: 'lucide:file-plus', kbds: ['meta', 'n'] },\n { label: 'New Window', icon: 'lucide:window', kbds: ['meta', 'shift', 'n'] },\n { type: 'separator' },\n { label: 'Open File...', icon: 'lucide:folder-open', kbds: ['meta', 'o'] },\n { label: 'Save', icon: 'lucide:save', kbds: ['meta', 's'] },\n { type: 'separator' },\n { label: 'Exit', icon: 'lucide:log-out' }\n ]\n\n const coloredItems: DropdownMenuItem[] = [\n { label: 'Edit', icon: 'lucide:pencil' },\n { label: 'Duplicate', icon: 'lucide:copy' },\n { type: 'separator' },\n { label: 'Archive', icon: 'lucide:archive', color: 'warning' },\n { label: 'Delete', icon: 'lucide:trash-2', color: 'error' }\n ]\n\n const checkboxItems: DropdownMenuItem[] = $derived([\n { type: 'label', label: 'Appearance' },\n {\n type: 'checkbox',\n label: 'Status Bar',\n checked: showStatusBar,\n onCheckedChange: (v: boolean) => (showStatusBar = v)\n },\n {\n type: 'checkbox',\n label: 'Activity Bar',\n checked: showActivityBar,\n onCheckedChange: (v: boolean) => (showActivityBar = v)\n },\n {\n type: 'checkbox',\n label: 'Panel',\n checked: showPanel,\n onCheckedChange: (v: boolean) => (showPanel = v)\n }\n ])\n\n const radioItems: DropdownMenuItem[] = [\n { type: 'label', label: 'Theme' },\n { type: 'radio', label: 'Light', value: 'light' },\n { type: 'radio', label: 'Dark', value: 'dark' },\n { type: 'radio', label: 'System', value: 'system' }\n ]\n\n const radioGroups: DropdownMenuRadioGroup[] = $derived([\n {\n name: 'theme',\n value: selectedTheme,\n onValueChange: (v: string) => (selectedTheme = v)\n }\n ])\n\n const closeOnSelectItems: DropdownMenuItem[] = $derived([\n {\n label: 'Click me (stays open)',\n icon: 'lucide:hand-metal',\n closeOnSelect: false,\n onSelect: () => clickCount++\n },\n {\n label: 'Me too (stays open)',\n icon: 'lucide:hand-metal',\n closeOnSelect: false,\n onSelect: () => clickCount++\n },\n { type: 'separator' },\n {\n label: 'Click me (closes menu)',\n icon: 'lucide:log-out',\n onSelect: () => clickCount++\n }\n ])\n\n const checkboxCloseOnSelectItems: DropdownMenuItem[] = $derived([\n { type: 'label', label: 'Select Multiple' },\n {\n type: 'checkbox',\n label: 'Option 1',\n checked: option1,\n closeOnSelect: false,\n onCheckedChange: (v: boolean) => (option1 = v)\n },\n {\n type: 'checkbox',\n label: 'Option 2',\n checked: option2,\n closeOnSelect: false,\n onCheckedChange: (v: boolean) => (option2 = v)\n },\n {\n type: 'checkbox',\n label: 'Option 3',\n checked: option3,\n closeOnSelect: false,\n onCheckedChange: (v: boolean) => (option3 = v)\n }\n ])\n\n const submenuItems: DropdownMenuItem[] = [\n { label: 'Back', icon: 'lucide:arrow-left', kbds: ['meta', '['] },\n { label: 'Forward', icon: 'lucide:arrow-right', kbds: ['meta', ']'], disabled: true },\n { label: 'Reload', icon: 'lucide:refresh-cw', kbds: ['meta', 'r'] },\n { type: 'separator' },\n {\n type: 'sub',\n label: 'More Tools',\n icon: 'lucide:wrench',\n items: [\n { label: 'Save Page As...', kbds: ['meta', 's'] },\n { label: 'Create Shortcut...' },\n { label: 'Name Window...' },\n { type: 'separator' },\n { label: 'Developer Tools', kbds: ['meta', 'alt', 'i'] }\n ]\n }\n ]\n\n const profileItems: DropdownMenuItem[] = [\n { type: 'label', label: 'My Account' },\n { label: 'Profile', icon: 'lucide:user', kbds: ['meta', 'p'] },\n { label: 'Billing', icon: 'lucide:credit-card', kbds: ['meta', 'b'] },\n { label: 'Settings', icon: 'lucide:settings', kbds: ['meta', ','] },\n { type: 'separator' },\n { label: 'Team', icon: 'lucide:users' },\n {\n type: 'sub',\n label: 'Invite users',\n icon: 'lucide:user-plus',\n items: [\n { label: 'Email', icon: 'lucide:mail' },\n { label: 'Message', icon: 'lucide:message-square' },\n { type: 'separator' },\n { label: 'More...', icon: 'lucide:plus-circle' }\n ]\n },\n { label: 'New Team', icon: 'lucide:plus', kbds: ['meta', 't'] },\n { type: 'separator' },\n { label: 'GitHub', icon: 'lucide:github' },\n { label: 'Support', icon: 'lucide:life-buoy' },\n { label: 'API', icon: 'lucide:cloud', disabled: true },\n { type: 'separator' },\n { label: 'Log out', icon: 'lucide:log-out', kbds: ['meta', 'shift', 'q'] }\n ]\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">DropdownMenu</h1>\n <p class=\"text-on-surface-variant\">\n Display a menu of actions or options triggered by a button. Built on bits-ui\n DropdownMenu primitive.\n </p>\n </div>\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <DropdownMenu items={basicItems}>\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\">\n File\n <span class=\"ml-2 text-xs opacity-60\">Alt+F</span>\n </Button>\n {/snippet}\n </DropdownMenu>\n </div>\n </section>\n\n <!-- Positions -->\n <section class=\"space-y-3\">\n <h2 id=\"Positions\" class=\"text-lg font-semibold\">\n<a href=\"#Positions\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Positions\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Control dropdown placement with the\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">side</code>\n prop.\n </p>\n <div\n class=\"flex flex-wrap items-center justify-center gap-8 rounded-lg bg-surface-container-high p-8\"\n >\n {#each [{ side: 'top' as const, label: 'Top' }, { side: 'right' as const, label: 'Right' }, { side: 'bottom' as const, label: 'Bottom' }, { side: 'left' as const, label: 'Left' }] as item (item.side)}\n <DropdownMenu items={basicItems.slice(0, 4)} side={item.side}>\n {#snippet children({ props })}\n <Button {...props} variant=\"soft\">{item.label}</Button>\n {/snippet}\n </DropdownMenu>\n {/each}\n </div>\n </section>\n\n <!-- Sizes -->\n <section class=\"space-y-3\">\n <h2 id=\"Sizes\" class=\"text-lg font-semibold\">\n<a href=\"#Sizes\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Sizes\n </a>\n</h2>\n <div class=\"flex flex-wrap items-center gap-4 rounded-lg bg-surface-container-high p-4\">\n {#each [{ size: 'xs' as const, label: 'XS' }, { size: 'sm' as const, label: 'SM' }, { size: 'md' as const, label: 'MD' }, { size: 'lg' as const, label: 'LG' }, { size: 'xl' as const, label: 'XL' }] as item (item.size)}\n <DropdownMenu items={basicItems.slice(0, 4)} size={item.size}>\n {#snippet children({ props })}\n <Button {...props} size={item.size} variant=\"outline\">{item.label}</Button>\n {/snippet}\n </DropdownMenu>\n {/each}\n </div>\n </section>\n\n <!-- Colored Items -->\n <section class=\"space-y-3\">\n <h2 id=\"Colored-Items\" class=\"text-lg font-semibold\">\n<a href=\"#Colored-Items\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Colored Items\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use the\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">color</code>\n prop on items for semantic coloring.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <DropdownMenu items={coloredItems}>\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\" icon=\"lucide:more-horizontal\"\n >Actions</Button\n >\n {/snippet}\n </DropdownMenu>\n </div>\n </section>\n\n <!-- Checkbox Items -->\n <section class=\"space-y-3\">\n <h2 id=\"Checkbox-Items\" class=\"text-lg font-semibold\">\n<a href=\"#Checkbox-Items\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Checkbox Items\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Toggle boolean states with checkbox items.</p>\n <div class=\"flex flex-wrap items-center gap-4 rounded-lg bg-surface-container-high p-4\">\n <DropdownMenu items={checkboxItems}>\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\" icon=\"lucide:layout\">View</Button>\n {/snippet}\n </DropdownMenu>\n <div class=\"text-sm text-on-surface-variant\">\n <p>Status Bar: <code class=\"text-primary\">{showStatusBar}</code></p>\n <p>Activity Bar: <code class=\"text-primary\">{showActivityBar}</code></p>\n <p>Panel: <code class=\"text-primary\">{showPanel}</code></p>\n </div>\n </div>\n </section>\n\n <!-- Radio Items -->\n <section class=\"space-y-3\">\n <h2 id=\"Radio-Items\" class=\"text-lg font-semibold\">\n<a href=\"#Radio-Items\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Radio Items\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Single selection with radio items and groups.</p>\n <div class=\"flex flex-wrap items-center gap-4 rounded-lg bg-surface-container-high p-4\">\n <DropdownMenu items={radioItems} {radioGroups}>\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\" icon=\"lucide:sun-moon\">Theme</Button>\n {/snippet}\n </DropdownMenu>\n <div class=\"text-sm text-on-surface-variant\">\n Selected: <code class=\"text-primary\">{selectedTheme}</code>\n </div>\n </div>\n </section>\n\n <!-- Submenus -->\n <section class=\"space-y-3\">\n <h2 id=\"Submenus\" class=\"text-lg font-semibold\">\n<a href=\"#Submenus\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Submenus\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <DropdownMenu items={submenuItems}>\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\" icon=\"lucide:menu\">Navigate</Button>\n {/snippet}\n </DropdownMenu>\n </div>\n </section>\n\n <!-- Close On Select -->\n <section class=\"space-y-3\">\n <h2 id=\"Close-On-Select\" class=\"text-lg font-semibold\">\n<a href=\"#Close-On-Select\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Close On Select\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\"\n >closeOnSelect: false</code\n >\n to keep the menu open after clicking an item.\n </p>\n <div class=\"flex flex-wrap items-center gap-4 rounded-lg bg-surface-container-high p-4\">\n <DropdownMenu items={closeOnSelectItems}>\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\" icon=\"lucide:mouse-pointer-click\"\n >Actions</Button\n >\n {/snippet}\n </DropdownMenu>\n <DropdownMenu items={checkboxCloseOnSelectItems}>\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\" icon=\"lucide:check-square\"\n >Multi-Select</Button\n >\n {/snippet}\n </DropdownMenu>\n <div class=\"text-sm text-on-surface-variant\">\n <p>Click count: <code class=\"text-primary\">{clickCount}</code></p>\n <p>Options: <code class=\"text-primary\">[{option1}, {option2}, {option3}]</code></p>\n </div>\n </div>\n </section>\n\n <!-- Controlled -->\n <section class=\"space-y-3\">\n <h2 id=\"Controlled\" class=\"text-lg font-semibold\">\n<a href=\"#Controlled\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Controlled\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Control dropdown visibility programmatically with\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">open</code>\n binding.\n </p>\n <div class=\"grid gap-4 sm:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Observe click state</p>\n <div class=\"flex items-center gap-4 rounded-lg bg-surface-container-high p-4\">\n <DropdownMenu items={basicItems.slice(0, 4)} bind:open={controlledOpen}>\n {#snippet children({ props })}\n <Button {...props} variant={controlledOpen ? 'solid' : 'outline'}>\n Click me\n </Button>\n {/snippet}\n </DropdownMenu>\n <span class=\"text-sm text-on-surface-variant\">\n open: <code class=\"text-primary\">{controlledOpen}</code>\n </span>\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Toggle programmatically</p>\n <div class=\"flex items-center gap-4 rounded-lg bg-surface-container-high p-4\">\n <DropdownMenu items={basicItems.slice(0, 4)} open={controlledOpen}>\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\">Target</Button>\n {/snippet}\n </DropdownMenu>\n <Button variant=\"soft\" onclick={() => (controlledOpen = !controlledOpen)}>\n {controlledOpen ? 'Close' : 'Open'}\n </Button>\n </div>\n </div>\n </div>\n </section>\n\n <!-- Custom Header/Footer -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-HeaderFooter\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-HeaderFooter\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Header/Footer\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <DropdownMenu items={basicItems.slice(0, 4)}>\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\" icon=\"lucide:user-circle\">Account</Button>\n {/snippet}\n {#snippet header()}\n <div class=\"px-3 py-2\">\n <p class=\"text-sm font-medium\">John Doe</p>\n <p class=\"text-xs text-on-surface-variant\">john@example.com</p>\n </div>\n <Separator />\n {/snippet}\n {#snippet footer({ close })}\n <Separator />\n <div class=\"p-1\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n class=\"w-full justify-start\"\n icon=\"lucide:log-out\"\n color=\"error\"\n onclick={close}\n >\n Log out\n </Button>\n </div>\n {/snippet}\n </DropdownMenu>\n </div>\n </section>\n\n <!-- Custom Content -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-Content\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-Content\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Content\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Use the\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">content</code>\n slot for fully custom dropdown content.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <DropdownMenu>\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\" icon=\"lucide:palette\">Colors</Button>\n {/snippet}\n {#snippet content({ close })}\n <div class=\"p-4\">\n <p class=\"mb-3 text-sm font-medium\">Choose a color</p>\n <div class=\"grid grid-cols-5 gap-2\">\n {#each ['bg-red-500', 'bg-orange-500', 'bg-yellow-500', 'bg-green-500', 'bg-teal-500', 'bg-blue-500', 'bg-indigo-500', 'bg-purple-500', 'bg-pink-500', 'bg-gray-500'] as color (color)}\n <button\n class=\"size-8 rounded-full {color} transition-all hover:ring-2 hover:ring-outline hover:ring-offset-2\"\n aria-label=\"Select {color\n .replace('bg-', '')\n .replace('-500', '')} color\"\n onclick={close}\n ></button>\n {/each}\n </div>\n </div>\n {/snippet}\n </DropdownMenu>\n </div>\n </section>\n\n <!-- UI Slot Overrides -->\n <section class=\"space-y-3\">\n <h2 id=\"UI-Slot-Overrides\" class=\"text-lg font-semibold\">\n<a href=\"#UI-Slot-Overrides\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n UI Slot Overrides\n </a>\n</h2>\n <div class=\"grid gap-4 sm:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Primary theme</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <DropdownMenu\n items={basicItems.slice(0, 4)}\n ui={{\n content: 'bg-primary text-on-primary ring-primary/50',\n item: 'data-[highlighted]:bg-on-primary/20',\n separator: 'bg-on-primary/20'\n }}\n >\n {#snippet children({ props })}\n <Button {...props}>Primary Style</Button>\n {/snippet}\n </DropdownMenu>\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Custom rounding</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <DropdownMenu\n items={basicItems.slice(0, 4)}\n ui={{ content: 'rounded-xl shadow-2xl' }}\n >\n {#snippet children({ props })}\n <Button {...props} variant=\"outline\">Rounded</Button>\n {/snippet}\n </DropdownMenu>\n </div>\n </div>\n </div>\n </section>\n\n <Separator />\n\n <!-- Real World Examples -->\n <section class=\"space-y-6\">\n <h2 id=\"Real-World-Examples\" class=\"text-lg font-semibold\">\n<a href=\"#Real-World-Examples\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World Examples\n </a>\n</h2>\n\n <div class=\"grid gap-4 md:grid-cols-2\">\n <!-- Profile Menu -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium\">Profile Menu</p>\n <div\n class=\"flex items-center justify-center rounded-lg bg-surface-container-high p-6\"\n >\n <DropdownMenu items={profileItems}>\n {#snippet children({ props })}\n <Button {...props} icon=\"lucide:chevron-down\" trailing>\n <span class=\"flex items-center gap-2\">\n <span\n class=\"flex size-6 items-center justify-center rounded-full bg-primary text-xs text-on-primary\"\n >\n JD\n </span>\n shadcn\n </span>\n </Button>\n {/snippet}\n </DropdownMenu>\n </div>\n </div>\n\n <!-- Context Menu Style -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium\">Right-Click Style</p>\n <div\n class=\"flex items-center justify-center rounded-lg bg-surface-container-high p-6\"\n >\n <DropdownMenu items={coloredItems} arrow align=\"center\">\n {#snippet children({ props })}\n <Button\n {...props}\n variant=\"outline\"\n icon=\"lucide:more-vertical\"\n square\n aria-label=\"More options\"\n />\n {/snippet}\n </DropdownMenu>\n </div>\n </div>\n\n <!-- Notification Bell -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium\">Notification Bell</p>\n <div\n class=\"flex items-center justify-center rounded-lg bg-surface-container-high p-6\"\n >\n <DropdownMenu items={notificationItems} arrow align=\"end\">\n {#snippet children({ props })}\n <Button\n {...props}\n variant=\"ghost\"\n square\n class=\"relative\"\n aria-label=\"Notifications\"\n >\n <Icon name=\"lucide:bell\" size=\"20\" />\n <Badge \n size=\"sm\" \n color=\"error\"\n class=\"absolute top-1 right-1 size-2 p-0\" \n />\n </Button>\n {/snippet}\n </DropdownMenu>\n </div>\n </div>\n </div>\n </section>\n</div>\n",
173
179
  "popover": "<script lang=\"ts\">\n import { Popover, Button, Badge, Icon, Separator } from '$lib/index.js'\n\n let controlledOpen = $state(false)\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">Popover</h1>\n <p class=\"text-on-surface-variant\">\n Display rich content in a floating panel triggered by click. Built on bits-ui Popover\n primitive.\n </p>\n </div>\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Popover>\n <Button variant=\"outline\">Click me</Button>\n {#snippet content()}\n <div class=\"w-72 p-4\">\n <p class=\"text-sm font-medium\">Popover Content</p>\n <p class=\"mt-1 text-sm text-on-surface-variant\">\n This is a simple popover with some informational text.\n </p>\n </div>\n {/snippet}\n </Popover>\n </div>\n </section>\n\n <!-- Positions -->\n <section class=\"space-y-3\">\n <h2 id=\"Positions\" class=\"text-lg font-semibold\">\n<a href=\"#Positions\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Positions\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Control popover placement with the\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">side</code>\n prop.\n </p>\n <div\n class=\"flex flex-wrap items-center justify-center gap-8 rounded-lg bg-surface-container-high p-8\"\n >\n {#each [{ side: 'top' as const, label: 'Top' }, { side: 'right' as const, label: 'Right' }, { side: 'bottom' as const, label: 'Bottom' }, { side: 'left' as const, label: 'Left' }] as item (item.side)}\n <Popover side={item.side}>\n <Button variant=\"soft\">{item.label}</Button>\n {#snippet content()}\n <div class=\"p-3\">\n <p class=\"text-sm\">Popover on {item.side}</p>\n </div>\n {/snippet}\n </Popover>\n {/each}\n </div>\n </section>\n\n <!-- Alignment -->\n <section class=\"space-y-3\">\n <h2 id=\"Alignment\" class=\"text-lg font-semibold\">\n<a href=\"#Alignment\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Alignment\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Control alignment with the\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">align</code>\n prop.\n </p>\n <div\n class=\"flex flex-wrap items-center justify-center gap-8 rounded-lg bg-surface-container-high p-8\"\n >\n {#each [{ align: 'start' as const, label: 'Start' }, { align: 'center' as const, label: 'Center' }, { align: 'end' as const, label: 'End' }] as item (item.align)}\n <Popover side=\"bottom\" align={item.align}>\n <Button variant=\"outline\">{item.label}</Button>\n {#snippet content()}\n <div class=\"w-48 p-3\">\n <p class=\"text-sm\">Aligned to {item.align}</p>\n </div>\n {/snippet}\n </Popover>\n {/each}\n </div>\n </section>\n\n <!-- Arrow -->\n <section class=\"space-y-3\">\n <h2 id=\"Arrow\" class=\"text-lg font-semibold\">\n<a href=\"#Arrow\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Arrow\n </a>\n</h2>\n <div class=\"grid gap-4 sm:grid-cols-2\">\n {#each [{ side: 'top' as const, label: 'Top' }, { side: 'right' as const, label: 'Right' }, { side: 'bottom' as const, label: 'Bottom' }, { side: 'left' as const, label: 'Left' }] as item (item.side)}\n <div\n class=\"flex items-center justify-center rounded-lg bg-surface-container-high p-6\"\n >\n <Popover arrow side={item.side}>\n <Button>{item.label}</Button>\n {#snippet content()}\n <div class=\"p-3\">\n <p class=\"text-sm\">Content with arrow</p>\n </div>\n {/snippet}\n </Popover>\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Close Action -->\n <section class=\"space-y-3\">\n <h2 id=\"Close-Action\" class=\"text-lg font-semibold\">\n<a href=\"#Close-Action\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Close Action\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n The\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">content</code>\n snippet exposes a\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">close</code>\n function to programmatically dismiss the popover.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Popover>\n <Button variant=\"outline\">Confirm Action</Button>\n {#snippet content({ close })}\n <div class=\"w-72 p-4\">\n <div class=\"flex items-center justify-between\">\n <p class=\"font-medium\">Confirm Action</p>\n <Button\n icon=\"lucide:x\"\n square\n variant=\"ghost\"\n size=\"xs\"\n onclick={close}\n />\n </div>\n <p class=\"mt-2 text-sm text-on-surface-variant\">\n Are you sure you want to proceed with this action?\n </p>\n <div class=\"mt-4 flex justify-end gap-2\">\n <Button variant=\"outline\" size=\"sm\" onclick={close}>Cancel</Button>\n <Button size=\"sm\" onclick={close}>Confirm</Button>\n </div>\n </div>\n {/snippet}\n </Popover>\n </div>\n </section>\n\n <!-- Controlled -->\n <section class=\"space-y-3\">\n <h2 id=\"Controlled\" class=\"text-lg font-semibold\">\n<a href=\"#Controlled\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Controlled\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Control popover visibility programmatically with\n <code class=\"rounded bg-surface-container-highest px-1.5 py-0.5 text-xs\">open</code>\n binding.\n </p>\n <div class=\"grid gap-4 sm:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Observe click state</p>\n <div class=\"flex items-center gap-4 rounded-lg bg-surface-container-high p-4\">\n <Popover bind:open={controlledOpen}>\n <Button variant={controlledOpen ? 'solid' : 'outline'}>Click me</Button>\n {#snippet content()}\n <div class=\"w-64 p-4\">\n <p class=\"text-sm\">Controlled popover content</p>\n </div>\n {/snippet}\n </Popover>\n <span class=\"text-sm text-on-surface-variant\">\n open: <code class=\"text-primary\">{controlledOpen}</code>\n </span>\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Toggle programmatically</p>\n <div class=\"flex items-center gap-4 rounded-lg bg-surface-container-high p-4\">\n <Popover open={controlledOpen}>\n <Button variant=\"outline\">Target</Button>\n {#snippet content()}\n <div class=\"w-64 p-4\">\n <p class=\"text-sm\">Programmatic popover</p>\n </div>\n {/snippet}\n </Popover>\n <Button variant=\"soft\" onclick={() => (controlledOpen = !controlledOpen)}>\n {controlledOpen ? 'Close' : 'Open'}\n </Button>\n </div>\n </div>\n </div>\n </section>\n\n <!-- Non-dismissible -->\n <section class=\"space-y-3\">\n <h2 id=\"Non-dismissible\" class=\"text-lg font-semibold\">\n<a href=\"#Non-dismissible\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Non-dismissible\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Prevent dismissing by clicking outside or pressing Escape.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Popover dismissible={false}>\n <Button variant=\"outline\">Non-dismissible</Button>\n {#snippet content({ close })}\n <div class=\"w-72 p-4\">\n <p class=\"text-sm font-medium\">Cannot dismiss by clicking outside</p>\n <p class=\"mt-1 text-sm text-on-surface-variant\">\n You must use the close button to dismiss this popover.\n </p>\n <div class=\"mt-3 flex justify-end\">\n <Button size=\"sm\" onclick={close}>Got it</Button>\n </div>\n </div>\n {/snippet}\n </Popover>\n </div>\n </section>\n\n <!-- UI Slot Overrides -->\n <section class=\"space-y-3\">\n <h2 id=\"UI-Slot-Overrides\" class=\"text-lg font-semibold\">\n<a href=\"#UI-Slot-Overrides\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n UI Slot Overrides\n </a>\n</h2>\n <div class=\"grid gap-4 sm:grid-cols-2\">\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">Custom rounding</p>\n <div\n class=\"flex items-center justify-center rounded-lg bg-surface-container-high p-6\"\n >\n <Popover ui={{ content: 'rounded-xl shadow-2xl' }}>\n <Button variant=\"outline\">Rounded</Button>\n {#snippet content()}\n <div class=\"w-64 p-4\">\n <p class=\"text-sm\">Extra rounded with larger shadow.</p>\n </div>\n {/snippet}\n </Popover>\n </div>\n </div>\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium text-on-surface-variant\">No ring border</p>\n <div\n class=\"flex items-center justify-center rounded-lg bg-surface-container-high p-6\"\n >\n <Popover ui={{ content: 'ring-0 shadow-xl' }}>\n <Button variant=\"outline\">No Ring</Button>\n {#snippet content()}\n <div class=\"w-64 p-4\">\n <p class=\"text-sm\">Ring removed, shadow only.</p>\n </div>\n {/snippet}\n </Popover>\n </div>\n </div>\n </div>\n </section>\n\n <Separator />\n\n <!-- Real World Examples -->\n <section class=\"space-y-6\">\n <h2 id=\"Real-World-Examples\" class=\"text-lg font-semibold\">\n<a href=\"#Real-World-Examples\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World Examples\n </a>\n</h2>\n\n <div class=\"grid gap-4 md:grid-cols-2\">\n <!-- User Profile Card -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium\">User Profile Card</p>\n <div\n class=\"flex items-center justify-center rounded-lg bg-surface-container-high p-6\"\n >\n <Popover>\n <Button variant=\"soft\" icon=\"lucide:user\">Profile</Button>\n {#snippet content()}\n <div class=\"w-72\">\n <div class=\"p-4\">\n <div class=\"flex items-center gap-3\">\n <div\n class=\"flex size-10 items-center justify-center rounded-full bg-primary text-on-primary\"\n >\n <Icon name=\"lucide:user\" size=\"20\" />\n </div>\n <div>\n <p class=\"font-medium\">John Doe</p>\n <p class=\"text-sm text-on-surface-variant\">\n john@example.com\n </p>\n </div>\n </div>\n <Separator class=\"my-3\" />\n <div class=\"flex gap-4 text-sm text-on-surface-variant\">\n <span\n ><strong class=\"text-on-surface\">128</strong> posts</span\n >\n <span\n ><strong class=\"text-on-surface\">1.2k</strong> followers</span\n >\n </div>\n </div>\n <Separator />\n <div class=\"p-2\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n class=\"w-full justify-start\"\n icon=\"lucide:settings\">Settings</Button\n >\n <Button\n variant=\"ghost\"\n size=\"sm\"\n class=\"w-full justify-start\"\n icon=\"lucide:log-out\"\n color=\"error\">Sign out</Button\n >\n </div>\n </div>\n {/snippet}\n </Popover>\n </div>\n </div>\n\n <!-- Share Menu -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium\">Share Menu</p>\n <div\n class=\"flex items-center justify-center rounded-lg bg-surface-container-high p-6\"\n >\n <Popover>\n <Button variant=\"outline\" icon=\"lucide:share-2\">Share</Button>\n {#snippet content({ close })}\n <div class=\"w-56 p-2\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n class=\"w-full justify-start\"\n icon=\"lucide:link\"\n onclick={close}>Copy link</Button\n >\n <Button\n variant=\"ghost\"\n size=\"sm\"\n class=\"w-full justify-start\"\n icon=\"lucide:twitter\"\n onclick={close}>Twitter</Button\n >\n <Button\n variant=\"ghost\"\n size=\"sm\"\n class=\"w-full justify-start\"\n icon=\"lucide:facebook\"\n onclick={close}>Facebook</Button\n >\n <Button\n variant=\"ghost\"\n size=\"sm\"\n class=\"w-full justify-start\"\n icon=\"lucide:mail\"\n onclick={close}>Email</Button\n >\n </div>\n {/snippet}\n </Popover>\n </div>\n </div>\n\n <!-- Notification Panel -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium\">Notification Panel</p>\n <div\n class=\"flex items-center justify-center rounded-lg bg-surface-container-high p-6\"\n >\n <Popover>\n <Button variant=\"outline\" icon=\"lucide:bell\">Notifications</Button>\n {#snippet content()}\n <div class=\"w-80\">\n <div class=\"flex items-center justify-between p-4 pb-2\">\n <p class=\"font-medium\">Notifications</p>\n <Badge label=\"3 new\" size=\"xs\" color=\"primary\" />\n </div>\n <div class=\"divide-y divide-outline-variant\">\n <div class=\"flex gap-3 p-4\">\n <div\n class=\"flex size-8 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary\"\n >\n <Icon name=\"lucide:message-circle\" size=\"16\" />\n </div>\n <div>\n <p class=\"text-sm\">New comment on your post</p>\n <p class=\"text-xs text-on-surface-variant\">\n 2 minutes ago\n </p>\n </div>\n </div>\n <div class=\"flex gap-3 p-4\">\n <div\n class=\"flex size-8 shrink-0 items-center justify-center rounded-full bg-success/10 text-success\"\n >\n <Icon name=\"lucide:user-plus\" size=\"16\" />\n </div>\n <div>\n <p class=\"text-sm\">New follower: Jane Smith</p>\n <p class=\"text-xs text-on-surface-variant\">\n 1 hour ago\n </p>\n </div>\n </div>\n </div>\n </div>\n {/snippet}\n </Popover>\n </div>\n </div>\n\n <!-- Filter Panel -->\n <div class=\"space-y-2\">\n <p class=\"text-sm font-medium\">Filter Panel</p>\n <div\n class=\"flex items-center justify-center rounded-lg bg-surface-container-high p-6\"\n >\n <Popover>\n <Button variant=\"outline\" icon=\"lucide:filter\">Filters</Button>\n {#snippet content({ close })}\n <div class=\"w-72\">\n <div class=\"p-4\">\n <p class=\"font-medium\">Filter Results</p>\n </div>\n <Separator />\n <div class=\"space-y-3 p-4\">\n <div>\n <p class=\"text-sm font-medium\">Status</p>\n <div class=\"mt-1 flex gap-2\">\n <Badge label=\"Active\" color=\"success\" />\n <Badge label=\"Pending\" color=\"warning\" />\n <Badge label=\"Closed\" color=\"error\" />\n </div>\n </div>\n <div>\n <p class=\"text-sm font-medium\">Priority</p>\n <div class=\"mt-1 flex gap-2\">\n <Badge label=\"High\" />\n <Badge label=\"Medium\" />\n <Badge label=\"Low\" />\n </div>\n </div>\n </div>\n <Separator />\n <div class=\"flex justify-end gap-2 p-3\">\n <Button variant=\"ghost\" size=\"sm\" onclick={close}>Reset</Button>\n <Button size=\"sm\" onclick={close}>Apply</Button>\n </div>\n </div>\n {/snippet}\n </Popover>\n </div>\n </div>\n </div>\n </section>\n</div>\n",
@@ -180,6 +186,8 @@
180
186
  "use-clipboard": "<script lang=\"ts\">\n import { useClipboard } from '$lib/index.js'\n import { Button, Input, Textarea, Badge, Icon, Card } from '$lib/index.js'\n\n const clipboard = useClipboard()\n const clipboardLong = useClipboard({ timeout: 5000 })\n\n let inputValue = $state('Hello, Svelora!')\n let textareaValue = $state('const greeting = \"Hello World\";\\nconsole.log(greeting);')\n\n const snippets = [\n { label: 'npm', text: 'npm install svelora', icon: 'lucide:terminal' },\n { label: 'pnpm', text: 'pnpm add svelora', icon: 'lucide:terminal' },\n { label: 'yarn', text: 'yarn add svelora', icon: 'lucide:terminal' }\n ]\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">useClipboard</h1>\n <p class=\"text-on-surface-variant\">\n Reactive clipboard hook. Copies text and tracks the copied state with auto-reset.\n </p>\n </div>\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <div class=\"flex flex-wrap items-center gap-3 rounded-lg bg-surface-container-high p-4\">\n <Button\n onclick={() => clipboard.copy('Hello from Svelora!')}\n icon={clipboard.copied ? 'lucide:check' : 'lucide:copy'}\n color={clipboard.copied ? 'success' : 'primary'}\n >\n {clipboard.copied ? 'Copied!' : 'Copy Text'}\n </Button>\n\n <Badge\n label={clipboard.copied ? 'Copied' : 'Ready'}\n color={clipboard.copied ? 'success' : 'surface'}\n variant=\"subtle\"\n />\n </div>\n </section>\n\n <!-- Custom Timeout -->\n <section class=\"space-y-3\">\n <h2 id=\"Custom-Timeout\" class=\"text-lg font-semibold\">\n<a href=\"#Custom-Timeout\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Custom Timeout\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n The default timeout is 2 seconds. You can set a custom timeout for longer feedback.\n </p>\n <div class=\"flex flex-wrap items-center gap-3 rounded-lg bg-surface-container-high p-4\">\n <Button\n onclick={() => clipboardLong.copy('Copied with 5s timeout!')}\n icon={clipboardLong.copied ? 'lucide:check' : 'lucide:copy'}\n color={clipboardLong.copied ? 'success' : 'secondary'}\n variant=\"outline\"\n >\n {clipboardLong.copied ? 'Copied! (5s reset)' : 'Copy (5s timeout)'}\n </Button>\n </div>\n </section>\n\n <!-- Copy from Input -->\n <section class=\"space-y-3\">\n <h2 id=\"Copy-from-Input\" class=\"text-lg font-semibold\">\n<a href=\"#Copy-from-Input\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Copy from Input\n </a>\n</h2>\n <div class=\"space-y-3 rounded-lg bg-surface-container-high p-4\">\n <div class=\"flex items-center gap-2\">\n <div class=\"flex-1\">\n <Input bind:value={inputValue} placeholder=\"Type something...\" />\n </div>\n <Button\n onclick={() => clipboard.copy(inputValue)}\n icon={clipboard.copied ? 'lucide:check' : 'lucide:copy'}\n color={clipboard.copied ? 'success' : 'primary'}\n variant=\"outline\"\n square\n />\n </div>\n </div>\n </section>\n\n <!-- Copy Code Block -->\n <section class=\"space-y-3\">\n <h2 id=\"Copy-from-Textarea\" class=\"text-lg font-semibold\">\n<a href=\"#Copy-from-Textarea\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Copy from Textarea\n </a>\n</h2>\n <div class=\"space-y-3 rounded-lg bg-surface-container-high p-4\">\n <Textarea bind:value={textareaValue} rows={3} />\n <Button\n onclick={() => clipboard.copy(textareaValue)}\n icon={clipboard.copied ? 'lucide:check' : 'lucide:copy'}\n color={clipboard.copied ? 'success' : 'primary'}\n size=\"sm\"\n >\n {clipboard.copied ? 'Copied!' : 'Copy Code'}\n </Button>\n </div>\n </section>\n\n <!-- Install Commands -->\n <section class=\"space-y-3\">\n <h2 id=\"Real-World-Install-Commands\" class=\"text-lg font-semibold\">\n<a href=\"#Real-World-Install-Commands\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World: Install Commands\n </a>\n</h2>\n <div class=\"space-y-2 rounded-lg bg-surface-container-high p-4\">\n {#each snippets as snippet (snippet.label)}\n <div class=\"flex items-center justify-between rounded-md bg-surface-container p-3\">\n <div class=\"flex items-center gap-3\">\n <Icon name={snippet.icon} size=\"16\" class=\"text-on-surface-variant\" />\n <code class=\"font-mono text-sm\">{snippet.text}</code>\n </div>\n <Button\n onclick={() => clipboard.copy(snippet.text)}\n icon={clipboard.copied ? 'lucide:check' : 'lucide:clipboard'}\n color={clipboard.copied ? 'success' : 'surface'}\n variant=\"ghost\"\n size=\"xs\"\n square\n />\n </div>\n {/each}\n </div>\n </section>\n\n <!-- Share Card -->\n <section class=\"space-y-3\">\n <h2 id=\"Real-World-Share-Link\" class=\"text-lg font-semibold\">\n<a href=\"#Real-World-Share-Link\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World: Share Link\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Card class=\"p-4\">\n <div class=\"space-y-3\">\n <p class=\"text-sm font-medium\">Share this page</p>\n <div class=\"flex items-center gap-2\">\n <div\n class=\"flex-1 truncate rounded-md bg-surface-container px-3 py-2 font-mono text-xs text-on-surface-variant\"\n >\n https://svelora.vercel.app/use-clipboard\n </div>\n <Button\n onclick={() => clipboard.copy('https://svelora.vercel.app/use-clipboard')}\n icon={clipboard.copied ? 'lucide:check' : 'lucide:link'}\n color={clipboard.copied ? 'success' : 'primary'}\n variant=\"soft\"\n size=\"sm\"\n >\n {clipboard.copied ? 'Copied!' : 'Copy Link'}\n </Button>\n </div>\n </div>\n </Card>\n </div>\n </section>\n</div>\n",
181
187
  "use-form-field": "<script lang=\"ts\">\n import { FormField, Input, Select, Switch, Checkbox, Badge, Separator } from '$lib/index.js'\n\n let name = $state('')\n let email = $state('')\n let nameError = $state<string | boolean>(false)\n let emailError = $state<string | boolean>(false)\n\n function validateName() {\n if (!name) {\n nameError = 'Name is required'\n } else if (name.length < 2) {\n nameError = 'Name must be at least 2 characters'\n } else {\n nameError = false\n }\n }\n\n function validateEmail() {\n if (!email) {\n emailError = 'Email is required'\n } else if (!email.includes('@')) {\n emailError = 'Please enter a valid email'\n } else {\n emailError = false\n }\n }\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">useFormField</h1>\n <p class=\"text-on-surface-variant\">\n Access the nearest FormField context from any child component. Provides reactive access\n to name, size, error, and ariaId.\n </p>\n </div>\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Child form controls automatically inherit size, error state, name, and ariaId from the\n parent FormField via <code class=\"rounded bg-surface-container px-1\"\n >useFormField()</code\n >.\n </p>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-4\">\n <FormField label=\"Username\" name=\"username\" description=\"Choose a unique username\">\n <Input placeholder=\"Enter username\" />\n </FormField>\n\n <FormField label=\"Email\" name=\"email\" description=\"We'll never share your email\">\n <Input type=\"email\" placeholder=\"you@example.com\" />\n </FormField>\n </div>\n </section>\n\n <!-- Size Inheritance -->\n <section class=\"space-y-3\">\n <h2 id=\"Size-Inheritance\" class=\"text-lg font-semibold\">\n<a href=\"#Size-Inheritance\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Size Inheritance\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n The Input automatically picks up the size from FormField — no need to pass it\n explicitly.\n </p>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-4\">\n {#each ['sm', 'md', 'lg'] as size (size)}\n {@const formSize = size as 'sm' | 'md' | 'lg'}\n <FormField label=\"Size: {formSize}\" name=\"size-{formSize}\" size={formSize}>\n <Input placeholder=\"Inherits size={formSize}\" />\n </FormField>\n {/each}\n </div>\n </section>\n\n <!-- Error Propagation -->\n <section class=\"space-y-3\">\n <h2 id=\"Error-Propagation\" class=\"text-lg font-semibold\">\n<a href=\"#Error-Propagation\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Error Propagation\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n When FormField has an error, child inputs automatically get error styling and ARIA\n attributes via the shared context.\n </p>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-4\">\n <FormField label=\"Name\" name=\"name-validate\" error={nameError} required>\n <Input bind:value={name} placeholder=\"Enter your name\" oninput={validateName} />\n </FormField>\n\n <FormField label=\"Email\" name=\"email-validate\" error={emailError} required>\n <Input\n bind:value={email}\n type=\"email\"\n placeholder=\"you@example.com\"\n oninput={validateEmail}\n />\n </FormField>\n\n <div class=\"flex items-center gap-2 text-sm text-on-surface-variant\">\n <Badge\n label=\"Name: {nameError || 'valid'}\"\n color={nameError ? 'error' : 'success'}\n variant=\"soft\"\n size=\"sm\"\n />\n <Badge\n label=\"Email: {emailError || 'valid'}\"\n color={emailError ? 'error' : 'success'}\n variant=\"soft\"\n size=\"sm\"\n />\n </div>\n </div>\n </section>\n\n <!-- Different Form Controls -->\n <section class=\"space-y-3\">\n <h2 id=\"Works-with-All-Form-Controls\" class=\"text-lg font-semibold\">\n<a href=\"#Works-with-All-Form-Controls\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Works with All Form Controls\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n All form components (Input, Select, Switch, Checkbox, etc.) consume the same FormField\n context via useFormField().\n </p>\n <div class=\"space-y-4 rounded-lg bg-surface-container-high p-4\">\n <FormField label=\"Full Name\" name=\"full-name\" help=\"Your legal full name\">\n <Input placeholder=\"John Doe\" />\n </FormField>\n\n <Separator />\n\n <FormField label=\"Country\" name=\"country\" help=\"Select your country\">\n <Select\n placeholder=\"Choose a country\"\n items={[\n { label: 'Vietnam', value: 'vn' },\n { label: 'United States', value: 'us' },\n { label: 'Japan', value: 'jp' },\n { label: 'South Korea', value: 'kr' }\n ]}\n />\n </FormField>\n\n <Separator />\n\n <FormField label=\"Notifications\" name=\"notifications\">\n <Switch label=\"Enable email notifications\" />\n </FormField>\n\n <Separator />\n\n <FormField label=\"Terms\" name=\"terms\" error=\"You must accept the terms\">\n <Checkbox label=\"I agree to the terms and conditions\" />\n </FormField>\n </div>\n </section>\n\n <!-- How it works -->\n <section class=\"space-y-3\">\n <h2 id=\"How-It-Works\" class=\"text-lg font-semibold\">\n<a href=\"#How-It-Works\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n How It Works\n </a>\n</h2>\n <div class=\"space-y-3 rounded-lg bg-surface-container-high p-4\">\n <div class=\"rounded-md bg-surface-container p-4\">\n <p class=\"mb-2 text-sm font-medium\">Context Shape</p>\n <pre\n class=\"overflow-x-auto rounded bg-surface-container-highest p-3 font-mono text-xs\">{`interface FormFieldContext {\n name?: string // Form field name for submission\n size: 'sm' | 'md' | 'lg' // Inherited size\n error?: string | boolean // Error state/message\n ariaId: string // ARIA ID for accessibility\n}`}</pre>\n </div>\n\n <div class=\"rounded-md bg-surface-container p-4\">\n <p class=\"mb-2 text-sm font-medium\">Usage in Custom Components</p>\n <pre\n class=\"overflow-x-auto rounded bg-surface-container-highest p-3 font-mono text-xs\">{`import { useFormField } from 'svelora'\n\nconst formField = useFormField()\n// formField?.name → 'email'\n// formField?.size → 'md'\n// formField?.error → 'Invalid email'\n// formField?.ariaId → 'form-field-email'`}</pre>\n </div>\n </div>\n </section>\n</div>\n",
182
188
  "use-click-outside": "<script lang=\"ts\">\n import { useClickOutside } from '$lib/index.js'\n import { Button, Badge, Card } from '$lib/index.js'\n\n let dropdownOpen = $state(false)\n let clickCount = $state(0)\n\n let editMode = $state(false)\n let editValue = $state('Click to edit this text')\n\n let popupOpen = $state(false)\n let enabled = $state(true)\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">useClickOutside</h1>\n <p class=\"text-on-surface-variant\">\n Svelte action that detects clicks outside an element. Use it with\n <code class=\"rounded bg-surface-container px-1\">use:useClickOutside</code>.\n </p>\n </div>\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Click inside the blue box — nothing happens. Click outside — counter increments.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <div class=\"flex items-center gap-4\">\n <div\n use:useClickOutside={{ handler: () => clickCount++ }}\n class=\"flex items-center justify-center rounded-lg border-2 border-dashed border-primary bg-primary/10 p-8\"\n >\n <span class=\"text-sm font-medium\">Click outside me</span>\n </div>\n <Badge\n label=\"Outside clicks: {clickCount}\"\n color={clickCount > 0 ? 'primary' : 'surface'}\n variant=\"subtle\"\n />\n </div>\n </div>\n </section>\n\n <!-- Dropdown -->\n <section class=\"space-y-3\">\n <h2 id=\"Dropdown\" class=\"text-lg font-semibold\">\n<a href=\"#Dropdown\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Dropdown\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <div class=\"relative inline-block\">\n <Button onclick={() => (dropdownOpen = !dropdownOpen)}>\n {dropdownOpen ? 'Close Menu' : 'Open Menu'}\n </Button>\n\n {#if dropdownOpen}\n <div\n use:useClickOutside={{ handler: () => (dropdownOpen = false) }}\n class=\"absolute top-full left-0 z-10 mt-2 w-48 rounded-lg border border-outline-variant bg-surface-container p-1 shadow-lg\"\n >\n {#each ['Profile', 'Settings', 'Logout'] as item (item)}\n <button\n class=\"w-full rounded-md px-3 py-2 text-left text-sm hover:bg-surface-container-high\"\n onclick={() => (dropdownOpen = false)}\n >\n {item}\n </button>\n {/each}\n </div>\n {/if}\n </div>\n </div>\n </section>\n\n <!-- Inline Edit -->\n <section class=\"space-y-3\">\n <h2 id=\"Inline-Edit\" class=\"text-lg font-semibold\">\n<a href=\"#Inline-Edit\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Inline Edit\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Click the text to edit. Click outside to save.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n {#if editMode}\n <div use:useClickOutside={{ handler: () => (editMode = false) }}>\n <input\n bind:value={editValue}\n class=\"w-full max-w-sm rounded-md border border-primary bg-surface px-3 py-2 text-sm ring-2 ring-primary/30 outline-none\"\n />\n </div>\n {:else}\n <button\n class=\"rounded-md px-3 py-2 text-sm hover:bg-surface-container\"\n onclick={() => (editMode = true)}\n >\n {editValue}\n <span class=\"ml-2 text-xs text-on-surface-variant\">(click to edit)</span>\n </button>\n {/if}\n </div>\n </section>\n\n <!-- Enabled/Disabled -->\n <section class=\"space-y-3\">\n <h2 id=\"Enable--Disable\" class=\"text-lg font-semibold\">\n<a href=\"#Enable--Disable\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Enable / Disable\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Toggle the listener on and off with the <code class=\"rounded bg-surface-container px-1\"\n >enabled</code\n > option.\n </p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <div class=\"flex items-center gap-4\">\n <Button variant=\"outline\" size=\"sm\" onclick={() => (enabled = !enabled)}>\n Listener: {enabled ? 'ON' : 'OFF'}\n </Button>\n\n <Button\n variant=\"outline\"\n size=\"sm\"\n onclick={() => (popupOpen = true)}\n disabled={popupOpen}\n >\n Show Popup\n </Button>\n </div>\n\n {#if popupOpen}\n <Card class=\"mt-3 inline-block p-4\">\n <div\n use:useClickOutside={{\n handler: () => (popupOpen = false),\n enabled\n }}\n >\n <p class=\"text-sm font-medium\">Popup Content</p>\n <p class=\"text-xs text-on-surface-variant\">\n {enabled\n ? 'Click outside to close'\n : 'Outside click disabled — use button'}\n </p>\n <Button\n size=\"xs\"\n variant=\"ghost\"\n class=\"mt-2\"\n onclick={() => (popupOpen = false)}\n >\n Close manually\n </Button>\n </div>\n </Card>\n {/if}\n </div>\n </section>\n</div>\n",
189
+ "use-sortable": "<script lang=\"ts\">\n import { Badge, Button, Card, Link, SortableList, useSortable } from '$lib/index.js'\n\n type Task = { id: string; title: string; color: 'primary' | 'success' | 'warning' }\n\n let tasks = $state<Task[]>([\n { id: 'design', title: 'Design system tokens', color: 'primary' },\n { id: 'build', title: 'Build sortable hook', color: 'success' },\n { id: 'docs', title: 'Write documentation', color: 'warning' },\n { id: 'ship', title: 'Ship release', color: 'primary' }\n ])\n\n let horizontalItems = $state([\n { id: 'a', label: 'Alpha' },\n { id: 'b', label: 'Beta' },\n { id: 'c', label: 'Gamma' }\n ])\n\n let sortDisabled = $state(false)\n\n const sortable = useSortable({\n getItems: () => tasks,\n getId: (task) => task.id,\n handle: '[data-sortable-handle]',\n disabled: () => sortDisabled,\n onReorder: (next) => {\n tasks = next\n }\n })\n\n const horizontalSortable = useSortable({\n getItems: () => horizontalItems,\n getId: (item) => item.id,\n axis: 'horizontal',\n handle: '[data-sortable-handle]',\n onReorder: (next) => {\n horizontalItems = next\n }\n })\n\n const sortableOptionsReference = [\n { name: 'getItems', type: '() => T[]', description: 'Reactive getter for the current array.' },\n { name: 'getId', type: '(item: T) => string | number', description: 'Stable id per row (must match data-sortable-id).' },\n { name: 'onReorder', type: '(items: T[]) => void', description: 'Called with the reordered array after drop.' },\n { name: 'axis', type: \"'vertical' | 'horizontal' | 'grid' | (() => ...)\", description: 'Drag axis. Default: vertical. Use grid for CSS grid layouts.' },\n { name: 'handle', type: 'string | (() => string | undefined)', description: 'Handle selector. Omit to drag the whole row.' },\n { name: 'disabled', type: 'boolean | (() => boolean)', description: 'Disable sorting. Default: false.' }\n ] as const\n\n const sortableReturnReference = [\n { name: 'Provider', type: 'Component', description: 'Required wrapper — mounts @dnd-kit/svelte DragDropProvider.' },\n { name: 'container', type: 'Action<HTMLElement>', description: 'Optional list wrapper — sets data-sortable-active while dragging.' },\n { name: 'item', type: 'Action<HTMLElement, { index, item }>', description: 'Attach to each row with the current index and item.' },\n { name: 'draggingId', type: 'string | number | null', description: 'Readonly id of the row currently being dragged.' }\n ] as const\n\n const markupReference = [\n { name: 'use:sortable.item', description: 'Required on each row — pass { index, item } from the #each block.' },\n { name: 'data-sortable-handle', description: 'Optional handle element when using handle selector.' },\n { name: 'data-sortable-item / data-sortable-id', description: 'Set automatically by use:sortable.item for styling hooks.' }\n ] as const\n\n function resetOrder() {\n tasks = [\n { id: 'design', title: 'Design system tokens', color: 'primary' },\n { id: 'build', title: 'Build sortable hook', color: 'success' },\n { id: 'docs', title: 'Write documentation', color: 'warning' },\n { id: 'ship', title: 'Ship release', color: 'primary' }\n ]\n }\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">useSortable</h1>\n <p class=\"text-on-surface-variant\">\n Sortable list reordering powered by\n <a href=\"https://clauderic-dnd-kit.mintlify.app/frameworks/svelte\" class=\"text-primary underline\" target=\"_blank\" rel=\"noreferrer\">@dnd-kit/svelte</a>.\n Wrap markup in\n <code class=\"rounded bg-surface-container-high px-1\">sortable.Provider</code>,\n attach\n <code class=\"rounded bg-surface-container-high px-1\">use:sortable.item</code>\n to each row, and optionally use\n <code class=\"rounded bg-surface-container-high px-1\">use:sortable.container</code>\n on the list wrapper. Browse all demos in the\n <Link href=\"/docs/drag-and-drop\" class=\"text-primary underline\">Drag & Drop playground</Link>.\n </p>\n </div>\n\n <section class=\"space-y-3\">\n <h2 id=\"When-to-use-what\" class=\"text-lg font-semibold\">\n <a href=\"#When-to-use-what\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n When to use what\n </a>\n </h2>\n <div class=\"grid gap-3 md:grid-cols-2\">\n <Card class=\"space-y-2 border border-outline-variant/70 p-4\">\n <p class=\"font-semibold text-on-surface\">useSortable / SortableList</p>\n <p class=\"text-sm text-on-surface-variant\">Reorder items inside one list (tasks, menu items, table rows).</p>\n </Card>\n <Card class=\"space-y-2 border border-outline-variant/70 p-4\">\n <p class=\"font-semibold text-on-surface\">\n <Link href=\"/docs/hooks/use-drag-drop\" class=\"text-primary underline\">useDragDrop</Link>\n </p>\n <p class=\"text-sm text-on-surface-variant\">Move items between containers (kanban columns, trays).</p>\n </Card>\n <Card class=\"space-y-2 border border-outline-variant/70 p-4\">\n <p class=\"font-semibold text-on-surface\">\n <Link href=\"/docs/components/file-upload\" class=\"text-primary underline\">FileUpload</Link>\n </p>\n <p class=\"text-sm text-on-surface-variant\">Drag files from the OS onto a dropzone — not for reordering UI lists.</p>\n </Card>\n <Card class=\"space-y-2 border border-outline-variant/70 p-4\">\n <p class=\"font-semibold text-on-surface\">\n <Link href=\"/docs/components/editor\" class=\"text-primary underline\">Editor dragHandle</Link>\n </p>\n <p class=\"text-sm text-on-surface-variant\">Reorder rich-text blocks inside TipTap — built into Editor, not this hook.</p>\n </Card>\n </div>\n </section>\n\n <section class=\"space-y-3\">\n <h2 id=\"Manual-markup\" class=\"text-lg font-semibold\">\n <a href=\"#Manual-markup\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Manual markup\n </a>\n </h2>\n <sortable.Provider>\n <div use:sortable.container class=\"space-y-2 rounded-lg bg-surface-container-high p-4\">\n {#each tasks as task, index (task.id)}\n <div\n use:sortable.item={{ index, item: task }}\n class={[\n 'flex items-center gap-3 rounded-lg border border-outline-variant/60 bg-surface px-3 py-2',\n sortable.draggingId === task.id && 'opacity-80 shadow-md'\n ]}\n aria-grabbed={sortable.draggingId === task.id}\n >\n <button\n type=\"button\"\n data-sortable-handle\n class=\"cursor-grab rounded-md p-1 text-on-surface-variant hover:bg-surface-container-high active:cursor-grabbing\"\n aria-label=\"Drag to reorder\"\n >\n ::\n </button>\n <Badge label={task.title} color={task.color} variant=\"soft\" />\n </div>\n {/each}\n </div>\n </sortable.Provider>\n <div class=\"flex flex-wrap gap-2\">\n <Button variant=\"outline\" size=\"sm\" label=\"Reset order\" onclick={resetOrder} />\n <Button\n variant={sortDisabled ? 'solid' : 'outline'}\n size=\"sm\"\n label={sortDisabled ? 'Sorting disabled' : 'Disable sorting'}\n onclick={() => (sortDisabled = !sortDisabled)}\n />\n </div>\n </section>\n\n <section class=\"space-y-3\">\n <h2 id=\"Horizontal-axis\" class=\"text-lg font-semibold\">\n <a href=\"#Horizontal-axis\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Horizontal axis\n </a>\n </h2>\n <horizontalSortable.Provider>\n <div use:horizontalSortable.container class=\"flex flex-wrap gap-2 rounded-lg bg-surface-container-high p-4\">\n {#each horizontalItems as item, index (item.id)}\n <div\n use:horizontalSortable.item={{ index, item }}\n class={[\n 'flex items-center gap-2 rounded-lg border border-outline-variant/60 bg-surface px-3 py-2',\n horizontalSortable.draggingId === item.id && 'opacity-80 shadow-md'\n ]}\n >\n <button type=\"button\" data-sortable-handle class=\"cursor-grab px-1\" aria-label=\"Drag\">::</button>\n <span class=\"text-sm font-medium\">{item.label}</span>\n </div>\n {/each}\n </div>\n </horizontalSortable.Provider>\n </section>\n\n <section class=\"space-y-3\">\n <h2 id=\"SortableList-component\" class=\"text-lg font-semibold\">\n <a href=\"#SortableList-component\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n SortableList component\n </a>\n </h2>\n <p class=\"text-sm text-on-surface-variant\">\n Prefer a styled wrapper? See\n <Link href=\"/docs/components/sortable-list\" class=\"text-primary underline\">SortableList</Link>.\n </p>\n <Card class=\"border border-outline-variant/70 p-4\">\n <SortableList bind:items={tasks} getKey={(task) => task.id}>\n {#snippet children({ item })}\n <span class=\"text-sm font-medium\">{item.title}</span>\n {/snippet}\n </SortableList>\n </Card>\n </section>\n\n <section class=\"space-y-3\">\n <h2 id=\"API-Reference\" class=\"text-lg font-semibold\">\n <a href=\"#API-Reference\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n API Reference\n </a>\n </h2>\n <div class=\"grid gap-4 lg:grid-cols-2\">\n <Card class=\"border border-outline-variant/70\">\n <div class=\"space-y-4\">\n <h3 class=\"text-base font-semibold\">useSortable options</h3>\n <div class=\"space-y-3\">\n {#each sortableOptionsReference as item (item.name)}\n <div class=\"border-b border-outline-variant/60 pb-3 last:border-b-0 last:pb-0\">\n <p class=\"font-mono text-xs text-primary\">{item.name}</p>\n <p class=\"mt-1 text-sm font-medium\">{item.type}</p>\n <p class=\"mt-1 text-sm text-on-surface-variant\">{item.description}</p>\n </div>\n {/each}\n </div>\n </div>\n </Card>\n <Card class=\"border border-outline-variant/70\">\n <div class=\"space-y-4\">\n <h3 class=\"text-base font-semibold\">Return value</h3>\n <div class=\"space-y-3\">\n {#each sortableReturnReference as item (item.name)}\n <div class=\"border-b border-outline-variant/60 pb-3 last:border-b-0 last:pb-0\">\n <p class=\"font-mono text-xs text-primary\">{item.name}</p>\n <p class=\"mt-1 text-sm font-medium\">{item.type}</p>\n <p class=\"mt-1 text-sm text-on-surface-variant\">{item.description}</p>\n </div>\n {/each}\n </div>\n <h3 class=\"pt-2 text-base font-semibold\">Markup attributes</h3>\n <div class=\"space-y-3\">\n {#each markupReference as item (item.name)}\n <div class=\"border-b border-outline-variant/60 pb-3 last:border-b-0 last:pb-0\">\n <p class=\"font-mono text-xs text-on-surface-variant\">{item.name}</p>\n <p class=\"mt-1 text-sm text-on-surface-variant\">{item.description}</p>\n </div>\n {/each}\n </div>\n </div>\n </Card>\n </div>\n </section>\n</div>\n",
190
+ "use-drag-drop": "<script lang=\"ts\">\n import { Badge, Button, Card, Link, useDragDrop } from '$lib/index.js'\n\n type Task = { id: string; label: string; lane: 'todo' | 'done' }\n\n let board = $state<{ todo: Task[]; done: Task[] }>({\n todo: [\n { id: 't1', label: 'Design tokens', lane: 'todo' },\n { id: 't2', label: 'Sortable hook', lane: 'todo' },\n { id: 't3', label: 'Docs page', lane: 'todo' }\n ],\n done: [{ id: 'd1', label: 'Kickoff meeting', lane: 'done' }]\n })\n\n let dropDisabled = $state(false)\n let lastDrop = $state('')\n\n const dragDrop = useDragDrop()\n\n function moveTask(task: Task, target: 'todo' | 'done') {\n const source: 'todo' | 'done' = target === 'todo' ? 'done' : 'todo'\n const item = board[source].find((entry) => entry.id === task.id)\n if (!item) return\n\n board = {\n ...board,\n [source]: board[source].filter((entry) => entry.id !== task.id),\n [target]: [...board[target], { ...item, lane: target }]\n }\n lastDrop = `${task.label} → ${target}`\n }\n\n const draggableOptionsReference = [\n { name: 'id', type: 'string', description: 'Unique id for the draggable element.' },\n { name: 'container', type: 'string', description: 'Source zone id — must match the droppable id of the column this item is in.' },\n { name: 'data', type: 'unknown', description: 'Payload passed to droppable onDrop.' },\n { name: 'disabled', type: 'boolean | (() => boolean)', description: 'Prevent dragging.' },\n { name: 'handle', type: 'string', description: 'Optional CSS selector for a drag handle.' }\n ] as const\n\n const droppableOptionsReference = [\n { name: 'id', type: 'string', description: 'Zone id — drops from the same id are ignored.' },\n { name: 'onDrop', type: '(payload) => void', description: 'Called with { id, data? } from the draggable.' },\n { name: 'accept', type: '(payload) => boolean', description: 'Optional filter — return false to reject the drop.' },\n { name: 'disabled', type: 'boolean | (() => boolean)', description: 'Prevent dropping on this zone.' }\n ] as const\n\n const returnReference = [\n { name: 'Provider', type: 'Component', description: 'Required wrapper — mounts @dnd-kit/svelte DragDropProvider.' },\n { name: 'draggable', type: 'Action<HTMLElement, UseDraggableOptions>', description: 'Attach to drag sources.' },\n { name: 'droppable', type: 'Action<HTMLElement, UseDroppableOptions>', description: 'Attach to drop targets.' },\n { name: 'draggingId', type: 'string | null', description: 'Id of the item currently being dragged.' }\n ] as const\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">useDragDrop</h1>\n <p class=\"text-on-surface-variant\">\n Cross-container drag and drop powered by\n <a href=\"https://clauderic-dnd-kit.mintlify.app/frameworks/svelte\" class=\"text-primary underline\" target=\"_blank\" rel=\"noreferrer\">@dnd-kit/svelte</a>.\n Wrap markup in\n <code class=\"rounded bg-surface-container-high px-1\">dragDrop.Provider</code>,\n use\n <code class=\"rounded bg-surface-container-high px-1\">use:dragDrop.draggable</code>\n on sources and\n <code class=\"rounded bg-surface-container-high px-1\">use:dragDrop.droppable</code>\n on targets. Browse all demos in the\n <Link href=\"/docs/drag-and-drop\" class=\"text-primary underline\">Drag & Drop playground</Link>.\n For reordering within one list, use\n <Link href=\"/docs/hooks/use-sortable\" class=\"text-primary underline\">useSortable</Link>.\n </p>\n </div>\n\n <section class=\"space-y-3\">\n <h2 id=\"When-to-use-what\" class=\"text-lg font-semibold\">\n <a href=\"#When-to-use-what\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n When to use what\n </a>\n </h2>\n <Card class=\"border border-info/20 bg-info/10 p-4 text-sm text-on-surface-variant\">\n <p>\n <strong class=\"text-on-surface\">useDragDrop</strong> — kanban, trays, moving cards between columns.\n <strong class=\"text-on-surface\">useSortable</strong> —\n <Link href=\"/docs/hooks/use-sortable\" class=\"text-primary underline\">reorder inside one list</Link>.\n <strong class=\"text-on-surface\">FileUpload</strong> —\n <Link href=\"/docs/components/file-upload\" class=\"text-primary underline\">OS file drops</Link>.\n <strong class=\"text-on-surface\">Editor</strong> —\n <Link href=\"/docs/components/editor#Drag-handle-Phase-3\" class=\"text-primary underline\">block reorder in rich text</Link>.\n </p>\n </Card>\n </section>\n\n <section class=\"space-y-3\">\n <h2 id=\"Kanban-board\" class=\"text-lg font-semibold\">\n <a href=\"#Kanban-board\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Kanban board\n </a>\n </h2>\n <dragDrop.Provider>\n <div class=\"grid gap-4 md:grid-cols-2\">\n <div\n class=\"space-y-3 rounded-lg border border-outline-variant/70 bg-surface-container-low p-4\"\n use:dragDrop.droppable={{\n id: 'todo',\n disabled: () => dropDisabled,\n onDrop: ({ data }) => moveTask(data as Task, 'todo')\n }}\n >\n <h3 class=\"font-semibold\">To do</h3>\n <div class=\"space-y-2\">\n {#each board.todo as task (task.id)}\n <div\n use:dragDrop.draggable={{ id: task.id, container: 'todo', data: task }}\n class={[\n 'cursor-grab rounded-lg border border-outline-variant/60 bg-surface px-3 py-2 active:cursor-grabbing',\n dragDrop.draggingId === task.id && 'opacity-80'\n ]}\n >\n {task.label}\n </div>\n {/each}\n </div>\n </div>\n\n <div\n class=\"space-y-3 rounded-lg border border-outline-variant/70 bg-surface-container-low p-4\"\n use:dragDrop.droppable={{\n id: 'done',\n disabled: () => dropDisabled,\n accept: (payload) => String((payload.data as Task)?.label ?? '').length > 0,\n onDrop: ({ data }) => moveTask(data as Task, 'done')\n }}\n >\n <h3 class=\"font-semibold\">Done</h3>\n <p class=\"text-xs text-on-surface-variant\">accept — rejects empty labels</p>\n <div class=\"space-y-2\">\n {#each board.done as task (task.id)}\n <div\n use:dragDrop.draggable={{ id: task.id, container: 'done', data: task }}\n class={[\n 'cursor-grab rounded-lg border border-success/30 bg-success/10 px-3 py-2 active:cursor-grabbing',\n dragDrop.draggingId === task.id && 'opacity-80'\n ]}\n >\n {task.label}\n </div>\n {/each}\n </div>\n </div>\n </div>\n </dragDrop.Provider>\n\n <div class=\"flex flex-wrap items-center gap-2\">\n {#if dragDrop.draggingId}\n <Badge label=\"Dragging: {dragDrop.draggingId}\" color=\"primary\" variant=\"soft\" />\n {/if}\n {#if lastDrop}\n <Badge label=\"Last drop: {lastDrop}\" color=\"success\" variant=\"soft\" />\n {/if}\n <Button\n variant={dropDisabled ? 'solid' : 'outline'}\n size=\"sm\"\n label={dropDisabled ? 'Drops disabled' : 'Disable drops'}\n onclick={() => (dropDisabled = !dropDisabled)}\n />\n </div>\n </section>\n\n <section class=\"space-y-3\">\n <h2 id=\"API-Reference\" class=\"text-lg font-semibold\">\n <a href=\"#API-Reference\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n API Reference\n </a>\n </h2>\n <div class=\"grid gap-4 lg:grid-cols-2\">\n <Card class=\"border border-outline-variant/70\">\n <div class=\"space-y-4\">\n <h3 class=\"text-base font-semibold\">draggable options</h3>\n <div class=\"space-y-3\">\n {#each draggableOptionsReference as item (item.name)}\n <div class=\"border-b border-outline-variant/60 pb-3 last:border-b-0 last:pb-0\">\n <p class=\"font-mono text-xs text-primary\">{item.name}</p>\n <p class=\"mt-1 text-sm font-medium\">{item.type}</p>\n <p class=\"mt-1 text-sm text-on-surface-variant\">{item.description}</p>\n </div>\n {/each}\n </div>\n </div>\n </Card>\n <Card class=\"border border-outline-variant/70\">\n <div class=\"space-y-4\">\n <h3 class=\"text-base font-semibold\">droppable options</h3>\n <div class=\"space-y-3\">\n {#each droppableOptionsReference as item (item.name)}\n <div class=\"border-b border-outline-variant/60 pb-3 last:border-b-0 last:pb-0\">\n <p class=\"font-mono text-xs text-primary\">{item.name}</p>\n <p class=\"mt-1 text-sm font-medium\">{item.type}</p>\n <p class=\"mt-1 text-sm text-on-surface-variant\">{item.description}</p>\n </div>\n {/each}\n </div>\n </div>\n </Card>\n </div>\n <Card class=\"border border-outline-variant/70\">\n <div class=\"space-y-3\">\n <h3 class=\"text-base font-semibold\">Return value</h3>\n {#each returnReference as item (item.name)}\n <div class=\"border-b border-outline-variant/60 pb-3 last:border-b-0 last:pb-0\">\n <p class=\"font-mono text-xs text-primary\">{item.name}</p>\n <p class=\"mt-1 text-sm font-medium\">{item.type}</p>\n <p class=\"mt-1 text-sm text-on-surface-variant\">{item.description}</p>\n </div>\n {/each}\n </div>\n </Card>\n </section>\n</div>\n",
183
191
  "use-infinite-scroll": "<script lang=\"ts\">\n import { useInfiniteScroll } from '$lib/index.js'\n import { Badge, Button, Skeleton, Table, type TableColumn } from '$lib/index.js'\n\n // ==================== Basic List ====================\n\n let items = $state<{ id: number; title: string }[]>(\n Array.from({ length: 20 }, (_, i) => ({ id: i + 1, title: `Item ${i + 1}` }))\n )\n let hasMore = $state(true)\n let loadCount = $state(0)\n\n async function fetchMore() {\n await new Promise((r) => setTimeout(r, 800))\n const start = items.length\n const next = Array.from({ length: 20 }, (_, i) => ({\n id: start + i + 1,\n title: `Item ${start + i + 1}`\n }))\n items.push(...next)\n loadCount++\n if (items.length >= 100) hasMore = false\n }\n\n const scroll = useInfiniteScroll({\n onLoad: fetchMore,\n threshold: 150,\n enabled: () => hasMore\n })\n\n function reset() {\n items = Array.from({ length: 20 }, (_, i) => ({ id: i + 1, title: `Item ${i + 1}` }))\n hasMore = true\n loadCount = 0\n }\n\n // ==================== Table ====================\n\n interface User {\n id: number\n name: string\n email: string\n role: string\n status: 'active' | 'inactive' | 'pending'\n }\n\n const roles = ['Admin', 'Editor', 'Viewer', 'Moderator']\n const statuses = ['active', 'inactive', 'pending'] as const\n const firstNames = [\n 'Alice',\n 'Bob',\n 'Charlie',\n 'Diana',\n 'Eve',\n 'Frank',\n 'Grace',\n 'Henry',\n 'Iris',\n 'Jack'\n ]\n const lastNames = [\n 'Johnson',\n 'Smith',\n 'Brown',\n 'Prince',\n 'Davis',\n 'Wilson',\n 'Taylor',\n 'Clark',\n 'Lee',\n 'Hall'\n ]\n\n function generateUsers(start: number, count: number): User[] {\n return Array.from({ length: count }, (_, i) => {\n const id = start + i + 1\n const first = firstNames[id % firstNames.length]\n const last = lastNames[Math.floor(id / firstNames.length) % lastNames.length]\n return {\n id,\n name: `${first} ${last}`,\n email: `${first.toLowerCase()}.${last.toLowerCase()}${id}@example.com`,\n role: roles[id % roles.length],\n status: statuses[id % statuses.length]\n }\n })\n }\n\n let users = $state<User[]>(generateUsers(0, 30))\n let tableHasMore = $state(true)\n let tableLoadCount = $state(0)\n\n async function fetchMoreUsers() {\n await new Promise((r) => setTimeout(r, 1000))\n const next = generateUsers(users.length, 30)\n users.push(...next)\n tableLoadCount++\n if (users.length >= 150) tableHasMore = false\n }\n\n const tableScroll = useInfiniteScroll({\n onLoad: fetchMoreUsers,\n threshold: 200,\n enabled: () => tableHasMore\n })\n\n const columns: TableColumn<User>[] = [\n { key: 'id', label: '#', width: 60, align: 'center' },\n { key: 'name', label: 'Name', sortable: true },\n { key: 'email', label: 'Email' },\n { key: 'role', label: 'Role' },\n { key: 'status', label: 'Status' }\n ]\n\n function resetTable() {\n users = generateUsers(0, 30)\n tableHasMore = true\n tableLoadCount = 0\n }\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">useInfiniteScroll</h1>\n <p class=\"text-on-surface-variant\">\n Reactive infinite scroll hook with Svelte action. Triggers a callback when the user\n scrolls near the bottom of a container.\n </p>\n </div>\n\n <!-- Basic List -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Scroll down inside the container to load more items. Stops at 100 items.\n </p>\n <div class=\"flex flex-wrap items-center gap-3\">\n <Badge label=\"Items: {items.length}\" color=\"primary\" variant=\"subtle\" />\n <Badge label=\"Loads: {loadCount}\" color=\"info\" variant=\"subtle\" />\n <Badge\n label={hasMore ? 'Has more' : 'All loaded'}\n color={hasMore ? 'success' : 'surface'}\n variant=\"subtle\"\n />\n <Button size=\"xs\" variant=\"outline\" onclick={reset}>Reset</Button>\n </div>\n <div\n use:scroll.action\n class=\"h-80 space-y-2 overflow-y-auto rounded-lg bg-surface-container-high p-4\"\n >\n {#each items as item (item.id)}\n <div\n class=\"flex items-center justify-between rounded-md bg-surface-container px-4 py-3\"\n >\n <span class=\"text-sm\">{item.title}</span>\n <Badge label=\"#{item.id}\" color=\"surface\" variant=\"outline\" size=\"sm\" />\n </div>\n {/each}\n\n {#if scroll.loading}\n <div class=\"space-y-2 pt-2\">\n <Skeleton class=\"h-11 w-full rounded-md\" />\n <Skeleton class=\"h-11 w-full rounded-md\" />\n <Skeleton class=\"h-11 w-full rounded-md\" />\n </div>\n {/if}\n\n {#if !hasMore}\n <p class=\"py-4 text-center text-sm text-on-surface-variant\">All items loaded</p>\n {/if}\n </div>\n </section>\n\n <!-- Table + Infinite Scroll -->\n <section class=\"space-y-3\">\n <h2 id=\"Table--Infinite-Scroll\" class=\"text-lg font-semibold\">\n<a href=\"#Table--Infinite-Scroll\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Table + Infinite Scroll\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Combine with the Table component for paginated data loading. Scroll the table to\n automatically load more rows. Stops at 150 users.\n </p>\n <div class=\"flex flex-wrap items-center gap-3\">\n <Badge label=\"Users: {users.length}\" color=\"primary\" variant=\"subtle\" />\n <Badge label=\"Loads: {tableLoadCount}\" color=\"info\" variant=\"subtle\" />\n <Badge\n label={tableHasMore ? 'Has more' : 'All loaded'}\n color={tableHasMore ? 'success' : 'surface'}\n variant=\"subtle\"\n />\n <Button size=\"xs\" variant=\"outline\" onclick={resetTable}>Reset</Button>\n </div>\n <Table\n data={users}\n {columns}\n rowKey=\"id\"\n manualPagination\n total={users.length}\n pageSize={users.length}\n sticky=\"header\"\n hoverable\n loading={tableScroll.loading}\n action={tableScroll.action}\n class=\"h-112 overflow-y-auto\"\n >\n {#snippet cellSlot({ column, value })}\n {@const cellValue = String(value ?? '')}\n {#if column.key === 'status'}\n <Badge\n label={cellValue}\n color={cellValue === 'active'\n ? 'success'\n : cellValue === 'pending'\n ? 'warning'\n : 'surface'}\n variant=\"soft\"\n size=\"sm\"\n />\n {:else if column.key === 'role'}\n <Badge label={cellValue} color=\"info\" variant=\"subtle\" size=\"sm\" />\n {:else}\n {cellValue}\n {/if}\n {/snippet}\n\n {#snippet bodyBottomSlot()}\n {#if !tableHasMore}\n <tr>\n <td\n colspan={columns.length}\n class=\"py-4 text-center text-sm text-on-surface-variant\"\n >\n All users loaded\n </td>\n </tr>\n {/if}\n {/snippet}\n </Table>\n </section>\n</div>\n",
184
192
  "use-escape-keydown": "<script lang=\"ts\">\n import { useEscapeKeydown } from '$lib/index.js'\n import { Button, Badge, Card } from '$lib/index.js'\n\n let escCount = $state(0)\n\n let panelOpen = $state(false)\n let confirmOpen = $state(false)\n\n let enabled = $state(true)\n let controlledOpen = $state(false)\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">useEscapeKeydown</h1>\n <p class=\"text-on-surface-variant\">\n Svelte action that listens for the Escape key. Lightweight alternative to\n <code class=\"rounded bg-surface-container px-1\">useKbd</code> when you only need Escape.\n </p>\n </div>\n\n <!-- Basic -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic\" class=\"text-lg font-semibold\">\n<a href=\"#Basic\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Press Escape anywhere on this page.</p>\n <div\n use:useEscapeKeydown={{ handler: () => escCount++ }}\n class=\"flex items-center gap-4 rounded-lg bg-surface-container-high p-4\"\n >\n <Badge\n label=\"Escape pressed: {escCount} time{escCount === 1 ? '' : 's'}\"\n color={escCount > 0 ? 'primary' : 'surface'}\n variant=\"subtle\"\n size=\"md\"\n />\n <Button size=\"xs\" variant=\"ghost\" onclick={() => (escCount = 0)}>Reset</Button>\n </div>\n </section>\n\n <!-- Dismiss Panel -->\n <section class=\"space-y-3\">\n <h2 id=\"Dismiss-Panel\" class=\"text-lg font-semibold\">\n<a href=\"#Dismiss-Panel\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Dismiss Panel\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <Button onclick={() => (panelOpen = true)} disabled={panelOpen}>Show Panel</Button>\n\n {#if panelOpen}\n <Card class=\"mt-3 p-4\">\n <div use:useEscapeKeydown={{ handler: () => (panelOpen = false) }}>\n <p class=\"text-sm font-medium\">Info Panel</p>\n <p class=\"text-xs text-on-surface-variant\">\n Press <kbd\n class=\"rounded border border-outline-variant bg-surface-container px-1.5 py-0.5 font-mono text-xs\"\n >Esc</kbd\n > to close this panel.\n </p>\n </div>\n </Card>\n {/if}\n </div>\n </section>\n\n <!-- Confirmation Dialog -->\n <section class=\"space-y-3\">\n <h2 id=\"Confirmation-Dialog\" class=\"text-lg font-semibold\">\n<a href=\"#Confirmation-Dialog\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Confirmation Dialog\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">Press Escape to cancel the confirmation.</p>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n {#if !confirmOpen}\n <Button color=\"error\" variant=\"soft\" onclick={() => (confirmOpen = true)}>\n Delete Item\n </Button>\n {:else}\n <Card class=\"inline-block p-4\">\n <div use:useEscapeKeydown={{ handler: () => (confirmOpen = false) }}>\n <p class=\"text-sm font-medium\">Are you sure?</p>\n <p class=\"mb-3 text-xs text-on-surface-variant\">\n Press Escape to cancel, or click Confirm.\n </p>\n <div class=\"flex gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n onclick={() => (confirmOpen = false)}\n >\n Cancel\n </Button>\n <Button size=\"sm\" color=\"error\" onclick={() => (confirmOpen = false)}>\n Confirm\n </Button>\n </div>\n </div>\n </Card>\n {/if}\n </div>\n </section>\n\n <!-- Enable/Disable -->\n <section class=\"space-y-3\">\n <h2 id=\"Enable--Disable\" class=\"text-lg font-semibold\">\n<a href=\"#Enable--Disable\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Enable / Disable\n </a>\n</h2>\n <div class=\"rounded-lg bg-surface-container-high p-4\">\n <div class=\"flex items-center gap-4\">\n <Button variant=\"outline\" size=\"sm\" onclick={() => (enabled = !enabled)}>\n Listener: {enabled ? 'ON' : 'OFF'}\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onclick={() => (controlledOpen = true)}\n disabled={controlledOpen}\n >\n Show Box\n </Button>\n </div>\n\n {#if controlledOpen}\n <Card class=\"mt-3 inline-block p-4\">\n <div\n use:useEscapeKeydown={{ handler: () => (controlledOpen = false), enabled }}\n >\n <p class=\"text-sm font-medium\">Controlled Box</p>\n <p class=\"text-xs text-on-surface-variant\">\n {enabled ? 'Press Escape to close' : 'Escape disabled — close manually'}\n </p>\n <Button\n size=\"xs\"\n variant=\"ghost\"\n class=\"mt-2\"\n onclick={() => (controlledOpen = false)}\n >\n Close manually\n </Button>\n </div>\n </Card>\n {/if}\n </div>\n </section>\n</div>\n",
185
193
  "use-debounce": "<script lang=\"ts\">\n import { useDebounce } from '$lib/index.js'\n import { Button, Input, Badge, Card, Icon } from '$lib/index.js'\n\n // ==================== Basic ====================\n let searchQuery = $state('')\n let searchResult = $state('')\n let searchCount = $state(0)\n const searchDebounce = useDebounce({ delay: 500 })\n\n function handleSearch(e: Event) {\n searchQuery = (e.currentTarget as HTMLInputElement).value\n searchDebounce.run(() => {\n searchResult = searchQuery\n searchCount++\n })\n }\n\n // ==================== Auto-save ====================\n let noteText = $state('Start typing to auto-save...')\n let savedText = $state('')\n let saveCount = $state(0)\n const saveDebounce = useDebounce({ delay: 1000 })\n\n function handleNoteInput(e: Event) {\n noteText = (e.currentTarget as HTMLTextAreaElement).value\n saveDebounce.run(() => {\n savedText = noteText\n saveCount++\n })\n }\n\n // ==================== API Simulation ====================\n interface UserResult {\n id: number\n name: string\n }\n\n let apiQuery = $state('')\n let apiResults = $state<UserResult[]>([])\n let apiLoading = $state(false)\n const apiDebounce = useDebounce({ delay: 400 })\n\n const allUsers: UserResult[] = [\n { id: 1, name: 'Alice Johnson' },\n { id: 2, name: 'Bob Smith' },\n { id: 3, name: 'Charlie Brown' },\n { id: 4, name: 'Diana Prince' },\n { id: 5, name: 'Eve Davis' },\n { id: 6, name: 'Frank Wilson' },\n { id: 7, name: 'Grace Taylor' },\n { id: 8, name: 'Henry Clark' }\n ]\n\n function handleApiSearch(e: Event) {\n apiQuery = (e.currentTarget as HTMLInputElement).value\n if (!apiQuery.trim()) {\n apiDebounce.cancel()\n apiResults = []\n apiLoading = false\n return\n }\n apiDebounce.run(async () => {\n apiLoading = true\n await new Promise((r) => setTimeout(r, 300))\n const q = apiQuery.toLowerCase()\n apiResults = allUsers.filter((u) => u.name.toLowerCase().includes(q))\n apiLoading = false\n })\n }\n\n // ==================== Cancel & Flush ====================\n let controlValue = $state('')\n let controlResult = $state('')\n const controlDebounce = useDebounce({ delay: 2000 })\n\n function handleControlInput(e: Event) {\n controlValue = (e.currentTarget as HTMLInputElement).value\n controlDebounce.run(() => {\n controlResult = controlValue\n })\n }\n</script>\n\n<div class=\"space-y-8\">\n <div class=\"space-y-2\">\n <h1 class=\"text-2xl font-bold\">useDebounce</h1>\n <p class=\"text-on-surface-variant\">\n Reactive debounce hook. Delays execution until a pause in calls. Tracks pending state\n and supports cancel/flush.\n </p>\n </div>\n\n <!-- Basic Search -->\n <section class=\"space-y-3\">\n <h2 id=\"Basic-Search\" class=\"text-lg font-semibold\">\n<a href=\"#Basic-Search\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Basic: Search\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Type in the input — the search only fires after 500ms of inactivity.\n </p>\n <div class=\"space-y-3 rounded-lg bg-surface-container-high p-4\">\n <Input\n value={searchQuery}\n oninput={handleSearch}\n placeholder=\"Search something...\"\n leadingIcon=\"lucide:search\"\n />\n <div class=\"flex flex-wrap items-center gap-3\">\n <Badge\n label={searchDebounce.pending ? 'Typing...' : 'Idle'}\n color={searchDebounce.pending ? 'warning' : 'surface'}\n variant=\"soft\"\n />\n <Badge label=\"Executed: {searchCount}x\" color=\"info\" variant=\"subtle\" />\n {#if searchResult}\n <span class=\"text-sm text-on-surface-variant\">\n Last search: <strong>{searchResult}</strong>\n </span>\n {/if}\n </div>\n </div>\n </section>\n\n <!-- Auto-save -->\n <section class=\"space-y-3\">\n <h2 id=\"Auto-save\" class=\"text-lg font-semibold\">\n<a href=\"#Auto-save\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Auto-save\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Content auto-saves 1 second after you stop typing.\n </p>\n <div class=\"space-y-3 rounded-lg bg-surface-container-high p-4\">\n <textarea\n value={noteText}\n oninput={handleNoteInput}\n rows=\"3\"\n class=\"w-full rounded-md border border-outline-variant bg-surface px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-primary/30\"\n ></textarea>\n <div class=\"flex items-center gap-3\">\n {#if saveDebounce.pending}\n <Badge label=\"Unsaved changes...\" color=\"warning\" variant=\"soft\" />\n {:else}\n <Badge\n label={saveCount > 0 ? 'Saved' : 'No changes'}\n color={saveCount > 0 ? 'success' : 'surface'}\n variant=\"soft\"\n />\n {/if}\n {#if savedText}\n <span class=\"text-xs text-on-surface-variant\">\n Last saved: {savedText.length} chars\n </span>\n {/if}\n </div>\n </div>\n </section>\n\n <!-- API Search -->\n <section class=\"space-y-3\">\n <h2 id=\"Real-World-API-Search\" class=\"text-lg font-semibold\">\n<a href=\"#Real-World-API-Search\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Real World: API Search\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Debounced API call with loading state. Try typing \"alice\", \"bob\", or \"grace\".\n </p>\n <div class=\"space-y-3 rounded-lg bg-surface-container-high p-4\">\n <Input\n value={apiQuery}\n oninput={handleApiSearch}\n placeholder=\"Search users...\"\n leadingIcon=\"lucide:users\"\n loading={apiLoading}\n />\n\n {#if apiResults.length > 0}\n <div class=\"space-y-1\">\n {#each apiResults as user (user.id)}\n <div\n class=\"flex items-center gap-3 rounded-md bg-surface-container px-3 py-2\"\n >\n <Icon name=\"lucide:user\" size=\"16\" class=\"text-on-surface-variant\" />\n <span class=\"text-sm\">{user.name}</span>\n </div>\n {/each}\n </div>\n {:else if apiQuery && !apiLoading && !apiDebounce.pending}\n <p class=\"text-sm text-on-surface-variant\">No users found.</p>\n {/if}\n </div>\n </section>\n\n <!-- Cancel & Flush -->\n <section class=\"space-y-3\">\n <h2 id=\"Cancel--Flush\" class=\"text-lg font-semibold\">\n<a href=\"#Cancel--Flush\" class=\"group relative inline-flex items-center no-underline hover:underline focus:outline-none focus-visible:underline w-fit\">\n <span class=\"absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100 text-primary/60 font-normal text-base leading-none\" aria-hidden=\"true\">#</span>\n Cancel & Flush\n </a>\n</h2>\n <p class=\"text-sm text-on-surface-variant\">\n Delay is 2 seconds. Use <strong>Cancel</strong> to discard, or <strong>Flush</strong> to execute\n immediately.\n </p>\n <div class=\"space-y-3 rounded-lg bg-surface-container-high p-4\">\n <Input\n value={controlValue}\n oninput={handleControlInput}\n placeholder=\"Type and use buttons below...\"\n />\n <div class=\"flex flex-wrap items-center gap-3\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n onclick={() => controlDebounce.cancel()}\n disabled={!controlDebounce.pending}\n >\n Cancel\n </Button>\n <Button\n size=\"sm\"\n variant=\"soft\"\n onclick={() => controlDebounce.flush(() => (controlResult = controlValue))}\n disabled={!controlDebounce.pending}\n >\n Flush Now\n </Button>\n <Badge\n label={controlDebounce.pending ? 'Pending (2s)...' : 'Idle'}\n color={controlDebounce.pending ? 'warning' : 'surface'}\n variant=\"soft\"\n />\n </div>\n {#if controlResult}\n <Card class=\"p-3\">\n <p class=\"text-sm\">\n Result: <strong>{controlResult}</strong>\n </p>\n </Card>\n {/if}\n </div>\n </section>\n</div>\n",