@invopop/popui 0.1.4-beta.1 → 0.1.4-beta.10
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/DatePicker.svelte +77 -25
- package/dist/DropdownSelect.svelte +2 -2
- package/dist/data-table/column-definitions.js +10 -8
- package/dist/data-table/data-table-pagination.svelte +9 -1
- package/dist/data-table/data-table-svelte.svelte.js +4 -0
- package/dist/data-table/data-table-toolbar.svelte +1 -1
- package/dist/data-table/data-table-types.d.ts +5 -0
- package/dist/data-table/data-table.svelte +130 -123
- package/dist/data-table/table-setup.d.ts +6 -4
- package/dist/data-table/table-setup.js +22 -9
- package/dist/data-table/table-styles.js +2 -2
- package/dist/table/table-header.svelte +1 -1
- package/dist/types.d.ts +5 -1
- package/package.json +1 -1
package/dist/DatePicker.svelte
CHANGED
|
@@ -3,13 +3,16 @@
|
|
|
3
3
|
import RangeCalendar from './range-calendar/range-calendar.svelte'
|
|
4
4
|
import { parseDate, type DateValue } from '@internationalized/date'
|
|
5
5
|
import type { DateRange } from 'bits-ui'
|
|
6
|
-
import { Icon } from '@steeze-ui/svelte-icon'
|
|
7
|
-
import { Calendar } from '@invopop/ui-icons'
|
|
6
|
+
import { Icon, type IconSource } from '@steeze-ui/svelte-icon'
|
|
8
7
|
import Transition from 'svelte-transition'
|
|
9
8
|
import type { DatePickerProps } from './types'
|
|
10
9
|
import { clickOutside } from './clickOutside'
|
|
11
10
|
import BaseButton from './BaseButton.svelte'
|
|
12
|
-
import { datesFromToday, toCalendarDate } from './helpers'
|
|
11
|
+
import { datesFromToday, toCalendarDate, resolveIcon } from './helpers'
|
|
12
|
+
import { buttonVariants } from './button/button.svelte'
|
|
13
|
+
import { offset, flip, shift } from 'svelte-floating-ui/dom'
|
|
14
|
+
import { createFloatingActions } from 'svelte-floating-ui'
|
|
15
|
+
import { portal } from 'svelte-portal'
|
|
13
16
|
|
|
14
17
|
const {
|
|
15
18
|
startOfThisWeek,
|
|
@@ -109,23 +112,47 @@
|
|
|
109
112
|
|
|
110
113
|
let {
|
|
111
114
|
label = 'Date',
|
|
112
|
-
|
|
115
|
+
placement = 'bottom-start',
|
|
113
116
|
from = '',
|
|
114
117
|
to = '',
|
|
115
|
-
onSelect
|
|
118
|
+
onSelect,
|
|
119
|
+
stackLeft = false,
|
|
120
|
+
stackRight = false,
|
|
121
|
+
icon = undefined,
|
|
122
|
+
iconTheme = 'default'
|
|
116
123
|
}: DatePickerProps = $props()
|
|
117
124
|
|
|
125
|
+
const [floatingRef, floatingContent] = createFloatingActions({
|
|
126
|
+
strategy: 'absolute',
|
|
127
|
+
placement,
|
|
128
|
+
middleware: [offset(8), flip(), shift()]
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
let resolvedIcon: IconSource | undefined = $state()
|
|
132
|
+
|
|
133
|
+
$effect(() => {
|
|
134
|
+
resolveIcon(icon).then((res) => (resolvedIcon = res))
|
|
135
|
+
})
|
|
136
|
+
|
|
118
137
|
let selectedPeriod = $state('custom')
|
|
119
138
|
let value = $state<DateRange>({
|
|
120
139
|
start: undefined,
|
|
121
140
|
end: undefined
|
|
122
141
|
})
|
|
123
142
|
let isOpen = $state(false)
|
|
143
|
+
let isStacked = $derived(stackLeft || stackRight)
|
|
144
|
+
let hasSelectedDates = $derived(value.start !== undefined)
|
|
124
145
|
let styles = $derived(
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
146
|
+
isStacked
|
|
147
|
+
? buttonVariants({
|
|
148
|
+
variant: 'ghost',
|
|
149
|
+
stackedLeft: stackLeft,
|
|
150
|
+
stackedRight: stackRight
|
|
151
|
+
})
|
|
152
|
+
: clsx('border backdrop-blur-sm backdrop-filter', {
|
|
153
|
+
'border-border-selected-bold shadow-active': isOpen,
|
|
154
|
+
'border-border-default-secondary hover:border-border-default-secondary-hover': !isOpen
|
|
155
|
+
})
|
|
129
156
|
)
|
|
130
157
|
let selectedLabel = $state(label)
|
|
131
158
|
|
|
@@ -137,9 +164,19 @@
|
|
|
137
164
|
|
|
138
165
|
$effect(() => {
|
|
139
166
|
if (from) {
|
|
167
|
+
const startDate = parseDate(from)
|
|
168
|
+
const endDate = to ? parseDate(to) : undefined
|
|
169
|
+
|
|
140
170
|
value = {
|
|
141
|
-
start:
|
|
142
|
-
end:
|
|
171
|
+
start: startDate,
|
|
172
|
+
end: endDate
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Update label directly without calling getLabel() to avoid circular dependency
|
|
176
|
+
if (startDate === endDate) {
|
|
177
|
+
selectedLabel = getDisplayFromValue(startDate)
|
|
178
|
+
} else {
|
|
179
|
+
selectedLabel = `${getDisplayFromValue(startDate)} → ${getDisplayFromValue(endDate)}`
|
|
143
180
|
}
|
|
144
181
|
return
|
|
145
182
|
}
|
|
@@ -183,19 +220,34 @@
|
|
|
183
220
|
</script>
|
|
184
221
|
|
|
185
222
|
<div>
|
|
186
|
-
<
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
223
|
+
<button
|
|
224
|
+
use:floatingRef
|
|
225
|
+
onclick={() => {
|
|
226
|
+
isOpen = !isOpen
|
|
227
|
+
}}
|
|
228
|
+
class="{styles} {isStacked
|
|
229
|
+
? 'h-7 py-1.5'
|
|
230
|
+
: 'py-1.5'} datepicker-trigger flex items-center w-full {resolvedIcon
|
|
231
|
+
? 'pl-7'
|
|
232
|
+
: 'pl-2'} pr-2 text-left rounded-lg bg-background cursor-pointer relative overflow-hidden"
|
|
233
|
+
>
|
|
234
|
+
{#if resolvedIcon}
|
|
235
|
+
<Icon
|
|
236
|
+
src={resolvedIcon}
|
|
237
|
+
theme={iconTheme}
|
|
238
|
+
class="h-4 w-4 absolute top-1.5 left-2 text-foreground-default-secondary"
|
|
239
|
+
/>
|
|
240
|
+
{/if}
|
|
241
|
+
<span
|
|
242
|
+
class="flex-1 text-base truncate {hasSelectedDates
|
|
243
|
+
? 'text-foreground'
|
|
244
|
+
: 'text-foreground-default-secondary'}"
|
|
192
245
|
>
|
|
193
246
|
{selectedLabel}
|
|
194
|
-
</
|
|
195
|
-
|
|
196
|
-
</div>
|
|
247
|
+
</span>
|
|
248
|
+
</button>
|
|
197
249
|
|
|
198
|
-
|
|
250
|
+
{#if isOpen}
|
|
199
251
|
<Transition
|
|
200
252
|
show={isOpen}
|
|
201
253
|
enter="transition ease-out duration-100"
|
|
@@ -207,9 +259,9 @@
|
|
|
207
259
|
>
|
|
208
260
|
<!-- @ts-ignore -->
|
|
209
261
|
<div
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
class="bg-background inline-flex flex-col shadow-lg rounded-xl absolute
|
|
262
|
+
use:portal
|
|
263
|
+
use:floatingContent
|
|
264
|
+
class="bg-background inline-flex flex-col shadow-lg rounded-xl absolute z-1001 border border-border"
|
|
213
265
|
use:clickOutside
|
|
214
266
|
onclick_outside={() => {
|
|
215
267
|
if (!isOpen) return
|
|
@@ -239,5 +291,5 @@
|
|
|
239
291
|
</div>
|
|
240
292
|
</div>
|
|
241
293
|
</Transition>
|
|
242
|
-
|
|
294
|
+
{/if}
|
|
243
295
|
</div>
|
|
@@ -76,10 +76,9 @@
|
|
|
76
76
|
function handleClick(val: AnyProp) {
|
|
77
77
|
value = val
|
|
78
78
|
|
|
79
|
-
onSelect?.(value)
|
|
80
|
-
|
|
81
79
|
if (multiple) return
|
|
82
80
|
|
|
81
|
+
onSelect?.(value)
|
|
83
82
|
selectDropdown?.toggle()
|
|
84
83
|
}
|
|
85
84
|
|
|
@@ -90,6 +89,7 @@
|
|
|
90
89
|
if (isEqual(value, val)) return
|
|
91
90
|
|
|
92
91
|
value = val
|
|
92
|
+
onSelect?.(value)
|
|
93
93
|
}
|
|
94
94
|
</script>
|
|
95
95
|
|
|
@@ -10,19 +10,21 @@ export function createSelectionColumn() {
|
|
|
10
10
|
checked: table.getIsAllPageRowsSelected(),
|
|
11
11
|
onchange: (value) => table.toggleAllPageRowsSelected(value),
|
|
12
12
|
indeterminate: table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected(),
|
|
13
|
-
'aria-label': 'Select all'
|
|
13
|
+
'aria-label': 'Select all',
|
|
14
|
+
onclick: (e) => e.stopPropagation()
|
|
14
15
|
}),
|
|
15
16
|
cell: ({ row }) => renderComponent(InputCheckbox, {
|
|
16
17
|
checked: row.getIsSelected(),
|
|
17
18
|
onchange: (value) => row.toggleSelected(value),
|
|
18
|
-
'aria-label': 'Select row'
|
|
19
|
+
'aria-label': 'Select row',
|
|
20
|
+
onclick: (e) => e.stopPropagation()
|
|
19
21
|
}),
|
|
20
22
|
enableSorting: false,
|
|
21
23
|
enableHiding: false,
|
|
22
24
|
enableResizing: false,
|
|
23
|
-
size:
|
|
24
|
-
minSize:
|
|
25
|
-
maxSize:
|
|
25
|
+
size: 52,
|
|
26
|
+
minSize: 52,
|
|
27
|
+
maxSize: 52
|
|
26
28
|
};
|
|
27
29
|
}
|
|
28
30
|
/**
|
|
@@ -33,8 +35,8 @@ export function createActionsColumn(rowActionsSnippet) {
|
|
|
33
35
|
id: 'actions',
|
|
34
36
|
cell: ({ row }) => renderSnippet(rowActionsSnippet, { row }),
|
|
35
37
|
enableResizing: false,
|
|
36
|
-
size:
|
|
37
|
-
minSize:
|
|
38
|
-
maxSize:
|
|
38
|
+
size: 56,
|
|
39
|
+
minSize: 56,
|
|
40
|
+
maxSize: 56
|
|
39
41
|
};
|
|
40
42
|
}
|
|
@@ -21,7 +21,13 @@
|
|
|
21
21
|
|
|
22
22
|
let currentPage = $derived(table.getState().pagination.pageIndex + 1)
|
|
23
23
|
let totalPages = $derived(table.getPageCount())
|
|
24
|
-
let totalItems = $derived
|
|
24
|
+
let totalItems = $derived.by(() => {
|
|
25
|
+
const rowCount = table.getRowCount?.()
|
|
26
|
+
if (table.options.manualPagination && rowCount !== undefined) {
|
|
27
|
+
return rowCount
|
|
28
|
+
}
|
|
29
|
+
return table.getFilteredRowModel().rows.length
|
|
30
|
+
})
|
|
25
31
|
let rowsPerPage = $derived(table.getState().pagination.pageSize)
|
|
26
32
|
let hasSelection = $derived(Object.keys(table.getState().rowSelection).length > 0)
|
|
27
33
|
|
|
@@ -145,7 +151,9 @@
|
|
|
145
151
|
onchange={(value) => {
|
|
146
152
|
const size = Number(value)
|
|
147
153
|
table.setPageSize(size)
|
|
154
|
+
table.setPageIndex(0)
|
|
148
155
|
onPageSizeChange?.(size)
|
|
156
|
+
onPageChange?.(1)
|
|
149
157
|
}}
|
|
150
158
|
placeholder="Rows per page"
|
|
151
159
|
disablePlaceholder={true}
|
|
@@ -53,6 +53,10 @@ export function createSvelteTable(options) {
|
|
|
53
53
|
}
|
|
54
54
|
updateOptions();
|
|
55
55
|
$effect.pre(() => {
|
|
56
|
+
// Access data and columns to track them - this reads but doesn't write
|
|
57
|
+
// so it won't cause infinite loops
|
|
58
|
+
void options.data;
|
|
59
|
+
void options.columns;
|
|
56
60
|
updateOptions();
|
|
57
61
|
});
|
|
58
62
|
return table;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
let { table, filters }: { table: Table<TData>; filters?: Snippet } = $props()
|
|
7
7
|
</script>
|
|
8
8
|
|
|
9
|
-
<div class="flex items-center justify-between">
|
|
9
|
+
<div class="flex items-center justify-between px-6 py-4">
|
|
10
10
|
{#if filters}
|
|
11
11
|
<div class="flex-1">
|
|
12
12
|
{@render filters()}
|
|
@@ -47,6 +47,7 @@ export interface DataTableProps<TData> {
|
|
|
47
47
|
disableSelection?: boolean;
|
|
48
48
|
disablePagination?: boolean;
|
|
49
49
|
rowActions?: TableAction[];
|
|
50
|
+
getRowActions?: (row: TData) => TableAction[];
|
|
50
51
|
onRowAction?: (action: AnyProp, row: TData) => void;
|
|
51
52
|
initialPageSize?: number;
|
|
52
53
|
pageSizeOptions?: number[];
|
|
@@ -56,8 +57,12 @@ export interface DataTableProps<TData> {
|
|
|
56
57
|
filters?: Snippet;
|
|
57
58
|
paginationSelectedSlot?: Snippet;
|
|
58
59
|
paginationUnselectedSlot?: Snippet;
|
|
60
|
+
manualPagination?: boolean;
|
|
61
|
+
pageCount?: number;
|
|
62
|
+
rowCount?: number;
|
|
59
63
|
onPageChange?: (pageIndex: number) => void;
|
|
60
64
|
onPageSizeChange?: (pageSize: number) => void;
|
|
65
|
+
onSortingChange?: (columnId: string, direction: 'asc' | 'desc') => void;
|
|
61
66
|
}
|
|
62
67
|
export interface DataTablePaginationProps<T> {
|
|
63
68
|
table: Table<T>;
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
disableSelection = false,
|
|
34
34
|
disablePagination = false,
|
|
35
35
|
rowActions = [],
|
|
36
|
+
getRowActions,
|
|
36
37
|
onRowAction,
|
|
37
38
|
initialPageSize = 10,
|
|
38
39
|
emptyState = {
|
|
@@ -45,8 +46,12 @@
|
|
|
45
46
|
filters,
|
|
46
47
|
paginationSelectedSlot,
|
|
47
48
|
paginationUnselectedSlot,
|
|
49
|
+
manualPagination = false,
|
|
50
|
+
pageCount,
|
|
51
|
+
rowCount,
|
|
48
52
|
onPageChange,
|
|
49
|
-
onPageSizeChange
|
|
53
|
+
onPageSizeChange,
|
|
54
|
+
onSortingChange
|
|
50
55
|
}: DataTableProps<TData> = $props()
|
|
51
56
|
|
|
52
57
|
const enableSelection = !disableSelection
|
|
@@ -70,7 +75,7 @@
|
|
|
70
75
|
|
|
71
76
|
// Build TanStack columns from config
|
|
72
77
|
const columns = $derived.by(() =>
|
|
73
|
-
buildColumns<TData>(columnConfig, enableSelection, RowActions, rowActions.length > 0)
|
|
78
|
+
buildColumns<TData>(columnConfig, enableSelection, RowActions, getRowActions !== undefined || rowActions.length > 0)
|
|
74
79
|
)
|
|
75
80
|
|
|
76
81
|
// Calculate initial column sizes based on available width
|
|
@@ -95,14 +100,13 @@
|
|
|
95
100
|
})
|
|
96
101
|
|
|
97
102
|
const table = setupTable({
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
},
|
|
101
|
-
get columns() {
|
|
102
|
-
return columns
|
|
103
|
-
},
|
|
103
|
+
getData: () => data,
|
|
104
|
+
getColumns: () => columns,
|
|
104
105
|
enableSelection,
|
|
105
106
|
enablePagination,
|
|
107
|
+
manualPagination,
|
|
108
|
+
pageCount,
|
|
109
|
+
getRowCount: () => rowCount,
|
|
106
110
|
getRowSelection: () => rowSelection,
|
|
107
111
|
getColumnVisibility: () => columnVisibility,
|
|
108
112
|
getSorting: () => sorting,
|
|
@@ -129,8 +133,8 @@
|
|
|
129
133
|
})}
|
|
130
134
|
<div
|
|
131
135
|
class={cn(
|
|
132
|
-
'h-10 flex items-center
|
|
133
|
-
align === 'right' ? 'justify-end' : ''
|
|
136
|
+
'h-10 flex items-center relative group-hover/row:bg-background-default-secondary group-data-[state=selected]/row:bg-background-selected',
|
|
137
|
+
align === 'right' ? 'justify-end pl-3 pr-6' : 'pl-6 pr-3'
|
|
134
138
|
)}
|
|
135
139
|
>
|
|
136
140
|
<div class="relative z-10">
|
|
@@ -141,7 +145,7 @@
|
|
|
141
145
|
|
|
142
146
|
{#snippet RowActions({ row }: { row: Row<TData> })}
|
|
143
147
|
<BaseTableActions
|
|
144
|
-
actions={rowActions}
|
|
148
|
+
actions={getRowActions ? getRowActions(row.original) : rowActions}
|
|
145
149
|
onclick={(action) => {
|
|
146
150
|
if (onRowAction) {
|
|
147
151
|
onRowAction(action, row.original)
|
|
@@ -180,7 +184,12 @@
|
|
|
180
184
|
<BaseTableHeaderOrderBy
|
|
181
185
|
sortDirection={column.getIsSorted() === 'asc' ? 'asc' : 'desc'}
|
|
182
186
|
isActive={column.getIsSorted() !== false}
|
|
183
|
-
onOrderBy={(direction) =>
|
|
187
|
+
onOrderBy={(direction) => {
|
|
188
|
+
column.toggleSorting(direction === 'desc')
|
|
189
|
+
if (onSortingChange) {
|
|
190
|
+
onSortingChange(column.id, direction)
|
|
191
|
+
}
|
|
192
|
+
}}
|
|
184
193
|
onHide={() => column.toggleVisibility(false)}
|
|
185
194
|
/>
|
|
186
195
|
</BaseDropdown>
|
|
@@ -188,128 +197,126 @@
|
|
|
188
197
|
{/if}
|
|
189
198
|
{/snippet}
|
|
190
199
|
|
|
191
|
-
<div class="flex flex-col
|
|
200
|
+
<div class="flex flex-col h-full">
|
|
192
201
|
<DataTableToolbar {table} {filters} />
|
|
193
|
-
<div class="flex flex-col
|
|
194
|
-
<div bind:this={containerRef} class="relative bg-background">
|
|
195
|
-
<
|
|
196
|
-
<Table.
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
{
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
{#if
|
|
208
|
-
{
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
/>
|
|
218
|
-
{/if}
|
|
202
|
+
<div class="flex-1 overflow-hidden flex flex-col">
|
|
203
|
+
<div bind:this={containerRef} class="relative bg-background flex-1 overflow-auto">
|
|
204
|
+
<Table.Root>
|
|
205
|
+
<Table.Header>
|
|
206
|
+
{#each table.getHeaderGroups() as headerGroup (headerGroup.id)}
|
|
207
|
+
<Table.Row class="hover:!bg-transparent border-b border-border">
|
|
208
|
+
{#each headerGroup.headers as header, index (header.id)}
|
|
209
|
+
{@const isLastScrollable = index === headerGroup.headers.length - 2}
|
|
210
|
+
<Table.Head
|
|
211
|
+
colspan={header.colSpan}
|
|
212
|
+
style={getHeaderStyle(header, isLastScrollable)}
|
|
213
|
+
class={getHeaderClasses(header, isLastScrollable)}
|
|
214
|
+
>
|
|
215
|
+
{#if !header.isPlaceholder}
|
|
216
|
+
{#if typeof header.column.columnDef.header === 'string'}
|
|
217
|
+
{@render ColumnHeader({
|
|
218
|
+
column: header.column as Column<TData>,
|
|
219
|
+
title: header.column.columnDef.header as string
|
|
220
|
+
})}
|
|
221
|
+
{:else}
|
|
222
|
+
<FlexRender
|
|
223
|
+
content={header.column.columnDef.header}
|
|
224
|
+
context={header.getContext()}
|
|
225
|
+
/>
|
|
219
226
|
{/if}
|
|
220
|
-
|
|
221
|
-
|
|
227
|
+
{/if}
|
|
228
|
+
{#if header.column.getCanResize()}
|
|
229
|
+
<!-- Always visible vertical border -->
|
|
230
|
+
<div
|
|
231
|
+
class={cn(
|
|
232
|
+
'absolute right-0 top-1/2 -translate-y-1/2 h-3 w-px bg-background-default-tertiary',
|
|
233
|
+
header.column.getIsResizing() && 'opacity-0'
|
|
234
|
+
)}
|
|
235
|
+
></div>
|
|
236
|
+
<!-- Resize handler (larger interactive area, enhanced on hover) -->
|
|
237
|
+
<div
|
|
238
|
+
role="button"
|
|
239
|
+
tabindex="0"
|
|
240
|
+
aria-label="Resize column"
|
|
241
|
+
class="absolute right-0 top-0 h-full w-3 cursor-col-resize select-none touch-none group -mr-1.5"
|
|
242
|
+
onmousedown={header.getResizeHandler()}
|
|
243
|
+
ontouchstart={header.getResizeHandler()}
|
|
244
|
+
>
|
|
222
245
|
<div
|
|
223
246
|
class={cn(
|
|
224
|
-
'absolute right-
|
|
225
|
-
header.column.getIsResizing() && 'opacity-
|
|
247
|
+
'absolute right-1.5 top-0 h-full w-0.5 bg-border-default-secondary transition-opacity opacity-0',
|
|
248
|
+
!header.column.getIsResizing() && 'group-hover:opacity-100'
|
|
226
249
|
)}
|
|
227
250
|
></div>
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
{@const visibleCells = row.getVisibleCells()}
|
|
260
|
-
{@const firstDataColumnIndex = visibleCells.findIndex(
|
|
261
|
-
(c) => c.column.id !== 'select' && c.column.id !== 'actions'
|
|
262
|
-
)}
|
|
263
|
-
{@const isFirstDataColumn = index === firstDataColumnIndex}
|
|
264
|
-
<Table.Cell
|
|
265
|
-
style={getCellStyle(cell, isLastScrollable)}
|
|
266
|
-
class={getCellClasses(cell, isLastScrollable, isFirstDataColumn)}
|
|
267
|
-
>
|
|
268
|
-
{#if cell.column.id === 'actions'}
|
|
269
|
-
{@render StickyCellWrapper({
|
|
270
|
-
align: 'right',
|
|
271
|
-
children: CellContent
|
|
272
|
-
})}
|
|
273
|
-
{#snippet CellContent()}
|
|
274
|
-
<FlexRender
|
|
275
|
-
content={cell.column.columnDef.cell}
|
|
276
|
-
context={cell.getContext()}
|
|
277
|
-
/>
|
|
278
|
-
{/snippet}
|
|
279
|
-
{:else if cell.column.id === 'select'}
|
|
280
|
-
{@render StickyCellWrapper({
|
|
281
|
-
align: 'left',
|
|
282
|
-
children: CellContent
|
|
283
|
-
})}
|
|
284
|
-
{#snippet CellContent()}
|
|
285
|
-
<FlexRender
|
|
286
|
-
content={cell.column.columnDef.cell}
|
|
287
|
-
context={cell.getContext()}
|
|
288
|
-
/>
|
|
289
|
-
{/snippet}
|
|
290
|
-
{:else}
|
|
251
|
+
</div>
|
|
252
|
+
{/if}
|
|
253
|
+
</Table.Head>
|
|
254
|
+
{/each}
|
|
255
|
+
</Table.Row>
|
|
256
|
+
{/each}
|
|
257
|
+
</Table.Header>
|
|
258
|
+
<Table.Body>
|
|
259
|
+
{#each table.getRowModel().rows as row (row.id)}
|
|
260
|
+
<Table.Row
|
|
261
|
+
data-state={row.getIsSelected() ? 'selected' : undefined}
|
|
262
|
+
class="border-b border-border"
|
|
263
|
+
onclick={() => onRowClick?.(row.original as TData)}
|
|
264
|
+
>
|
|
265
|
+
{#each row.getVisibleCells() as cell, index (cell.id)}
|
|
266
|
+
{@const isLastScrollable = index === row.getVisibleCells().length - 2}
|
|
267
|
+
{@const visibleCells = row.getVisibleCells()}
|
|
268
|
+
{@const firstDataColumnIndex = visibleCells.findIndex(
|
|
269
|
+
(c) => c.column.id !== 'select' && c.column.id !== 'actions'
|
|
270
|
+
)}
|
|
271
|
+
{@const isFirstDataColumn = index === firstDataColumnIndex}
|
|
272
|
+
<Table.Cell
|
|
273
|
+
style={getCellStyle(cell, isLastScrollable)}
|
|
274
|
+
class={getCellClasses(cell, isLastScrollable, isFirstDataColumn)}
|
|
275
|
+
>
|
|
276
|
+
{#if cell.column.id === 'actions'}
|
|
277
|
+
{@render StickyCellWrapper({
|
|
278
|
+
align: 'right',
|
|
279
|
+
children: CellContent
|
|
280
|
+
})}
|
|
281
|
+
{#snippet CellContent()}
|
|
291
282
|
<FlexRender
|
|
292
283
|
content={cell.column.columnDef.cell}
|
|
293
284
|
context={cell.getContext()}
|
|
294
285
|
/>
|
|
295
|
-
{/
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
286
|
+
{/snippet}
|
|
287
|
+
{:else if cell.column.id === 'select'}
|
|
288
|
+
{@render StickyCellWrapper({
|
|
289
|
+
align: 'left',
|
|
290
|
+
children: CellContent
|
|
291
|
+
})}
|
|
292
|
+
{#snippet CellContent()}
|
|
293
|
+
<FlexRender
|
|
294
|
+
content={cell.column.columnDef.cell}
|
|
295
|
+
context={cell.getContext()}
|
|
296
|
+
/>
|
|
297
|
+
{/snippet}
|
|
298
|
+
{:else}
|
|
299
|
+
<FlexRender
|
|
300
|
+
content={cell.column.columnDef.cell}
|
|
301
|
+
context={cell.getContext()}
|
|
302
|
+
/>
|
|
303
|
+
{/if}
|
|
307
304
|
</Table.Cell>
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
305
|
+
{/each}
|
|
306
|
+
</Table.Row>
|
|
307
|
+
{:else}
|
|
308
|
+
<Table.Row class="hover:!bg-transparent">
|
|
309
|
+
<Table.Cell colspan={columns.length} class="h-48">
|
|
310
|
+
<EmptyState
|
|
311
|
+
iconSource={emptyState.iconSource}
|
|
312
|
+
title={emptyState.title}
|
|
313
|
+
description={emptyState.description}
|
|
314
|
+
/>
|
|
315
|
+
</Table.Cell>
|
|
316
|
+
</Table.Row>
|
|
317
|
+
{/each}
|
|
318
|
+
</Table.Body>
|
|
319
|
+
</Table.Root>
|
|
313
320
|
</div>
|
|
314
321
|
{#if enablePagination}
|
|
315
322
|
<DataTablePagination
|
|
@@ -11,6 +11,11 @@ export declare function buildColumns<TData>(columnConfig: DataTableColumn<TData>
|
|
|
11
11
|
interface TableSetupOptions<TData> {
|
|
12
12
|
enableSelection: boolean;
|
|
13
13
|
enablePagination: boolean;
|
|
14
|
+
manualPagination?: boolean;
|
|
15
|
+
pageCount?: number;
|
|
16
|
+
getRowCount?: () => number | undefined;
|
|
17
|
+
getData?: () => TData[];
|
|
18
|
+
getColumns?: () => any[];
|
|
14
19
|
getRowSelection: () => RowSelectionState;
|
|
15
20
|
getColumnVisibility: () => VisibilityState;
|
|
16
21
|
getSorting: () => SortingState;
|
|
@@ -29,8 +34,5 @@ interface TableSetupOptions<TData> {
|
|
|
29
34
|
/**
|
|
30
35
|
* Create the TanStack table instance with all configuration
|
|
31
36
|
*/
|
|
32
|
-
export declare function setupTable<TData>(options: TableSetupOptions<TData>
|
|
33
|
-
data?: TData[];
|
|
34
|
-
columns?: any[];
|
|
35
|
-
}): import("@tanstack/table-core").Table<unknown>;
|
|
37
|
+
export declare function setupTable<TData>(options: TableSetupOptions<TData>): import("@tanstack/table-core").Table<unknown>;
|
|
36
38
|
export {};
|
|
@@ -55,6 +55,12 @@ export function setupTable(options) {
|
|
|
55
55
|
return options.getColumnOrder();
|
|
56
56
|
}
|
|
57
57
|
},
|
|
58
|
+
get data() {
|
|
59
|
+
return options.getData?.() ?? [];
|
|
60
|
+
},
|
|
61
|
+
get columns() {
|
|
62
|
+
return options.getColumns?.() ?? [];
|
|
63
|
+
},
|
|
58
64
|
enableRowSelection: options.enableSelection,
|
|
59
65
|
enableColumnResizing: true,
|
|
60
66
|
columnResizeMode: 'onChange',
|
|
@@ -116,15 +122,22 @@ export function setupTable(options) {
|
|
|
116
122
|
}
|
|
117
123
|
},
|
|
118
124
|
getCoreRowModel: getCoreRowModel(),
|
|
119
|
-
getPaginationRowModel: options.enablePagination ? getPaginationRowModel() : undefined,
|
|
120
|
-
getSortedRowModel: getSortedRowModel()
|
|
125
|
+
getPaginationRowModel: options.enablePagination && !options.manualPagination ? getPaginationRowModel() : undefined,
|
|
126
|
+
getSortedRowModel: getSortedRowModel(),
|
|
127
|
+
// Manual pagination configuration
|
|
128
|
+
manualPagination: options.manualPagination,
|
|
129
|
+
get pageCount() {
|
|
130
|
+
// Calculate pageCount from rowCount and current pageSize
|
|
131
|
+
const rowCount = options.getRowCount?.();
|
|
132
|
+
if (rowCount !== undefined) {
|
|
133
|
+
const pageSize = options.getPagination().pageSize;
|
|
134
|
+
return Math.ceil(rowCount / pageSize);
|
|
135
|
+
}
|
|
136
|
+
return options.pageCount ?? -1;
|
|
137
|
+
},
|
|
138
|
+
get rowCount() {
|
|
139
|
+
return options.getRowCount?.();
|
|
140
|
+
}
|
|
121
141
|
};
|
|
122
|
-
// Add data and columns as getters if provided
|
|
123
|
-
if (options.data !== undefined) {
|
|
124
|
-
tableOptions.data = options.data;
|
|
125
|
-
}
|
|
126
|
-
if (options.columns !== undefined) {
|
|
127
|
-
tableOptions.columns = options.columns;
|
|
128
|
-
}
|
|
129
142
|
return createSvelteTable(tableOptions);
|
|
130
143
|
}
|
|
@@ -17,8 +17,8 @@ export function getHeaderStyle(header, isLastScrollable) {
|
|
|
17
17
|
*/
|
|
18
18
|
export function getHeaderClasses(header, isLastScrollable) {
|
|
19
19
|
return clsx('relative whitespace-nowrap overflow-hidden', {
|
|
20
|
-
'sticky right-0 text-right bg-background': header.id === 'actions',
|
|
21
|
-
'sticky left-0 bg-background z-10': header.id === 'select',
|
|
20
|
+
'sticky right-0 text-right bg-background pl-3 pr-6': header.id === 'actions',
|
|
21
|
+
'sticky left-0 bg-background z-10 pl-6 pr-3': header.id === 'select',
|
|
22
22
|
'w-full': isLastScrollable,
|
|
23
23
|
'hover:!bg-transparent': !header.column.getCanSort()
|
|
24
24
|
});
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
<thead
|
|
16
16
|
bind:this={ref}
|
|
17
17
|
data-slot="table-header"
|
|
18
|
-
class={cn('[&_tr]:border-b [&_tr]:border-border bg-background', className)}
|
|
18
|
+
class={cn('sticky top-0 z-20 [&_tr]:border-b [&_tr]:border-border bg-background', className)}
|
|
19
19
|
onclick={bubble('click')}
|
|
20
20
|
onkeydown={bubble('keydown')}
|
|
21
21
|
>
|
package/dist/types.d.ts
CHANGED
|
@@ -368,13 +368,17 @@ export interface DataListItemProps {
|
|
|
368
368
|
}
|
|
369
369
|
export interface DatePickerProps {
|
|
370
370
|
label?: string;
|
|
371
|
-
|
|
371
|
+
placement?: Placement;
|
|
372
372
|
from?: string;
|
|
373
373
|
to?: string;
|
|
374
374
|
onSelect?: (date: {
|
|
375
375
|
from: string;
|
|
376
376
|
to: string;
|
|
377
377
|
}) => void;
|
|
378
|
+
stackLeft?: boolean;
|
|
379
|
+
stackRight?: boolean;
|
|
380
|
+
icon?: IconSource | string;
|
|
381
|
+
iconTheme?: IconTheme;
|
|
378
382
|
}
|
|
379
383
|
export interface DrawerContextProps {
|
|
380
384
|
items?: DrawerOption[];
|