svelora 3.0.13 → 3.0.15

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.
@@ -1,5 +1,10 @@
1
1
  import type { StandardSchemaV1 } from '@standard-schema/spec';
2
2
  export type UrlSchema = StandardSchemaV1<string, string>;
3
+ export declare function createUrlSchema(validateFn: (input: unknown) => {
4
+ issues: readonly StandardSchemaV1.Issue[];
5
+ } | {
6
+ value: string;
7
+ }): UrlSchema;
3
8
  export declare const httpUrlSchema: UrlSchema;
4
9
  export declare function isSafeImageSrc(src: string): boolean;
5
10
  export declare const youtubeUrlSchema: UrlSchema;
@@ -1,5 +1,31 @@
1
- import * as v from 'valibot';
2
- export const httpUrlSchema = v.pipe(v.string(), v.trim(), v.nonEmpty('URL is required'), v.url('Please enter a valid URL'), v.regex(/^https?:\/\//i, 'URL must start with http:// or https://'));
1
+ export function createUrlSchema(validateFn) {
2
+ return {
3
+ '~standard': {
4
+ version: 1,
5
+ vendor: 'svelora',
6
+ validate: validateFn
7
+ }
8
+ };
9
+ }
10
+ export const httpUrlSchema = createUrlSchema((input) => {
11
+ if (typeof input !== 'string') {
12
+ return { issues: [{ message: 'Expected a string' }] };
13
+ }
14
+ const trimmed = input.trim();
15
+ if (!trimmed) {
16
+ return { issues: [{ message: 'URL is required' }] };
17
+ }
18
+ try {
19
+ new URL(trimmed);
20
+ }
21
+ catch {
22
+ return { issues: [{ message: 'Please enter a valid URL' }] };
23
+ }
24
+ if (!/^https?:\/\//i.test(trimmed)) {
25
+ return { issues: [{ message: 'URL must start with http:// or https://' }] };
26
+ }
27
+ return { value: trimmed };
28
+ });
3
29
  function normalizeUrl(src) {
4
30
  const s = src.replace(/[\t\n\r]/g, '');
5
31
  let start = 0;
@@ -24,4 +50,22 @@ export function isSafeImageSrc(src) {
24
50
  }
25
51
  return false;
26
52
  }
27
- export const youtubeUrlSchema = v.pipe(v.string(), v.trim(), v.nonEmpty('URL is required'), v.url('Please enter a valid URL'), v.regex(/^https?:\/\/(?:www\.|m\.)?(?:youtube\.com|youtu\.be|youtube-nocookie\.com)\//i, 'Must be a YouTube URL (youtube.com or youtu.be)'));
53
+ export const youtubeUrlSchema = createUrlSchema((input) => {
54
+ if (typeof input !== 'string') {
55
+ return { issues: [{ message: 'Expected a string' }] };
56
+ }
57
+ const trimmed = input.trim();
58
+ if (!trimmed) {
59
+ return { issues: [{ message: 'URL is required' }] };
60
+ }
61
+ try {
62
+ new URL(trimmed);
63
+ }
64
+ catch {
65
+ return { issues: [{ message: 'Please enter a valid URL' }] };
66
+ }
67
+ if (!/^https?:\/\/(?:www\.|m\.)?(?:youtube\.com|youtu\.be|youtube-nocookie\.com)\//i.test(trimmed)) {
68
+ return { issues: [{ message: 'Must be a YouTube URL (youtube.com or youtu.be)' }] };
69
+ }
70
+ return { value: trimmed };
71
+ });
@@ -11,7 +11,7 @@ import { autoGenerateColumns, computePinOffsets, filterByColumns, filterByGlobal
11
11
  import { tableDefaults, tableVariants } from "./table.variants.js";
12
12
  const config = getComponentConfig("table", tableDefaults);
13
13
  const icons = getComponentConfig("icons", iconsDefaults);
14
- let { ref = $bindable(null), as = "div", data = [], columns: columnsProp, rowKey, caption, sorting = $bindable([]), multiSort = false, manualSorting = false, onSortingChange, globalFilter = $bindable(""), globalFilterKeys, manualFiltering = false, onGlobalFilterChange, columnFilters = $bindable({}), onColumnFiltersChange, page = $bindable(0), pageSize = 10, manualPagination = false, onPageChange, selection, selectedRows = $bindable([]), onSelectionChange, columnVisibility = $bindable(), onColumnVisibilityChange, columnPinning = $bindable(), pinnedRows = $bindable([]), onPinnedRowsChange, expandedRows = $bindable([]), onExpandedChange, columnSizing = $bindable({}), onColumnSizingChange, loading = false, loadingColor = config.defaultVariants.loadingColor ?? "primary", loadingAnimation = config.defaultVariants.loadingAnimation ?? "carousel", empty = "No data.", striped = false, hoverable = config.defaultVariants.hoverable ?? true, sticky = false, action, onRowClick, onRowHover, onRowContextmenu, ui, class: className, captionSlot, emptySlot, loadingSlot, expandedSlot, bodyTopSlot, bodyBottomSlot, headerSlot, cellSlot, ...restProps } = $props();
14
+ let { ref = $bindable(null), as = "div", data = [], columns: columnsProp, rowKey, caption, sorting = $bindable([]), multiSort = false, manualSorting = false, onSortingChange, globalFilter = $bindable(""), globalFilterKeys, manualFiltering = false, onGlobalFilterChange, columnFilters = $bindable({}), onColumnFiltersChange, page = $bindable(0), pageSize = 10, manualPagination = false, onPageChange, selection, selectedRows = $bindable([]), onSelectionChange, columnVisibility = $bindable(), onColumnVisibilityChange, columnPinning = $bindable(), pinnedRows = $bindable([]), onPinnedRowsChange, expandedRows = $bindable([]), onExpandedChange, columnSizing = $bindable({}), onColumnSizingChange, loading = false, loadingColor = config.defaultVariants.loadingColor ?? "primary", loadingAnimation = config.defaultVariants.loadingAnimation ?? "carousel", size = config.defaultVariants.size ?? "md", empty = "No data.", striped = false, hoverable = config.defaultVariants.hoverable ?? true, sticky = false, action, onRowClick, onRowHover, onRowContextmenu, ui, class: className, captionSlot, emptySlot, loadingSlot, expandedSlot, bodyTopSlot, bodyBottomSlot, headerSlot, cellSlot, ...restProps } = $props();
15
15
  // =========================================================================
16
16
  // Apply Svelte action on root element
17
17
  // =========================================================================
@@ -75,6 +75,7 @@ const resolvedColumns = $derived.by(() => {
75
75
  });
76
76
  const visibleColumns = $derived(resolveVisibleColumns(resolvedColumns, columnVisibility, columnPinning));
77
77
  const hasFooter = $derived(visibleColumns.some((col) => col.footer));
78
+ const hasColumnFilterRow = $derived(visibleColumns.some((col) => col.filterable === true));
78
79
  const totalColspan = $derived(visibleColumns.length + (selection === "multiple" ? 1 : 0));
79
80
  // =========================================================================
80
81
  // Data Pipeline: filter → sort → paginate
@@ -304,6 +305,7 @@ function getAlignClass(align) {
304
305
  // Variant Classes
305
306
  // =========================================================================
306
307
  const variantSlots = $derived(tableVariants({
308
+ size,
307
309
  hoverable: hoverable || undefined,
308
310
  striped: striped || undefined,
309
311
  sticky: sticky || undefined,
@@ -523,6 +525,60 @@ function tdClass(key) {
523
525
  </th>
524
526
  {/each}
525
527
  </tr>
528
+
529
+ {#if hasColumnFilterRow}
530
+ <tr class="{classes.tr} border-t border-outline-variant/30">
531
+ {#if selection === 'multiple'}
532
+ <th class={classes.th} style="width: 48px"></th>
533
+ {/if}
534
+ {#each visibleColumns as col (col.key)}
535
+ <th
536
+ class="px-2 py-1.5"
537
+ data-pinned={isPinned(col.key) || undefined}
538
+ style={getPinStyle(col.key)}
539
+ >
540
+ {#if col.filterable}
541
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
542
+ <div class="relative">
543
+ <input
544
+ type="text"
545
+ value={columnFilters[col.key] ?? ''}
546
+ placeholder="Filter…"
547
+ aria-label="Filter {col.label ?? col.key}"
548
+ oninput={(e) => {
549
+ const val = (e.currentTarget as HTMLInputElement).value;
550
+ if (val) {
551
+ columnFilters = { ...columnFilters, [col.key]: val };
552
+ } else {
553
+ const { [col.key]: _removed, ...rest } = columnFilters;
554
+ columnFilters = rest;
555
+ }
556
+ onColumnFiltersChange?.(columnFilters);
557
+ page = 0;
558
+ }}
559
+ class="w-full rounded-md border border-outline-variant/40 bg-surface-container-low px-2 py-1 text-xs text-on-surface placeholder:text-on-surface-variant/40 focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/30 transition-colors"
560
+ />
561
+ {#if columnFilters[col.key]}
562
+ <button
563
+ type="button"
564
+ aria-label="Clear filter for {col.label ?? col.key}"
565
+ class="absolute right-1.5 top-1/2 -translate-y-1/2 text-on-surface-variant/50 hover:text-on-surface transition-colors"
566
+ onclick={() => {
567
+ const { [col.key]: _removed, ...rest } = columnFilters;
568
+ columnFilters = rest;
569
+ onColumnFiltersChange?.(columnFilters);
570
+ page = 0;
571
+ }}
572
+ >
573
+ <svg xmlns="http://www.w3.org/2000/svg" class="size-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6L6 18M6 6l12 12"/></svg>
574
+ </button>
575
+ {/if}
576
+ </div>
577
+ {/if}
578
+ </th>
579
+ {/each}
580
+ </tr>
581
+ {/if}
526
582
  </thead>
527
583
 
528
584
  <!-- TBODY -->
@@ -160,6 +160,8 @@ export interface TableProps<T extends Record<string, any> = Record<string, any>>
160
160
  loadingColor?: NonNullable<TableVariantProps['loadingColor']>;
161
161
  /** Loading bar animation. @default 'carousel' */
162
162
  loadingAnimation?: NonNullable<TableVariantProps['loadingAnimation']>;
163
+ /** Table density preset. @default 'md' */
164
+ size?: NonNullable<TableVariantProps['size']>;
163
165
  /** Text shown when data is empty. @default 'No data.' */
164
166
  empty?: string;
165
167
  /** Alternate row background colors. @default false */
@@ -1,5 +1,16 @@
1
1
  import { type VariantProps } from 'tailwind-variants';
2
2
  export declare const tableVariants: import("tailwind-variants").TVReturnType<{
3
+ size: {
4
+ sm: {
5
+ th: string;
6
+ td: string;
7
+ };
8
+ md: {};
9
+ lg: {
10
+ th: string;
11
+ td: string;
12
+ };
13
+ };
3
14
  pinned: {
4
15
  true: {
5
16
  th: string;
@@ -63,6 +74,17 @@ export declare const tableVariants: import("tailwind-variants").TVReturnType<{
63
74
  empty: string;
64
75
  loading: string;
65
76
  }, undefined, {
77
+ size: {
78
+ sm: {
79
+ th: string;
80
+ td: string;
81
+ };
82
+ md: {};
83
+ lg: {
84
+ th: string;
85
+ td: string;
86
+ };
87
+ };
66
88
  pinned: {
67
89
  true: {
68
90
  th: string;
@@ -126,6 +148,17 @@ export declare const tableVariants: import("tailwind-variants").TVReturnType<{
126
148
  empty: string;
127
149
  loading: string;
128
150
  }, import("tailwind-variants").TVReturnType<{
151
+ size: {
152
+ sm: {
153
+ th: string;
154
+ td: string;
155
+ };
156
+ md: {};
157
+ lg: {
158
+ th: string;
159
+ td: string;
160
+ };
161
+ };
129
162
  pinned: {
130
163
  true: {
131
164
  th: string;
@@ -193,6 +226,7 @@ export type TableVariantProps = VariantProps<typeof tableVariants>;
193
226
  export type TableSlots = keyof ReturnType<typeof tableVariants>;
194
227
  export declare const tableDefaults: {
195
228
  defaultVariants: {
229
+ size: "md";
196
230
  hoverable: true;
197
231
  loadingColor: "primary";
198
232
  loadingAnimation: "carousel";
@@ -27,6 +27,17 @@ export const tableVariants = tv({
27
27
  loading: 'py-10 text-center'
28
28
  },
29
29
  variants: {
30
+ size: {
31
+ sm: {
32
+ th: 'px-3 py-2 text-[11px]',
33
+ td: 'px-3 py-2 text-xs'
34
+ },
35
+ md: {},
36
+ lg: {
37
+ th: 'px-5 py-3.5 text-sm',
38
+ td: 'px-5 py-4 text-base'
39
+ }
40
+ },
30
41
  pinned: {
31
42
  true: {
32
43
  th: 'sticky bg-surface-container-low/95 backdrop-blur-sm z-1',
@@ -110,6 +121,7 @@ export const tableVariants = tv({
110
121
  }
111
122
  ],
112
123
  defaultVariants: {
124
+ size: 'md',
113
125
  hoverable: true,
114
126
  loadingColor: 'primary',
115
127
  loadingAnimation: 'carousel'
@@ -118,6 +130,7 @@ export const tableVariants = tv({
118
130
  export const tableDefaults = {
119
131
  defaultVariants: {
120
132
  ...tableVariants.defaultVariants,
133
+ size: 'md',
121
134
  hoverable: true,
122
135
  loadingColor: 'primary',
123
136
  loadingAnimation: 'carousel'
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "version": 1,
3
3
  "packageName": "svelora",
4
- "packageVersion": "3.0.13",
5
- "generatedAt": "2026-06-27T06:22:41.961Z",
4
+ "packageVersion": "3.0.15",
5
+ "generatedAt": "2026-06-28T08:16:02.738Z",
6
6
  "slugs": {
7
7
  "components": [
8
8
  "button",
@@ -133,7 +133,7 @@
133
133
  "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
134
  "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
135
  "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",
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 // ==================== 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
137
  "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
138
  "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
139
  "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",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelora",
3
- "version": "3.0.13",
3
+ "version": "3.0.15",
4
4
  "description": "Modern primitive-based UI component library for Svelte 5",
5
5
  "packageManager": "bun@1.3.14",
6
6
  "author": "asphum",