compote-ui 0.32.0 → 0.33.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.
Files changed (43) hide show
  1. package/dist/components/checkbox/checkbox.svelte +34 -9
  2. package/dist/components/checkbox/checkbox.types.d.ts +5 -2
  3. package/dist/components/data-table/core/cells.d.ts +78 -0
  4. package/dist/components/data-table/core/cells.js +66 -0
  5. package/dist/components/data-table/core/create-table.svelte.d.ts +24 -0
  6. package/dist/components/data-table/core/create-table.svelte.js +74 -0
  7. package/dist/components/data-table/core/index.d.ts +3 -0
  8. package/dist/components/data-table/core/index.js +2 -0
  9. package/dist/components/data-table/data-table-column-visibility.svelte +79 -0
  10. package/dist/components/data-table/data-table-column-visibility.svelte.d.ts +29 -0
  11. package/dist/components/data-table/data-table-filters.svelte +285 -0
  12. package/dist/components/data-table/data-table-filters.svelte.d.ts +29 -0
  13. package/dist/components/data-table/data-table-title.svelte +16 -0
  14. package/dist/components/data-table/data-table-title.svelte.d.ts +10 -0
  15. package/dist/components/data-table/data-table-toolbar.svelte +16 -0
  16. package/dist/components/data-table/data-table-toolbar.svelte.d.ts +10 -0
  17. package/dist/components/data-table/data-table.svelte +342 -0
  18. package/dist/components/data-table/data-table.svelte.d.ts +32 -0
  19. package/dist/components/data-table/index.d.ts +7 -0
  20. package/dist/components/data-table/index.js +7 -0
  21. package/dist/components/number-input/number-input.svelte +3 -0
  22. package/dist/components/popover/index.d.ts +6 -0
  23. package/dist/components/popover/index.js +6 -0
  24. package/dist/components/popover/popover-close-trigger.svelte +25 -0
  25. package/dist/components/popover/popover-close-trigger.svelte.d.ts +8 -0
  26. package/dist/components/popover/popover-content.svelte +71 -0
  27. package/dist/components/popover/popover-content.svelte.d.ts +10 -0
  28. package/dist/components/popover/popover-description.svelte +15 -0
  29. package/dist/components/popover/popover-description.svelte.d.ts +8 -0
  30. package/dist/components/popover/popover-root.svelte +24 -0
  31. package/dist/components/popover/popover-root.svelte.d.ts +11 -0
  32. package/dist/components/popover/popover-title.svelte +15 -0
  33. package/dist/components/popover/popover-title.svelte.d.ts +8 -0
  34. package/dist/components/popover/popover-trigger.svelte +17 -0
  35. package/dist/components/popover/popover-trigger.svelte.d.ts +10 -0
  36. package/dist/components/scroll-area/scroll-area-content.svelte +1 -1
  37. package/dist/icons/PhArrowSquareOut.svelte +18 -0
  38. package/dist/icons/PhArrowSquareOut.svelte.d.ts +5 -0
  39. package/dist/icons/index.d.ts +1 -0
  40. package/dist/icons/index.js +1 -0
  41. package/dist/index.d.ts +1 -0
  42. package/dist/index.js +1 -0
  43. package/package.json +12 -1
