compote-ui 0.62.1 → 0.62.2

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.
@@ -0,0 +1,80 @@
1
+ # Overlays And Floating UI
2
+
3
+ `Dialog`, `Drawer`, `Menu`, `Popover`, `HoverCard`, `Tooltip`, and `Toast` are namespace exports.
4
+
5
+ Dialog:
6
+
7
+ ```svelte
8
+ <script lang="ts">
9
+ let open = $state(false);
10
+ </script>
11
+
12
+ <Dialog.Root bind:open>
13
+ <Dialog.Title>Confirm Action</Dialog.Title>
14
+ <Dialog.Description>This cannot be undone.</Dialog.Description>
15
+ <Dialog.Footer>
16
+ <Dialog.CloseTrigger>Cancel</Dialog.CloseTrigger>
17
+ <Button onclick={() => (open = false)}>Confirm</Button>
18
+ </Dialog.Footer>
19
+ <Dialog.CloseTrigger />
20
+ </Dialog.Root>
21
+ ```
22
+
23
+ Use `onOpenChange`, not `onClose`:
24
+
25
+ ```svelte
26
+ <Dialog.Root
27
+ bind:open
28
+ onOpenChange={(details) => {
29
+ if (!details.open) handleClose();
30
+ }}
31
+ >
32
+ <Dialog.Title>Title</Dialog.Title>
33
+ </Dialog.Root>
34
+ ```
35
+
36
+ Drawer content renders backdrop and positioner internally:
37
+
38
+ ```svelte
39
+ <Drawer.Root bind:open>
40
+ <Drawer.Content>
41
+ <Drawer.CloseTrigger />
42
+ <Drawer.Header>
43
+ <Drawer.Title>Edit Record</Drawer.Title>
44
+ </Drawer.Header>
45
+ <Drawer.Body>Scrollable content</Drawer.Body>
46
+ <Drawer.Footer>
47
+ <Button variant="ghost" onclick={() => (open = false)}>Cancel</Button>
48
+ <Button>Save</Button>
49
+ </Drawer.Footer>
50
+ </Drawer.Content>
51
+ </Drawer.Root>
52
+ ```
53
+
54
+ AlertDialog is for confirmations:
55
+
56
+ ```svelte
57
+ <AlertDialog
58
+ bind:open={deleteOpen}
59
+ title="Delete item?"
60
+ description="This action cannot be undone."
61
+ variant="destructive"
62
+ confirmLabel="Delete"
63
+ onConfirm={handleDelete}
64
+ />
65
+ ```
66
+
67
+ Toast setup belongs in layout:
68
+
69
+ ```svelte
70
+ <Toast.Toaster />
71
+ ```
72
+
73
+ Then fire toasts anywhere:
74
+
75
+ ```ts
76
+ import { toast } from 'compote-ui';
77
+
78
+ toast.success('Saved');
79
+ toast.error('Save failed', { description: 'Please try again.' });
80
+ ```
@@ -0,0 +1,247 @@
1
+ ---
2
+ name: data-table
3
+ description: >
4
+ Load when using compote-ui/data-table or compote-ui/data-table/virtual.
5
+ Covers TanStack Table v9 wrapper usage, createTable,
6
+ createDataTableColumnHelper, table columns, row selection, filters,
7
+ formatting, toolbars, virtualized imports, and optional peer dependencies.
8
+ metadata:
9
+ type: composition
10
+ library: compote-ui
11
+ library_version: '0.62.1'
12
+ requires:
13
+ - component-usage
14
+ - theming
15
+ sources:
16
+ - package.json
17
+ - src/lib/components/data-table-v9/index.ts
18
+ - src/lib/components/data-table-v9/types.ts
19
+ - src/lib/components/data-table-v9/features.ts
20
+ - src/lib/components/data-table-v9/column-helper.ts
21
+ - src/lib/components/data-table-v9/create-table.svelte.ts
22
+ - src/lib/components/data-table-v9/data-table.svelte
23
+ - src/lib/components/data-table-v9/virtual/data-table-virtualized.svelte
24
+ - src/lib/components/data-table-v9/virtual/data-table-virtual-rows.svelte
25
+ ---
26
+
27
+ This skill builds on component-usage and theming. Read those first for Compote namespace imports and theme token behavior.
28
+
29
+ # Compote UI — Data Table
30
+
31
+ Use `compote-ui/data-table` for regular tables and `compote-ui/data-table/virtual` for virtualized rows.
32
+
33
+ ## Integration Setup
34
+
35
+ ```bash
36
+ bun add @tanstack/svelte-table
37
+ ```
38
+
39
+ ```svelte
40
+ <script lang="ts">
41
+ import * as DataTable from 'compote-ui/data-table';
42
+
43
+ type Invoice = {
44
+ id: string;
45
+ customer: string;
46
+ total: number;
47
+ paid: boolean;
48
+ };
49
+
50
+ let invoices = $state<Invoice[]>([
51
+ { id: 'inv_1', customer: 'Ada', total: 1200, paid: true },
52
+ { id: 'inv_2', customer: 'Grace', total: 800, paid: false }
53
+ ]);
54
+
55
+ const col = DataTable.createDataTableColumnHelper<Invoice>();
56
+ const columns = col.columns([
57
+ col.accessor('id', { header: 'ID', enableHiding: false }),
58
+ col.accessor('customer', { header: 'Customer', grow: true }),
59
+ col.accessor('total', { header: 'Total', type: 'currency', align: 'right' }),
60
+ col.accessor('paid', { header: 'Paid', type: 'boolean', align: 'center' })
61
+ ]);
62
+
63
+ const table = DataTable.createTable({
64
+ get data() {
65
+ return invoices;
66
+ },
67
+ columns,
68
+ getRowId: (row) => row.id,
69
+ enableRowSelection: true
70
+ });
71
+ </script>
72
+
73
+ <DataTable.Toolbar>
74
+ <DataTable.Title>Invoices</DataTable.Title>
75
+ {#snippet center()}
76
+ <DataTable.Search {table} class="w-56" />
77
+ {/snippet}
78
+ {#snippet right()}
79
+ <DataTable.ColumnFilter {table} />
80
+ <DataTable.ColumnVisibility {table} />
81
+ {/snippet}
82
+ </DataTable.Toolbar>
83
+
84
+ <div class="h-96 min-h-0">
85
+ <DataTable.Root {table} caption="Invoices" />
86
+ </div>
87
+ ```
88
+
89
+ ## Core Integration Patterns
90
+
91
+ ### Use accessorFn with an explicit id
92
+
93
+ ```ts
94
+ const columns = col.columns([
95
+ col.accessorFn((row) => row.quantity * row.price, {
96
+ id: 'total',
97
+ header: 'Total',
98
+ type: 'currency',
99
+ align: 'right'
100
+ })
101
+ ]);
102
+ ```
103
+
104
+ ### Make runtime column changes reactive
105
+
106
+ ```ts
107
+ const idCol = col.accessor('id', { header: 'ID', enableHiding: false });
108
+ const totalCol = col.accessor('total', { header: 'Total', type: 'currency' });
109
+
110
+ let columns = $state.raw([idCol]);
111
+
112
+ const table = DataTable.createTable({
113
+ get data() {
114
+ return invoices;
115
+ },
116
+ get columns() {
117
+ return columns;
118
+ },
119
+ getRowId: (row) => row.id
120
+ });
121
+
122
+ function toggleTotal() {
123
+ columns = columns.includes(totalCol)
124
+ ? columns.filter((column) => column !== totalCol)
125
+ : [...columns, totalCol];
126
+ }
127
+ ```
128
+
129
+ ### Use virtual import for large row sets
130
+
131
+ ```bash
132
+ bun add @tanstack/svelte-virtual
133
+ ```
134
+
135
+ ```svelte
136
+ <script lang="ts">
137
+ import * as VirtualDataTable from 'compote-ui/data-table/virtual';
138
+
139
+ const table = VirtualDataTable.createTable({
140
+ get data() {
141
+ return rows;
142
+ },
143
+ columns,
144
+ getRowId: (row) => row.id,
145
+ enableRowSelection: true
146
+ });
147
+ </script>
148
+
149
+ <div class="h-96 min-h-0">
150
+ <VirtualDataTable.Root {table} caption="Large dataset" />
151
+ </div>
152
+ ```
153
+
154
+ ## Common Mistakes
155
+
156
+ ### CRITICAL Missing id for accessorFn column
157
+
158
+ Wrong:
159
+
160
+ ```ts
161
+ col.accessorFn((row) => row.a + row.b, { header: 'Total' });
162
+ ```
163
+
164
+ Correct:
165
+
166
+ ```ts
167
+ col.accessorFn((row) => row.a + row.b, { id: 'total', header: 'Total' });
168
+ ```
169
+
170
+ Accessor function columns cannot derive a stable column id from a key.
171
+
172
+ Source: `src/lib/components/data-table-v9/create-table.svelte.ts`
173
+
174
+ ### HIGH Non-reactive reassigned columns
175
+
176
+ Wrong:
177
+
178
+ ```ts
179
+ let columns = $state([idCol, nameCol]);
180
+ const table = DataTable.createTable({ data: rows, columns });
181
+ ```
182
+
183
+ Correct:
184
+
185
+ ```ts
186
+ let columns = $state.raw([idCol, nameCol]);
187
+ const table = DataTable.createTable({
188
+ get data() {
189
+ return rows;
190
+ },
191
+ get columns() {
192
+ return columns;
193
+ }
194
+ });
195
+ ```
196
+
197
+ Reassigned columns must be passed through a getter and kept raw so identity checks keep working.
198
+
199
+ Source: `src/routes/components/data-table-v9/+page.svelte`, `src/lib/components/data-table-v9/create-table.svelte.ts`
200
+
201
+ ### HIGH Rendering table without height constraint
202
+
203
+ Wrong:
204
+
205
+ ```svelte
206
+ <DataTable.Root {table} />
207
+ ```
208
+
209
+ Correct:
210
+
211
+ ```svelte
212
+ <div class="h-96 min-h-0">
213
+ <DataTable.Root {table} caption="Invoices" />
214
+ </div>
215
+ ```
216
+
217
+ `DataTable.Root` fills its container and uses internal scrolling.
218
+
219
+ Source: `src/lib/components/data-table-v9/data-table.svelte`
220
+
221
+ ### HIGH Importing virtual table without optional peer
222
+
223
+ Wrong:
224
+
225
+ ```ts
226
+ import * as VirtualDataTable from 'compote-ui/data-table/virtual';
227
+ ```
228
+
229
+ Correct:
230
+
231
+ ```bash
232
+ bun add @tanstack/svelte-virtual
233
+ ```
234
+
235
+ ```ts
236
+ import * as VirtualDataTable from 'compote-ui/data-table/virtual';
237
+ ```
238
+
239
+ The virtual subpath imports `@tanstack/svelte-virtual`.
240
+
241
+ Source: `package.json`, `src/lib/components/data-table-v9/virtual/data-table-virtual-rows.svelte`
242
+
243
+ ## References
244
+
245
+ - [Column options](references/column-options.md)
246
+ - [Table state and reactivity](references/table-state-and-reactivity.md)
247
+ - [Virtual table](references/virtual-table.md)
@@ -0,0 +1,53 @@
1
+ # Column Options
2
+
3
+ Column helper:
4
+
5
+ ```ts
6
+ const col = DataTable.createDataTableColumnHelper<Row>();
7
+ ```
8
+
9
+ Accessors:
10
+
11
+ ```ts
12
+ col.accessor('fieldName', { header: 'Field' });
13
+ col.accessorFn((row) => row.a + row.b, { id: 'sum', header: 'Sum' });
14
+ col.group('Group', [col.accessor('name', { header: 'Name' })]);
15
+ col.columns([col.accessor('id', { header: 'ID' })]);
16
+ ```
17
+
18
+ Common options:
19
+
20
+ - `header: string`
21
+ - `type?: 'text' | 'number' | 'currency' | 'percent' | 'boolean' | 'select' | 'url' | 'phone' | 'date' | 'time' | 'date-time'`
22
+ - `align?: 'left' | 'center' | 'right'`
23
+ - `size?: number`
24
+ - `minSize?: number`
25
+ - `maxSize?: number`
26
+ - `enableResizing?: boolean`
27
+ - `enableHiding?: boolean`
28
+ - `enableSorting?: boolean`
29
+ - `enableColumnFilter?: boolean`
30
+ - `filterFn?: FilterFn | string`
31
+ - `formatOptions?: Intl.NumberFormatOptions | Intl.DateTimeFormatOptions`
32
+ - `formatLocale?: string`
33
+ - `cell?: (value, row) => string | number | boolean | null | undefined`
34
+ - `cellComponent?: Component`
35
+ - `cellProps?: (value, row) => Record<string, unknown>`
36
+ - `cellSnippet?: Snippet`
37
+ - `pinned?: 'left' | 'right'`
38
+ - `grow?: boolean`
39
+ - `sum?: boolean`
40
+ - `footer?: (values: unknown[]) => string | number | undefined`
41
+
42
+ Type defaults:
43
+
44
+ - `number`, `currency`: right aligned, size `120`
45
+ - `percent`: right aligned, size `100`
46
+ - `date`: center aligned, size `110`
47
+ - `time`: center aligned, size `80`
48
+ - `date-time`: center aligned, size `160`
49
+ - `boolean`: center aligned, size `90`
50
+ - `url`: center aligned, sorting disabled, size `60`
51
+ - `phone`: left aligned, size `160`
52
+
53
+ Use one `grow: true` column to absorb extra horizontal space.
@@ -0,0 +1,45 @@
1
+ # Table State And Reactivity
2
+
3
+ Use reactive getters for state that changes:
4
+
5
+ ```ts
6
+ const table = DataTable.createTable({
7
+ get data() {
8
+ return rows;
9
+ },
10
+ get columns() {
11
+ return columns;
12
+ },
13
+ getRowId: (row) => row.id
14
+ });
15
+ ```
16
+
17
+ Use `$state.raw` for column arrays that are reassigned:
18
+
19
+ ```ts
20
+ let columns = $state.raw([idCol, nameCol]);
21
+ ```
22
+
23
+ Read table state:
24
+
25
+ ```ts
26
+ const selected = $derived(table.getSelectedRowModel().rows.map((row) => row.original));
27
+ const sorting = $derived(table.store.state.sorting);
28
+ const filters = $derived(table.store.state.columnFilters);
29
+ ```
30
+
31
+ Persist column visibility with the observer callback:
32
+
33
+ ```ts
34
+ const table = DataTable.createTable({
35
+ get data() {
36
+ return rows;
37
+ },
38
+ columns,
39
+ onColumnVisibilityChange: (visibility) => {
40
+ localStorage.setItem('column-visibility', JSON.stringify(visibility));
41
+ }
42
+ });
43
+ ```
44
+
45
+ Initial `columnVisibility`, `columnSizing`, and `columnPinning` are computed when the table is created. Columns added later default to visible and use their own size.
@@ -0,0 +1,44 @@
1
+ # Virtual Table
2
+
3
+ Use `compote-ui/data-table/virtual` for large row counts.
4
+
5
+ ```bash
6
+ bun add @tanstack/svelte-virtual
7
+ ```
8
+
9
+ ```svelte
10
+ <script lang="ts">
11
+ import * as VirtualDataTable from 'compote-ui/data-table/virtual';
12
+
13
+ const table = VirtualDataTable.createTable({
14
+ get data() {
15
+ return rows;
16
+ },
17
+ columns,
18
+ getRowId: (row) => row.id,
19
+ enableRowSelection: true
20
+ });
21
+ </script>
22
+
23
+ <VirtualDataTable.Toolbar>
24
+ <VirtualDataTable.Title>Large Table</VirtualDataTable.Title>
25
+ {#snippet center()}
26
+ <VirtualDataTable.Search {table} class="w-56" />
27
+ {/snippet}
28
+ {#snippet right()}
29
+ <VirtualDataTable.ColumnFilter {table} />
30
+ <VirtualDataTable.ColumnVisibility {table} />
31
+ {/snippet}
32
+ </VirtualDataTable.Toolbar>
33
+
34
+ <div class="h-96 min-h-0">
35
+ <VirtualDataTable.Root {table} caption="Large dataset" />
36
+ </div>
37
+ ```
38
+
39
+ Differences from regular table:
40
+
41
+ - Import path is `compote-ui/data-table/virtual`.
42
+ - Add the optional `@tanstack/svelte-virtual` peer.
43
+ - Rows are absolutely positioned with transforms.
44
+ - The table uses grid layout internally.
@@ -0,0 +1,173 @@
1
+ ---
2
+ name: getting-started
3
+ description: >
4
+ Load when setting up compote-ui in a Svelte or SvelteKit app. Covers npm install,
5
+ bun add, importing tailwindcss before compote-ui/theme.css, first imports from
6
+ compote-ui, Toast.Toaster placement, Svelte 5 runes usage, and peer dependency basics.
7
+ metadata:
8
+ type: lifecycle
9
+ library: compote-ui
10
+ library_version: '0.62.1'
11
+ sources:
12
+ - package.json
13
+ - README.md
14
+ - CLAUDE.md
15
+ - AGENTS.md
16
+ - src/lib/index.ts
17
+ - src/routes/layout.css
18
+ - src/lib/theme.css
19
+ ---
20
+
21
+ # Compote UI — Getting Started
22
+
23
+ Use Svelte 5 runes, import Tailwind first, then import `compote-ui/theme.css`.
24
+
25
+ ## Setup
26
+
27
+ ```bash
28
+ npm install compote-ui
29
+ ```
30
+
31
+ ```bash
32
+ bun add compote-ui
33
+ ```
34
+
35
+ ```css
36
+ @import 'tailwindcss';
37
+ @import 'compote-ui/theme.css';
38
+ ```
39
+
40
+ ```svelte
41
+ <script lang="ts">
42
+ import { Button, Card } from 'compote-ui';
43
+ </script>
44
+
45
+ <Card.Root>
46
+ <Card.Header>
47
+ <Card.Title>Account</Card.Title>
48
+ <Card.Description>Manage account settings.</Card.Description>
49
+ </Card.Header>
50
+ <Card.Content>
51
+ <Button>Save</Button>
52
+ </Card.Content>
53
+ </Card.Root>
54
+ ```
55
+
56
+ ## Core Patterns
57
+
58
+ ### Use Svelte 5 bindings
59
+
60
+ ```svelte
61
+ <script lang="ts">
62
+ import { Dialog, Button } from 'compote-ui';
63
+
64
+ let open = $state(false);
65
+ </script>
66
+
67
+ <Button onclick={() => (open = true)}>Open</Button>
68
+
69
+ <Dialog.Root bind:open>
70
+ <Dialog.Title>Confirm</Dialog.Title>
71
+ <Dialog.Description>Confirm the action.</Dialog.Description>
72
+ </Dialog.Root>
73
+ ```
74
+
75
+ ### Render toast output once
76
+
77
+ ```svelte
78
+ <script lang="ts">
79
+ import { Toast } from 'compote-ui';
80
+
81
+ let { children } = $props();
82
+ </script>
83
+
84
+ <Toast.Toaster />
85
+ {@render children()}
86
+ ```
87
+
88
+ ```ts
89
+ import { toast } from 'compote-ui';
90
+
91
+ toast.success('Saved');
92
+ ```
93
+
94
+ ### Add optional peers only when used
95
+
96
+ ```bash
97
+ bun add @tanstack/svelte-table
98
+ ```
99
+
100
+ ```bash
101
+ bun add @tanstack/svelte-virtual
102
+ ```
103
+
104
+ Use `@tanstack/svelte-table` with `compote-ui/data-table`; add `@tanstack/svelte-virtual` only with `compote-ui/data-table/virtual`.
105
+
106
+ ## Common Mistakes
107
+
108
+ ### CRITICAL Missing theme CSS import
109
+
110
+ Wrong:
111
+
112
+ ```css
113
+ @import 'tailwindcss';
114
+ ```
115
+
116
+ Correct:
117
+
118
+ ```css
119
+ @import 'tailwindcss';
120
+ @import 'compote-ui/theme.css';
121
+ ```
122
+
123
+ Components rely on `theme.css` for Compote CSS variables and Tailwind token definitions.
124
+
125
+ Source: `src/lib/theme.css`, `src/routes/layout.css`
126
+
127
+ ### HIGH Using Svelte legacy props
128
+
129
+ Wrong:
130
+
131
+ ```svelte
132
+ <script lang="ts">
133
+ export let open = false;
134
+ $: visible = open;
135
+ </script>
136
+ ```
137
+
138
+ Correct:
139
+
140
+ ```svelte
141
+ <script lang="ts">
142
+ let { open = $bindable(false) } = $props();
143
+ const visible = $derived(open);
144
+ </script>
145
+ ```
146
+
147
+ The project uses Svelte 5 runes mode globally.
148
+
149
+ Source: `CLAUDE.md`, `svelte.config.js`
150
+
151
+ ### MEDIUM Installing table peers for every app
152
+
153
+ Wrong:
154
+
155
+ ```bash
156
+ bun add compote-ui @tanstack/svelte-table @tanstack/svelte-virtual
157
+ ```
158
+
159
+ Correct:
160
+
161
+ ```bash
162
+ bun add compote-ui
163
+ bun add @tanstack/svelte-table
164
+ bun add @tanstack/svelte-virtual
165
+ ```
166
+
167
+ Install TanStack peers only when the consuming app imports the matching data-table subpath.
168
+
169
+ Source: `package.json`, `src/lib/index.ts`
170
+
171
+ ## References
172
+
173
+ - [Install and peers](references/install-and-peers.md)
@@ -0,0 +1,45 @@
1
+ # Install And Peers
2
+
3
+ Consumer apps may use either npm or bun:
4
+
5
+ ```bash
6
+ npm install compote-ui
7
+ ```
8
+
9
+ ```bash
10
+ bun add compote-ui
11
+ ```
12
+
13
+ Repository development uses bun exclusively:
14
+
15
+ ```bash
16
+ bun install
17
+ bun run check
18
+ bun run lint
19
+ ```
20
+
21
+ Peer dependency guidance:
22
+
23
+ - `svelte`: required, Svelte 5.
24
+ - `@tanstack/svelte-table`: required only for `compote-ui/data-table`.
25
+ - `@tanstack/svelte-virtual`: required only for `compote-ui/data-table/virtual`.
26
+
27
+ Consumer stylesheet setup:
28
+
29
+ ```css
30
+ @import 'tailwindcss';
31
+ @import 'compote-ui/theme.css';
32
+ ```
33
+
34
+ App-level toast setup:
35
+
36
+ ```svelte
37
+ <script lang="ts">
38
+ import { Toast } from 'compote-ui';
39
+
40
+ let { children } = $props();
41
+ </script>
42
+
43
+ <Toast.Toaster />
44
+ {@render children()}
45
+ ```