compote-ui 0.80.0 → 0.81.0
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.
- package/dist/components/data-table-v9/column-filter-kind.d.ts +10 -1
- package/dist/components/data-table-v9/column-filter-kind.js +7 -0
- package/dist/components/data-table-v9/data-table-head.svelte +230 -230
- package/dist/components/data-table-v9/data-table.svelte +154 -154
- package/dist/components/data-table-v9/filter-editors/boolean-filter-editor.svelte +45 -45
- package/dist/components/data-table-v9/filter-editors/column-filter-editor.svelte +39 -33
- package/dist/components/data-table-v9/filter-editors/column-filter-editor.svelte.d.ts +7 -2
- package/dist/components/data-table-v9/filter-editors/date-range-filter-editor.svelte +52 -52
- package/dist/components/data-table-v9/filter-editors/range-filter-editor.svelte +51 -51
- package/dist/components/data-table-v9/filter-editors/select-filter-editor.svelte +59 -59
- package/dist/components/data-table-v9/filter-editors/text-filter-editor.svelte +20 -20
- package/dist/components/data-table-v9/header/column-header-filter.svelte +66 -66
- package/dist/components/data-table-v9/index.d.ts +9 -1
- package/dist/components/data-table-v9/index.js +7 -0
- package/dist/components/data-table-v9/toolbar/data-table-column-filter.svelte +203 -203
- package/dist/components/data-table-v9/types.d.ts +3 -1
- package/dist/components/data-table-v9/virtual/data-table-virtualized.svelte +82 -82
- package/dist/components/data-table-v9/virtual/index.d.ts +2 -2
- package/dist/components/data-table-v9/virtual/index.js +1 -1
- package/dist/components/dialog/alert-dialog.svelte +73 -73
- package/dist/components/dialog/dialog-root.svelte +45 -45
- package/dist/components/drawer/drawer-content.svelte +213 -213
- package/dist/components/popover/popover-content.svelte +97 -97
- package/dist/components/popover/popover-description.svelte +19 -19
- package/dist/components/popover/popover-root.svelte +27 -27
- package/dist/components/popover/popover-title.svelte +19 -19
- package/dist/icons/PhFunnelFill.svelte +19 -19
- package/package.json +1 -1
- package/skills/component-usage/references/overlays-and-floating-ui.md +278 -278
- package/skills/data-table/SKILL.md +362 -314
- package/skills/data-table/references/column-options.md +175 -175
- package/skills/data-table/references/table-state-and-reactivity.md +65 -65
|
@@ -1,203 +1,203 @@
|
|
|
1
|
-
<script lang="ts" generics="T extends RowData">
|
|
2
|
-
import type { Column, RowData } from '@tanstack/svelte-table';
|
|
3
|
-
import * as Popover from '../../popover';
|
|
4
|
-
import * as Tooltip from '../../tooltip';
|
|
5
|
-
import * as ScrollArea from '../../scroll-area';
|
|
6
|
-
import { getColumnLabel, type DataTableInstance } from '../data-table-utils';
|
|
7
|
-
import type { DataTableFeatures } from '../features';
|
|
8
|
-
import Badge from '../../badge/badge.svelte';
|
|
9
|
-
import { button } from '../../button/button.variants';
|
|
10
|
-
import { PhX, PhMagnifyingGlass, PhFunnel } from '../../../icons';
|
|
11
|
-
import { ColumnFilterEditing } from '../column-filter-editing.svelte';
|
|
12
|
-
import ColumnFilterEditor from '../filter-editors/column-filter-editor.svelte';
|
|
13
|
-
|
|
14
|
-
type Props = {
|
|
15
|
-
table: DataTableInstance<T>;
|
|
16
|
-
/** Text label for the trigger. When omitted, a funnel icon is shown instead. */
|
|
17
|
-
triggerLabel?: string;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
let { table, triggerLabel }: Props = $props();
|
|
21
|
-
|
|
22
|
-
// Popover and tooltip share one trigger element, so both must agree on its id.
|
|
23
|
-
const triggerId = $props.id();
|
|
24
|
-
|
|
25
|
-
// Pending edits belong to the table: shared with the other filter hosts, and they still
|
|
26
|
-
// commit after this popover closes.
|
|
27
|
-
const editing = new ColumnFilterEditing({
|
|
28
|
-
table: () => table,
|
|
29
|
-
isActive: (columnId) => activeFilterIds.includes(columnId)
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
const columnFilters = $derived.by(() => table.atoms.columnFilters.get());
|
|
33
|
-
const columnVisibility = $derived.by(() => table.atoms.columnVisibility.get());
|
|
34
|
-
const activeCount = $derived(columnFilters.length);
|
|
35
|
-
|
|
36
|
-
// Filters the user has explicitly opened. Kept separate from the table's
|
|
37
|
-
// columnFilters so a filter card stays visible after its value is cleared
|
|
38
|
-
// (e.g. unchecking the last select option) until removed via the X button.
|
|
39
|
-
let manualFilterIds = $state<string[]>([]);
|
|
40
|
-
const activeFilterIds: string[] = $derived.by(() => {
|
|
41
|
-
const ids = columnFilters.map((f) => f.id);
|
|
42
|
-
for (const id of manualFilterIds) {
|
|
43
|
-
if (!ids.includes(id)) ids.push(id);
|
|
44
|
-
}
|
|
45
|
-
return ids;
|
|
46
|
-
});
|
|
47
|
-
let showColumnPicker = $state(false);
|
|
48
|
-
let columnSearchText = $state('');
|
|
49
|
-
|
|
50
|
-
const activeColumns = $derived.by(() => {
|
|
51
|
-
return activeFilterIds
|
|
52
|
-
.map((id) => table.getColumn(id))
|
|
53
|
-
.filter((col): col is Column<DataTableFeatures, T, unknown> => col != null);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const availableColumns = $derived.by(() => {
|
|
57
|
-
void columnVisibility;
|
|
58
|
-
return table
|
|
59
|
-
.getAllLeafColumns()
|
|
60
|
-
.filter((col) => col.getCanFilter() && !activeFilterIds.includes(col.id))
|
|
61
|
-
.filter(
|
|
62
|
-
(col) =>
|
|
63
|
-
!columnSearchText ||
|
|
64
|
-
getColumnLabel(col).toLowerCase().includes(columnSearchText.toLowerCase())
|
|
65
|
-
);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
function addFilter(column: Column<DataTableFeatures, T, unknown>) {
|
|
69
|
-
manualFilterIds = [...manualFilterIds, column.id];
|
|
70
|
-
showColumnPicker = false;
|
|
71
|
-
columnSearchText = '';
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function removeFilter(column: Column<DataTableFeatures, T, unknown>) {
|
|
75
|
-
manualFilterIds = manualFilterIds.filter((id) => id !== column.id);
|
|
76
|
-
editing.cancel(column.id);
|
|
77
|
-
column.setFilterValue(undefined);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function clearFilters() {
|
|
81
|
-
editing.cancelAll();
|
|
82
|
-
manualFilterIds = [];
|
|
83
|
-
showColumnPicker = false;
|
|
84
|
-
columnSearchText = '';
|
|
85
|
-
table.resetColumnFilters();
|
|
86
|
-
}
|
|
87
|
-
</script>
|
|
88
|
-
|
|
89
|
-
<Popover.Root ids={{ trigger: triggerId }} positioning={{ placement: 'bottom-end' }}>
|
|
90
|
-
<Tooltip.Root ids={{ trigger: triggerId }} disabled={!!triggerLabel}>
|
|
91
|
-
<Tooltip.Trigger>
|
|
92
|
-
{#snippet asChild(tooltipProps)}
|
|
93
|
-
<Popover.Trigger
|
|
94
|
-
{...tooltipProps()}
|
|
95
|
-
aria-label={triggerLabel ? undefined : 'Filters'}
|
|
96
|
-
class={button({
|
|
97
|
-
variant: 'outline',
|
|
98
|
-
size: triggerLabel ? 'default' : 'icon',
|
|
99
|
-
class: 'relative'
|
|
100
|
-
})}
|
|
101
|
-
>
|
|
102
|
-
{#if triggerLabel}
|
|
103
|
-
{triggerLabel}
|
|
104
|
-
{:else}
|
|
105
|
-
<PhFunnel />
|
|
106
|
-
{/if}
|
|
107
|
-
{#if activeCount > 0}
|
|
108
|
-
<Badge
|
|
109
|
-
variant="solid"
|
|
110
|
-
color="primary"
|
|
111
|
-
class="absolute top-0 right-0 size-4 translate-x-1/3 -translate-y-1/3 justify-center px-1 text-xs"
|
|
112
|
-
>
|
|
113
|
-
{activeCount > 99 ? '99+' : activeCount}
|
|
114
|
-
</Badge>
|
|
115
|
-
{/if}
|
|
116
|
-
</Popover.Trigger>
|
|
117
|
-
{/snippet}
|
|
118
|
-
</Tooltip.Trigger>
|
|
119
|
-
<Tooltip.Content>Filters</Tooltip.Content>
|
|
120
|
-
</Tooltip.Root>
|
|
121
|
-
|
|
122
|
-
<Popover.Content class="flex w-70 flex-col gap-3 p-3" showArrow={false}>
|
|
123
|
-
<div class="mr-1 flex items-center justify-between py-2.5">
|
|
124
|
-
<Popover.Title class="text-sm font-medium text-ink">Filters</Popover.Title>
|
|
125
|
-
{#if activeCount > 0}
|
|
126
|
-
<button type="button" onclick={clearFilters} class="text-xs text-primary hover:underline">
|
|
127
|
-
Clear all
|
|
128
|
-
</button>
|
|
129
|
-
{/if}
|
|
130
|
-
</div>
|
|
131
|
-
|
|
132
|
-
{#if activeColumns.length > 0}
|
|
133
|
-
<div class="overflow-hidden border-t border-surface-2">
|
|
134
|
-
<ScrollArea.Root>
|
|
135
|
-
<ScrollArea.Viewport class="max-h-96">
|
|
136
|
-
<ScrollArea.Content class="flex flex-col gap-3">
|
|
137
|
-
{#each activeColumns as column (column.id)}
|
|
138
|
-
<div class="border border-surface-3 p-3">
|
|
139
|
-
<div class="mb-2 flex items-center justify-between">
|
|
140
|
-
<span class="text-sm font-medium text-ink">{getColumnLabel(column)}</span>
|
|
141
|
-
<button
|
|
142
|
-
type="button"
|
|
143
|
-
onclick={() => removeFilter(column)}
|
|
144
|
-
class="text-ink-dim transition-colors hover:text-ink"
|
|
145
|
-
>
|
|
146
|
-
<PhX class="size-3.5" />
|
|
147
|
-
</button>
|
|
148
|
-
</div>
|
|
149
|
-
|
|
150
|
-
<ColumnFilterEditor {column} {editing} />
|
|
151
|
-
</div>
|
|
152
|
-
{/each}
|
|
153
|
-
</ScrollArea.Content>
|
|
154
|
-
</ScrollArea.Viewport>
|
|
155
|
-
<ScrollArea.Scrollbar orientation="vertical">
|
|
156
|
-
<ScrollArea.Thumb />
|
|
157
|
-
</ScrollArea.Scrollbar>
|
|
158
|
-
<ScrollArea.Corner />
|
|
159
|
-
</ScrollArea.Root>
|
|
160
|
-
</div>
|
|
161
|
-
{/if}
|
|
162
|
-
|
|
163
|
-
{#if showColumnPicker}
|
|
164
|
-
<div class="border-t border-surface-3 p-3">
|
|
165
|
-
<div class="relative mb-1">
|
|
166
|
-
<PhMagnifyingGlass
|
|
167
|
-
class="pointer-events-none absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2 text-ink-dim"
|
|
168
|
-
/>
|
|
169
|
-
<input
|
|
170
|
-
type="text"
|
|
171
|
-
placeholder="Search columns..."
|
|
172
|
-
bind:value={columnSearchText}
|
|
173
|
-
class="h-8 w-full rounded border border-border bg-surface-1 pr-3 pl-7 text-sm text-ink outline-none placeholder:text-ink-dim focus:ring-1 focus:ring-ring"
|
|
174
|
-
/>
|
|
175
|
-
</div>
|
|
176
|
-
<div class="max-h-48 overflow-y-auto">
|
|
177
|
-
{#each availableColumns as column (column.id)}
|
|
178
|
-
<button
|
|
179
|
-
type="button"
|
|
180
|
-
onclick={() => addFilter(column)}
|
|
181
|
-
class="w-full rounded px-2 py-1.5 text-left text-sm text-ink hover:bg-surface-2"
|
|
182
|
-
>
|
|
183
|
-
{getColumnLabel(column)}
|
|
184
|
-
</button>
|
|
185
|
-
{:else}
|
|
186
|
-
<p class="px-2 py-3 text-center text-sm text-ink-dim">No more columns</p>
|
|
187
|
-
{/each}
|
|
188
|
-
</div>
|
|
189
|
-
</div>
|
|
190
|
-
{:else}
|
|
191
|
-
<div class="border-t border-surface-3">
|
|
192
|
-
<button
|
|
193
|
-
type="button"
|
|
194
|
-
onclick={() => (showColumnPicker = true)}
|
|
195
|
-
class="flex w-full items-center gap-2 px-3 py-2.5 text-sm text-ink-dim hover:bg-surface-2 hover:text-ink"
|
|
196
|
-
>
|
|
197
|
-
<span class="text-base leading-none">+</span>
|
|
198
|
-
Add Filter
|
|
199
|
-
</button>
|
|
200
|
-
</div>
|
|
201
|
-
{/if}
|
|
202
|
-
</Popover.Content>
|
|
203
|
-
</Popover.Root>
|
|
1
|
+
<script lang="ts" generics="T extends RowData">
|
|
2
|
+
import type { Column, RowData } from '@tanstack/svelte-table';
|
|
3
|
+
import * as Popover from '../../popover';
|
|
4
|
+
import * as Tooltip from '../../tooltip';
|
|
5
|
+
import * as ScrollArea from '../../scroll-area';
|
|
6
|
+
import { getColumnLabel, type DataTableInstance } from '../data-table-utils';
|
|
7
|
+
import type { DataTableFeatures } from '../features';
|
|
8
|
+
import Badge from '../../badge/badge.svelte';
|
|
9
|
+
import { button } from '../../button/button.variants';
|
|
10
|
+
import { PhX, PhMagnifyingGlass, PhFunnel } from '../../../icons';
|
|
11
|
+
import { ColumnFilterEditing } from '../column-filter-editing.svelte';
|
|
12
|
+
import ColumnFilterEditor from '../filter-editors/column-filter-editor.svelte';
|
|
13
|
+
|
|
14
|
+
type Props = {
|
|
15
|
+
table: DataTableInstance<T>;
|
|
16
|
+
/** Text label for the trigger. When omitted, a funnel icon is shown instead. */
|
|
17
|
+
triggerLabel?: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
let { table, triggerLabel }: Props = $props();
|
|
21
|
+
|
|
22
|
+
// Popover and tooltip share one trigger element, so both must agree on its id.
|
|
23
|
+
const triggerId = $props.id();
|
|
24
|
+
|
|
25
|
+
// Pending edits belong to the table: shared with the other filter hosts, and they still
|
|
26
|
+
// commit after this popover closes.
|
|
27
|
+
const editing = new ColumnFilterEditing({
|
|
28
|
+
table: () => table,
|
|
29
|
+
isActive: (columnId) => activeFilterIds.includes(columnId)
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const columnFilters = $derived.by(() => table.atoms.columnFilters.get());
|
|
33
|
+
const columnVisibility = $derived.by(() => table.atoms.columnVisibility.get());
|
|
34
|
+
const activeCount = $derived(columnFilters.length);
|
|
35
|
+
|
|
36
|
+
// Filters the user has explicitly opened. Kept separate from the table's
|
|
37
|
+
// columnFilters so a filter card stays visible after its value is cleared
|
|
38
|
+
// (e.g. unchecking the last select option) until removed via the X button.
|
|
39
|
+
let manualFilterIds = $state<string[]>([]);
|
|
40
|
+
const activeFilterIds: string[] = $derived.by(() => {
|
|
41
|
+
const ids = columnFilters.map((f) => f.id);
|
|
42
|
+
for (const id of manualFilterIds) {
|
|
43
|
+
if (!ids.includes(id)) ids.push(id);
|
|
44
|
+
}
|
|
45
|
+
return ids;
|
|
46
|
+
});
|
|
47
|
+
let showColumnPicker = $state(false);
|
|
48
|
+
let columnSearchText = $state('');
|
|
49
|
+
|
|
50
|
+
const activeColumns = $derived.by(() => {
|
|
51
|
+
return activeFilterIds
|
|
52
|
+
.map((id) => table.getColumn(id))
|
|
53
|
+
.filter((col): col is Column<DataTableFeatures, T, unknown> => col != null);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const availableColumns = $derived.by(() => {
|
|
57
|
+
void columnVisibility;
|
|
58
|
+
return table
|
|
59
|
+
.getAllLeafColumns()
|
|
60
|
+
.filter((col) => col.getCanFilter() && !activeFilterIds.includes(col.id))
|
|
61
|
+
.filter(
|
|
62
|
+
(col) =>
|
|
63
|
+
!columnSearchText ||
|
|
64
|
+
getColumnLabel(col).toLowerCase().includes(columnSearchText.toLowerCase())
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
function addFilter(column: Column<DataTableFeatures, T, unknown>) {
|
|
69
|
+
manualFilterIds = [...manualFilterIds, column.id];
|
|
70
|
+
showColumnPicker = false;
|
|
71
|
+
columnSearchText = '';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function removeFilter(column: Column<DataTableFeatures, T, unknown>) {
|
|
75
|
+
manualFilterIds = manualFilterIds.filter((id) => id !== column.id);
|
|
76
|
+
editing.cancel(column.id);
|
|
77
|
+
column.setFilterValue(undefined);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function clearFilters() {
|
|
81
|
+
editing.cancelAll();
|
|
82
|
+
manualFilterIds = [];
|
|
83
|
+
showColumnPicker = false;
|
|
84
|
+
columnSearchText = '';
|
|
85
|
+
table.resetColumnFilters();
|
|
86
|
+
}
|
|
87
|
+
</script>
|
|
88
|
+
|
|
89
|
+
<Popover.Root ids={{ trigger: triggerId }} positioning={{ placement: 'bottom-end' }}>
|
|
90
|
+
<Tooltip.Root ids={{ trigger: triggerId }} disabled={!!triggerLabel}>
|
|
91
|
+
<Tooltip.Trigger>
|
|
92
|
+
{#snippet asChild(tooltipProps)}
|
|
93
|
+
<Popover.Trigger
|
|
94
|
+
{...tooltipProps()}
|
|
95
|
+
aria-label={triggerLabel ? undefined : 'Filters'}
|
|
96
|
+
class={button({
|
|
97
|
+
variant: 'outline',
|
|
98
|
+
size: triggerLabel ? 'default' : 'icon',
|
|
99
|
+
class: 'relative'
|
|
100
|
+
})}
|
|
101
|
+
>
|
|
102
|
+
{#if triggerLabel}
|
|
103
|
+
{triggerLabel}
|
|
104
|
+
{:else}
|
|
105
|
+
<PhFunnel />
|
|
106
|
+
{/if}
|
|
107
|
+
{#if activeCount > 0}
|
|
108
|
+
<Badge
|
|
109
|
+
variant="solid"
|
|
110
|
+
color="primary"
|
|
111
|
+
class="absolute top-0 right-0 size-4 translate-x-1/3 -translate-y-1/3 justify-center px-1 text-xs"
|
|
112
|
+
>
|
|
113
|
+
{activeCount > 99 ? '99+' : activeCount}
|
|
114
|
+
</Badge>
|
|
115
|
+
{/if}
|
|
116
|
+
</Popover.Trigger>
|
|
117
|
+
{/snippet}
|
|
118
|
+
</Tooltip.Trigger>
|
|
119
|
+
<Tooltip.Content>Filters</Tooltip.Content>
|
|
120
|
+
</Tooltip.Root>
|
|
121
|
+
|
|
122
|
+
<Popover.Content class="flex w-70 flex-col gap-3 p-3" showArrow={false}>
|
|
123
|
+
<div class="mr-1 flex items-center justify-between py-2.5">
|
|
124
|
+
<Popover.Title class="text-sm font-medium text-ink">Filters</Popover.Title>
|
|
125
|
+
{#if activeCount > 0}
|
|
126
|
+
<button type="button" onclick={clearFilters} class="text-xs text-primary hover:underline">
|
|
127
|
+
Clear all
|
|
128
|
+
</button>
|
|
129
|
+
{/if}
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
{#if activeColumns.length > 0}
|
|
133
|
+
<div class="overflow-hidden border-t border-surface-2">
|
|
134
|
+
<ScrollArea.Root>
|
|
135
|
+
<ScrollArea.Viewport class="max-h-96">
|
|
136
|
+
<ScrollArea.Content class="flex flex-col gap-3">
|
|
137
|
+
{#each activeColumns as column (column.id)}
|
|
138
|
+
<div class="border border-surface-3 p-3">
|
|
139
|
+
<div class="mb-2 flex items-center justify-between">
|
|
140
|
+
<span class="text-sm font-medium text-ink">{getColumnLabel(column)}</span>
|
|
141
|
+
<button
|
|
142
|
+
type="button"
|
|
143
|
+
onclick={() => removeFilter(column)}
|
|
144
|
+
class="text-ink-dim transition-colors hover:text-ink"
|
|
145
|
+
>
|
|
146
|
+
<PhX class="size-3.5" />
|
|
147
|
+
</button>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
<ColumnFilterEditor {column} {editing} />
|
|
151
|
+
</div>
|
|
152
|
+
{/each}
|
|
153
|
+
</ScrollArea.Content>
|
|
154
|
+
</ScrollArea.Viewport>
|
|
155
|
+
<ScrollArea.Scrollbar orientation="vertical">
|
|
156
|
+
<ScrollArea.Thumb />
|
|
157
|
+
</ScrollArea.Scrollbar>
|
|
158
|
+
<ScrollArea.Corner />
|
|
159
|
+
</ScrollArea.Root>
|
|
160
|
+
</div>
|
|
161
|
+
{/if}
|
|
162
|
+
|
|
163
|
+
{#if showColumnPicker}
|
|
164
|
+
<div class="border-t border-surface-3 p-3">
|
|
165
|
+
<div class="relative mb-1">
|
|
166
|
+
<PhMagnifyingGlass
|
|
167
|
+
class="pointer-events-none absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2 text-ink-dim"
|
|
168
|
+
/>
|
|
169
|
+
<input
|
|
170
|
+
type="text"
|
|
171
|
+
placeholder="Search columns..."
|
|
172
|
+
bind:value={columnSearchText}
|
|
173
|
+
class="h-8 w-full rounded border border-border bg-surface-1 pr-3 pl-7 text-sm text-ink outline-none placeholder:text-ink-dim focus:ring-1 focus:ring-ring"
|
|
174
|
+
/>
|
|
175
|
+
</div>
|
|
176
|
+
<div class="max-h-48 overflow-y-auto">
|
|
177
|
+
{#each availableColumns as column (column.id)}
|
|
178
|
+
<button
|
|
179
|
+
type="button"
|
|
180
|
+
onclick={() => addFilter(column)}
|
|
181
|
+
class="w-full rounded px-2 py-1.5 text-left text-sm text-ink hover:bg-surface-2"
|
|
182
|
+
>
|
|
183
|
+
{getColumnLabel(column)}
|
|
184
|
+
</button>
|
|
185
|
+
{:else}
|
|
186
|
+
<p class="px-2 py-3 text-center text-sm text-ink-dim">No more columns</p>
|
|
187
|
+
{/each}
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
{:else}
|
|
191
|
+
<div class="border-t border-surface-3">
|
|
192
|
+
<button
|
|
193
|
+
type="button"
|
|
194
|
+
onclick={() => (showColumnPicker = true)}
|
|
195
|
+
class="flex w-full items-center gap-2 px-3 py-2.5 text-sm text-ink-dim hover:bg-surface-2 hover:text-ink"
|
|
196
|
+
>
|
|
197
|
+
<span class="text-base leading-none">+</span>
|
|
198
|
+
Add Filter
|
|
199
|
+
</button>
|
|
200
|
+
</div>
|
|
201
|
+
{/if}
|
|
202
|
+
</Popover.Content>
|
|
203
|
+
</Popover.Root>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FilterFnOption, RowData } from '@tanstack/svelte-table';
|
|
1
|
+
import type { Column, FilterFnOption, RowData } from '@tanstack/svelte-table';
|
|
2
2
|
import type { Component, Snippet } from 'svelte';
|
|
3
3
|
import type { DataTableFeatures } from './features';
|
|
4
4
|
export type DataTableAlign = 'left' | 'center' | 'right';
|
|
@@ -63,6 +63,8 @@ export type DataTableGroupColumn<T extends RowData> = DataTableColumnBase & {
|
|
|
63
63
|
formatLocale?: never;
|
|
64
64
|
};
|
|
65
65
|
export type DataTableColumn<T extends RowData> = DataTableLeafColumn<T> | DataTableGroupColumn<T>;
|
|
66
|
+
/** A column of a compote data table, as `table.getColumn()` and `getAllLeafColumns()` return it. */
|
|
67
|
+
export type DataTableColumnInstance<T extends RowData> = Column<DataTableFeatures, T, unknown>;
|
|
66
68
|
export type DataTableColumnMeta = {
|
|
67
69
|
align?: DataTableAlign;
|
|
68
70
|
type?: DataTableColumnType;
|
|
@@ -1,82 +1,82 @@
|
|
|
1
|
-
<script lang="ts" generics="T extends RowData">
|
|
2
|
-
import type { RowData } from '@tanstack/svelte-table';
|
|
3
|
-
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
-
import { cn, type ClassValue } from 'tailwind-variants';
|
|
5
|
-
import type { DataTableInstance } from '../data-table-utils';
|
|
6
|
-
import { createTableViewState } from '../table-view-state.svelte';
|
|
7
|
-
import DataTableHead from '../data-table-head.svelte';
|
|
8
|
-
import DataTableFoot from '../data-table-foot.svelte';
|
|
9
|
-
import DataTableVirtualRows from './data-table-virtual-rows.svelte';
|
|
10
|
-
import { tableSizeStyle } from '../data-table-utils';
|
|
11
|
-
|
|
12
|
-
type Props = Omit<HTMLAttributes<HTMLDivElement>, 'class'> & {
|
|
13
|
-
table: DataTableInstance<T>;
|
|
14
|
-
caption?: string;
|
|
15
|
-
emptyMessage?: string;
|
|
16
|
-
class?: ClassValue;
|
|
17
|
-
/** Show a filter funnel in the header of every filterable column. */
|
|
18
|
-
headerFilters?: boolean;
|
|
19
|
-
onRowClick?: (details: { row: T; event: MouseEvent }) => void;
|
|
20
|
-
onRowDoubleClick?: (details: { row: T; event: MouseEvent }) => void;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
let {
|
|
24
|
-
table,
|
|
25
|
-
caption,
|
|
26
|
-
emptyMessage = 'No rows found',
|
|
27
|
-
class: className,
|
|
28
|
-
headerFilters = false,
|
|
29
|
-
onRowClick,
|
|
30
|
-
onRowDoubleClick,
|
|
31
|
-
...rest
|
|
32
|
-
}: Props = $props();
|
|
33
|
-
|
|
34
|
-
let scrollContainerRef = $state<HTMLDivElement | undefined>(undefined);
|
|
35
|
-
|
|
36
|
-
const view = createTableViewState(() => table);
|
|
37
|
-
</script>
|
|
38
|
-
|
|
39
|
-
<div
|
|
40
|
-
class={cn(
|
|
41
|
-
'flex max-h-full min-h-0 flex-col overflow-hidden rounded-lg border border-surface-3 bg-surface-1',
|
|
42
|
-
className
|
|
43
|
-
)}
|
|
44
|
-
{...rest}
|
|
45
|
-
>
|
|
46
|
-
{#if view.isColumnResizing}
|
|
47
|
-
<div aria-hidden="true" class="fixed inset-0 z-50 cursor-col-resize select-none"></div>
|
|
48
|
-
{/if}
|
|
49
|
-
|
|
50
|
-
<div class="min-h-0 flex-1 overflow-auto" bind:this={scrollContainerRef}>
|
|
51
|
-
<table
|
|
52
|
-
class="table-fixed border-separate border-spacing-0 text-sm"
|
|
53
|
-
style="display: grid; {tableSizeStyle(table, view.isRowSelectionEnabled, view)}"
|
|
54
|
-
>
|
|
55
|
-
{#if caption}
|
|
56
|
-
<caption class="sr-only">{caption}</caption>
|
|
57
|
-
{/if}
|
|
58
|
-
<DataTableHead {table} {view} isVirtual {headerFilters} />
|
|
59
|
-
{#if scrollContainerRef}
|
|
60
|
-
<DataTableVirtualRows
|
|
61
|
-
{table}
|
|
62
|
-
{view}
|
|
63
|
-
scrollContainer={scrollContainerRef}
|
|
64
|
-
{emptyMessage}
|
|
65
|
-
{onRowClick}
|
|
66
|
-
{onRowDoubleClick}
|
|
67
|
-
/>
|
|
68
|
-
{/if}
|
|
69
|
-
{#if view.hasFooter}
|
|
70
|
-
<DataTableFoot {table} {view} hasGrowColumn={false} isVirtual />
|
|
71
|
-
{/if}
|
|
72
|
-
</table>
|
|
73
|
-
</div>
|
|
74
|
-
|
|
75
|
-
<div class="shrink-0 border-t border-surface-3 bg-surface-2 px-3 py-2 text-sm text-ink-dim">
|
|
76
|
-
{#if view.isRowSelectionEnabled}
|
|
77
|
-
{view.selectedRowCount} of {view.rowModel.rows.length} rows selected
|
|
78
|
-
{:else}
|
|
79
|
-
{view.rowModel.rows.length} rows
|
|
80
|
-
{/if}
|
|
81
|
-
</div>
|
|
82
|
-
</div>
|
|
1
|
+
<script lang="ts" generics="T extends RowData">
|
|
2
|
+
import type { RowData } from '@tanstack/svelte-table';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
import { cn, type ClassValue } from 'tailwind-variants';
|
|
5
|
+
import type { DataTableInstance } from '../data-table-utils';
|
|
6
|
+
import { createTableViewState } from '../table-view-state.svelte';
|
|
7
|
+
import DataTableHead from '../data-table-head.svelte';
|
|
8
|
+
import DataTableFoot from '../data-table-foot.svelte';
|
|
9
|
+
import DataTableVirtualRows from './data-table-virtual-rows.svelte';
|
|
10
|
+
import { tableSizeStyle } from '../data-table-utils';
|
|
11
|
+
|
|
12
|
+
type Props = Omit<HTMLAttributes<HTMLDivElement>, 'class'> & {
|
|
13
|
+
table: DataTableInstance<T>;
|
|
14
|
+
caption?: string;
|
|
15
|
+
emptyMessage?: string;
|
|
16
|
+
class?: ClassValue;
|
|
17
|
+
/** Show a filter funnel in the header of every filterable column. */
|
|
18
|
+
headerFilters?: boolean;
|
|
19
|
+
onRowClick?: (details: { row: T; event: MouseEvent }) => void;
|
|
20
|
+
onRowDoubleClick?: (details: { row: T; event: MouseEvent }) => void;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
let {
|
|
24
|
+
table,
|
|
25
|
+
caption,
|
|
26
|
+
emptyMessage = 'No rows found',
|
|
27
|
+
class: className,
|
|
28
|
+
headerFilters = false,
|
|
29
|
+
onRowClick,
|
|
30
|
+
onRowDoubleClick,
|
|
31
|
+
...rest
|
|
32
|
+
}: Props = $props();
|
|
33
|
+
|
|
34
|
+
let scrollContainerRef = $state<HTMLDivElement | undefined>(undefined);
|
|
35
|
+
|
|
36
|
+
const view = createTableViewState(() => table);
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<div
|
|
40
|
+
class={cn(
|
|
41
|
+
'flex max-h-full min-h-0 flex-col overflow-hidden rounded-lg border border-surface-3 bg-surface-1',
|
|
42
|
+
className
|
|
43
|
+
)}
|
|
44
|
+
{...rest}
|
|
45
|
+
>
|
|
46
|
+
{#if view.isColumnResizing}
|
|
47
|
+
<div aria-hidden="true" class="fixed inset-0 z-50 cursor-col-resize select-none"></div>
|
|
48
|
+
{/if}
|
|
49
|
+
|
|
50
|
+
<div class="min-h-0 flex-1 overflow-auto" bind:this={scrollContainerRef}>
|
|
51
|
+
<table
|
|
52
|
+
class="table-fixed border-separate border-spacing-0 text-sm"
|
|
53
|
+
style="display: grid; {tableSizeStyle(table, view.isRowSelectionEnabled, view)}"
|
|
54
|
+
>
|
|
55
|
+
{#if caption}
|
|
56
|
+
<caption class="sr-only">{caption}</caption>
|
|
57
|
+
{/if}
|
|
58
|
+
<DataTableHead {table} {view} isVirtual {headerFilters} />
|
|
59
|
+
{#if scrollContainerRef}
|
|
60
|
+
<DataTableVirtualRows
|
|
61
|
+
{table}
|
|
62
|
+
{view}
|
|
63
|
+
scrollContainer={scrollContainerRef}
|
|
64
|
+
{emptyMessage}
|
|
65
|
+
{onRowClick}
|
|
66
|
+
{onRowDoubleClick}
|
|
67
|
+
/>
|
|
68
|
+
{/if}
|
|
69
|
+
{#if view.hasFooter}
|
|
70
|
+
<DataTableFoot {table} {view} hasGrowColumn={false} isVirtual />
|
|
71
|
+
{/if}
|
|
72
|
+
</table>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<div class="shrink-0 border-t border-surface-3 bg-surface-2 px-3 py-2 text-sm text-ink-dim">
|
|
76
|
+
{#if view.isRowSelectionEnabled}
|
|
77
|
+
{view.selectedRowCount} of {view.rowModel.rows.length} rows selected
|
|
78
|
+
{:else}
|
|
79
|
+
{view.rowModel.rows.length} rows
|
|
80
|
+
{/if}
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { default as Root } from './data-table-virtualized.svelte';
|
|
2
|
-
export { ColumnFilter, ColumnVisibility, FlexRender, Refresh, Search, Title, Toolbar, createDataTableColumnHelper, createTable, renderComponent, renderSnippet } from '../index';
|
|
3
|
-
export type { CreateDataTableOptions, DataTableAccessorFnColumn, DataTableAccessorKeyColumn, DataTableAlign, DataTableCellPropsResolver, DataTableCellRenderProps, DataTableColumn, DataTableColumnBase, DataTableColumnOptions, DataTableColumnType, DataTableGroupColumn, DataTableInstance, DataTableLeafColumn, DataTableLeafColumnBase } from '../index';
|
|
2
|
+
export { ColumnFilter, ColumnFilterEditing, ColumnFilterEditor, ColumnVisibility, EMPTY_SELECT_VALUE, FlexRender, Refresh, Search, Title, Toolbar, createDataTableColumnHelper, createTable, filterKindFor, getColumnFilterKind, getColumnLabel, renderComponent, renderSnippet, selectFilterOptions } from '../index';
|
|
3
|
+
export type { ColumnFilterEditingOptions, ColumnFilterKind, CreateDataTableOptions, DataTableAccessorFnColumn, DataTableAccessorKeyColumn, DataTableAlign, DataTableCellPropsResolver, DataTableCellRenderProps, DataTableColumn, DataTableColumnBase, DataTableColumnInstance, DataTableColumnOptions, DataTableColumnType, DataTableGroupColumn, DataTableInstance, DataTableLeafColumn, DataTableLeafColumnBase, FilterableColumn, SelectFilterOption, SelectFilterValue } from '../index';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { default as Root } from './data-table-virtualized.svelte';
|
|
2
|
-
export { ColumnFilter, ColumnVisibility, FlexRender, Refresh, Search, Title, Toolbar, createDataTableColumnHelper, createTable, renderComponent, renderSnippet } from '../index';
|
|
2
|
+
export { ColumnFilter, ColumnFilterEditing, ColumnFilterEditor, ColumnVisibility, EMPTY_SELECT_VALUE, FlexRender, Refresh, Search, Title, Toolbar, createDataTableColumnHelper, createTable, filterKindFor, getColumnFilterKind, getColumnLabel, renderComponent, renderSnippet, selectFilterOptions } from '../index';
|