@@ -0,0 +1,285 @@
1
+ <script lang="ts" generics="TData extends RowData, TSelected = object">
2
+ import * as Field from '../field';
3
+ import * as Popover from '../popover';
4
+ import * as ScrollArea from '../scroll-area';
5
+ import * as Fieldset from '../fieldset';
6
+ import Checkbox from '../checkbox/checkbox.svelte';
7
+ import Button from '../button/button.svelte';
8
+ import NumberInput from '../number-input/number-input.svelte';
9
+ import Select from '../select/select.svelte';
10
+ import type { Column, RowData } from '@tanstack/svelte-table';
11
+ import {
12
+ getDataTableFilterConfig,
13
+ type DataTable,
14
+ type DataTableFeatures,
15
+ type DataTableFilterConfig,
16
+ type DataTableFilterOption,
17
+ type DataTableSelectedState
18
+ } from './core';
19
+
20
+ type Props = {
21
+ table: DataTable<TData, TSelected>;
22
+ triggerLabel?: string;
23
+ };
24
+
25
+ type FilterableColumn = Column<DataTableFeatures, TData, unknown>;
26
+
27
+ let { table, triggerLabel = 'Filters' }: Props = $props();
28
+
29
+ const columnFilters = $derived((table.state as unknown as DataTableSelectedState).columnFilters);
30
+ const activeFilterCount = $derived(columnFilters.length);
31
+ const filterableColumns = $derived(
32
+ table
33
+ .getAllLeafColumns()
34
+ .filter((column) => column.getCanFilter() && getDataTableFilterConfig(column.columnDef))
35
+ );
36
+
37
+ function getColumnLabel(column: FilterableColumn) {
38
+ return typeof column.columnDef.header === 'string' ? column.columnDef.header : column.id;
39
+ }
40
+
41
+ function getFilterConfig(column: FilterableColumn, filtersState: unknown) {
42
+ void filtersState;
43
+ return getDataTableFilterConfig(column.columnDef);
44
+ }
45
+
46
+ function getFilterValue(column: FilterableColumn, filtersState: unknown) {
47
+ void filtersState;
48
+ return column.getFilterValue();
49
+ }
50
+
51
+ function setTextFilter(column: FilterableColumn, value: string) {
52
+ column.setFilterValue(value || undefined);
53
+ }
54
+
55
+ function setBooleanFilter(column: FilterableColumn, value: string) {
56
+ if (value === 'true') {
57
+ column.setFilterValue(true);
58
+ return;
59
+ }
60
+
61
+ if (value === 'false') {
62
+ column.setFilterValue(false);
63
+ return;
64
+ }
65
+
66
+ column.setFilterValue(undefined);
67
+ }
68
+
69
+ function setNumberRangeFilter(column: FilterableColumn, index: 0 | 1, value: number | null) {
70
+ const currentValue = column.getFilterValue();
71
+ const current = Array.isArray(currentValue) ? currentValue : [];
72
+ const next = [current[0], current[1]] as [number | undefined, number | undefined];
73
+ next[index] = value ?? undefined;
74
+
75
+ column.setFilterValue(next[0] === undefined && next[1] === undefined ? undefined : next);
76
+ }
77
+
78
+ function getNumberRangeValue(
79
+ column: FilterableColumn,
80
+ filtersState: unknown
81
+ ): [number | undefined, number | undefined] {
82
+ const value = getFilterValue(column, filtersState);
83
+ return Array.isArray(value) ? [value[0], value[1]] : [undefined, undefined];
84
+ }
85
+
86
+ function getFacetOptions(
87
+ column: FilterableColumn,
88
+ config: DataTableFilterConfig,
89
+ filtersState: unknown
90
+ ): DataTableFilterOption[] {
91
+ void filtersState;
92
+
93
+ if (config.type !== 'facet') return [];
94
+ if (config.options) return config.options;
95
+
96
+ return Array.from(column.getFacetedUniqueValues().entries())
97
+ .sort(([a], [b]) => String(a).localeCompare(String(b)))
98
+ .slice(0, config.maxOptions ?? 20)
99
+ .map(([value, count]) => ({
100
+ value,
101
+ label: `${String(value)} (${count})`
102
+ }));
103
+ }
104
+
105
+ function getFacetFilterValue(column: FilterableColumn, filtersState: unknown) {
106
+ const value = getFilterValue(column, filtersState);
107
+ return Array.isArray(value) ? value : [];
108
+ }
109
+
110
+ function getFacetOptionChecked(
111
+ column: FilterableColumn,
112
+ option: DataTableFilterOption,
113
+ filtersState: unknown
114
+ ) {
115
+ return getFacetFilterValue(column, filtersState).some((value) =>
116
+ Object.is(value, option.value)
117
+ );
118
+ }
119
+
120
+ function toggleFacetFilterValue(
121
+ column: FilterableColumn,
122
+ option: DataTableFilterOption,
123
+ checked: boolean
124
+ ) {
125
+ const currentValue = column.getFilterValue();
126
+ const current = Array.isArray(currentValue) ? currentValue : [];
127
+ const next = checked
128
+ ? [...current, option.value]
129
+ : current.filter((value) => !Object.is(value, option.value));
130
+
131
+ column.setFilterValue(next.length > 0 ? next : undefined);
132
+ }
133
+
134
+ function getBooleanFilterValue(column: FilterableColumn, filtersState: unknown) {
135
+ const value = getFilterValue(column, filtersState);
136
+ if (value === true) return 'true';
137
+ if (value === false) return 'false';
138
+ return null;
139
+ }
140
+
141
+ function getBooleanLabel(config: DataTableFilterConfig, value: boolean) {
142
+ if (config.type !== 'boolean') return value ? 'True' : 'False';
143
+ return value ? (config.trueLabel ?? 'True') : (config.falseLabel ?? 'False');
144
+ }
145
+
146
+ function getBooleanFilterItems(config: DataTableFilterConfig) {
147
+ return [
148
+ { value: 'true', label: getBooleanLabel(config, true) },
149
+ { value: 'false', label: getBooleanLabel(config, false) }
150
+ ];
151
+ }
152
+ </script>
153
+
154
+ <Popover.Root positioning={{ placement: 'bottom-end' }}>
155
+ <Popover.Trigger
156
+ class="flex h-9 cursor-pointer items-center gap-2 rounded-md border border-surface-3 bg-surface-1 px-3 text-sm font-medium text-ink shadow-sm outline-none hover:bg-surface-2 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
157
+ >
158
+ <span>{triggerLabel}</span>
159
+ {#if activeFilterCount > 0}
160
+ <span class="rounded-full bg-surface-3 px-1.5 text-xs text-ink-dim">
161
+ {activeFilterCount}
162
+ </span>
163
+ {/if}
164
+ </Popover.Trigger>
165
+
166
+ <Popover.Content class="w-80 p-0" showArrow={false}>
167
+ <Popover.Title class="flex items-center justify-between gap-3 border-b px-4 py-2">
168
+ <div class="text-sm font-medium text-ink">Filters</div>
169
+ <Button
170
+ variant="ghost"
171
+ disabled={activeFilterCount === 0}
172
+ onclick={() => table.resetColumnFilters(true)}
173
+ >
174
+ Clear
175
+ </Button>
176
+ </Popover.Title>
177
+
178
+ {#if filterableColumns.length === 0}
179
+ <div class="py-2 text-sm text-ink-dim">No filters available</div>
180
+ {:else}
181
+ <ScrollArea.Root class="h-96">
182
+ <ScrollArea.Viewport>
183
+ <ScrollArea.Content>
184
+ <div class="flex flex-col gap-4">
185
+ {#each filterableColumns as column (column.id)}
186
+ {@const config = getFilterConfig(column, columnFilters)}
187
+ {#if config}
188
+ <div class="space-y-2">
189
+ {#if config.type === 'text'}
190
+ <Field.Root>
191
+ <Field.Label>{getColumnLabel(column)}</Field.Label>
192
+ <Field.Input
193
+ type="search"
194
+ placeholder={config.placeholder ?? `Search ${getColumnLabel(column)}`}
195
+ value={String(getFilterValue(column, columnFilters) ?? '')}
196
+ oninput={(event) =>
197
+ setTextFilter(column, (event.currentTarget as HTMLInputElement).value)}
198
+ />
199
+ </Field.Root>
200
+ {:else if config.type === 'number-range'}
201
+ {@const rangeValue = getNumberRangeValue(column, columnFilters)}
202
+ <div class="text-sm font-medium text-ink">{getColumnLabel(column)}</div>
203
+ <div class="grid grid-cols-2 gap-2">
204
+ <NumberInput
205
+ label="Min"
206
+ min={config.min}
207
+ max={config.max}
208
+ step={config.step}
209
+ formatOptions={config.formatOptions ?? { minimumFractionDigits: 0 }}
210
+ value={rangeValue[0] ?? null}
211
+ onValueChange={({ valueAsNumber }) =>
212
+ setNumberRangeFilter(
213
+ column,
214
+ 0,
215
+ Number.isNaN(valueAsNumber) ? null : valueAsNumber
216
+ )}
217
+ />
218
+ <NumberInput
219
+ label="Max"
220
+ min={config.min}
221
+ max={config.max}
222
+ step={config.step}
223
+ formatOptions={config.formatOptions ?? { minimumFractionDigits: 0 }}
224
+ value={rangeValue[1] ?? null}
225
+ onValueChange={({ valueAsNumber }) =>
226
+ setNumberRangeFilter(
227
+ column,
228
+ 1,
229
+ Number.isNaN(valueAsNumber) ? null : valueAsNumber
230
+ )}
231
+ />
232
+ </div>
233
+ {:else if config.type === 'boolean'}
234
+ <Select
235
+ label={getColumnLabel(column)}
236
+ items={getBooleanFilterItems(config)}
237
+ placeholder="All"
238
+ value={getBooleanFilterValue(column, columnFilters)}
239
+ onValueChange={({ value }) =>
240
+ setBooleanFilter(
241
+ column,
242
+ Array.isArray(value) && value.length > 0 ? value[0] : ''
243
+ )}
244
+ />
245
+ {:else if config.type === 'facet'}
246
+ <Fieldset.Root class="max-h-44">
247
+ <Fieldset.Legend>{getColumnLabel(column)}</Fieldset.Legend>
248
+ <ScrollArea.Root class="hrounded-md border border-surface-3">
249
+ <ScrollArea.Viewport>
250
+ <ScrollArea.Content class="p-1 pe-3">
251
+ <div class="flex flex-col">
252
+ {#each getFacetOptions(column, config, columnFilters) as option (String(option.value))}
253
+ <Checkbox
254
+ size="sm"
255
+ label={option.label}
256
+ class="min-h-8 rounded-sm px-2 hover:bg-surface-2"
257
+ checked={getFacetOptionChecked(column, option, columnFilters)}
258
+ onCheckedChange={({ checked }) =>
259
+ toggleFacetFilterValue(column, option, checked === true)}
260
+ />
261
+ {/each}
262
+ </div>
263
+ </ScrollArea.Content>
264
+ </ScrollArea.Viewport>
265
+ <ScrollArea.Scrollbar orientation="vertical">
266
+ <ScrollArea.Thumb />
267
+ </ScrollArea.Scrollbar>
268
+ <ScrollArea.Corner />
269
+ </ScrollArea.Root>
270
+ </Fieldset.Root>
271
+ {/if}
272
+ </div>
273
+ {/if}
274
+ {/each}
275
+ </div>
276
+ </ScrollArea.Content>
277
+ </ScrollArea.Viewport>
278
+ <ScrollArea.Scrollbar orientation="vertical">
279
+ <ScrollArea.Thumb />
280
+ </ScrollArea.Scrollbar>
281
+ <ScrollArea.Corner />
282
+ </ScrollArea.Root>
283
+ {/if}
284
+ </Popover.Content>
285
+ </Popover.Root>
@@ -0,0 +1,29 @@
1
+ import type { RowData } from '@tanstack/svelte-table';
2
+ import { type DataTable } from './core';
3
+ declare function $$render<TData extends RowData, TSelected = object>(): {
4
+ props: {
5
+ table: DataTable<TData, TSelected>;
6
+ triggerLabel?: string;
7
+ };
8
+ exports: {};
9
+ bindings: "";
10
+ slots: {};
11
+ events: {};
12
+ };
13
+ declare class __sveltets_Render<TData extends RowData, TSelected = object> {
14
+ props(): ReturnType<typeof $$render<TData, TSelected>>['props'];
15
+ events(): ReturnType<typeof $$render<TData, TSelected>>['events'];
16
+ slots(): ReturnType<typeof $$render<TData, TSelected>>['slots'];
17
+ bindings(): "";
18
+ exports(): {};
19
+ }
20
+ interface $$IsomorphicComponent {
21
+ new <TData extends RowData, TSelected = object>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<TData, TSelected>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<TData, TSelected>['props']>, ReturnType<__sveltets_Render<TData, TSelected>['events']>, ReturnType<__sveltets_Render<TData, TSelected>['slots']>> & {
22
+ $$bindings?: ReturnType<__sveltets_Render<TData, TSelected>['bindings']>;
23
+ } & ReturnType<__sveltets_Render<TData, TSelected>['exports']>;
24
+ <TData extends RowData, TSelected = object>(internal: unknown, props: ReturnType<__sveltets_Render<TData, TSelected>['props']> & {}): ReturnType<__sveltets_Render<TData, TSelected>['exports']>;
25
+ z_$$bindings?: ReturnType<__sveltets_Render<any, any>['bindings']>;
26
+ }
27
+ declare const DataTableFilters: $$IsomorphicComponent;
28
+ type DataTableFilters<TData extends RowData, TSelected = object> = InstanceType<typeof DataTableFilters<TData, TSelected>>;
29
+ export default DataTableFilters;
@@ -0,0 +1,16 @@
1
+ <script lang="ts">
2
+ import { cn, type ClassValue } from 'tailwind-variants';
3
+ import type { HTMLAttributes } from 'svelte/elements';
4
+ import type { Snippet } from 'svelte';
5
+
6
+ type Props = Omit<HTMLAttributes<HTMLHeadingElement>, 'class'> & {
7
+ class?: ClassValue;
8
+ children?: Snippet;
9
+ };
10
+
11
+ let { class: className, children, ...rest }: Props = $props();
12
+ </script>
13
+
14
+ <h2 class={cn('text-lg font-semibold text-ink', className)} {...rest}>
15
+ {@render children?.()}
16
+ </h2>
@@ -0,0 +1,10 @@
1
+ import { type ClassValue } from 'tailwind-variants';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import type { Snippet } from 'svelte';
4
+ type Props = Omit<HTMLAttributes<HTMLHeadingElement>, 'class'> & {
5
+ class?: ClassValue;
6
+ children?: Snippet;
7
+ };
8
+ declare const DataTableTitle: import("svelte").Component<Props, {}, "">;
9
+ type DataTableTitle = ReturnType<typeof DataTableTitle>;
10
+ export default DataTableTitle;
@@ -0,0 +1,16 @@
1
+ <script lang="ts">
2
+ import { cn, type ClassValue } from 'tailwind-variants';
3
+ import type { HTMLAttributes } from 'svelte/elements';
4
+ import type { Snippet } from 'svelte';
5
+
6
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, 'class'> & {
7
+ class?: ClassValue;
8
+ children?: Snippet;
9
+ };
10
+
11
+ let { class: className, children, ...rest }: Props = $props();
12
+ </script>
13
+
14
+ <div class={cn('mb-4 flex flex-wrap items-center justify-between gap-3', className)} {...rest}>
15
+ {@render children?.()}
16
+ </div>
@@ -0,0 +1,10 @@
1
+ import { type ClassValue } from 'tailwind-variants';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import type { Snippet } from 'svelte';
4
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, 'class'> & {
5
+ class?: ClassValue;
6
+ children?: Snippet;
7
+ };
8
+ declare const DataTableToolbar: import("svelte").Component<Props, {}, "">;
9
+ type DataTableToolbar = ReturnType<typeof DataTableToolbar>;
10
+ export default DataTableToolbar;