@invopop/popui 0.1.35 → 0.1.40
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/BaseButton.svelte +4 -0
- package/dist/BaseDropdown.svelte +42 -3
- package/dist/BaseDropdown.svelte.d.ts +1 -0
- package/dist/BaseTableHeaderOrderBy.svelte +35 -12
- package/dist/ButtonSearch.svelte +82 -0
- package/dist/ButtonSearch.svelte.d.ts +4 -0
- package/dist/ButtonUuidCopy.svelte +1 -0
- package/dist/DatePicker.svelte +96 -27
- package/dist/DatePicker.svelte.d.ts +5 -1
- package/dist/DrawerContext.svelte +443 -34
- package/dist/DrawerContextItem.svelte +36 -29
- package/dist/DropdownSelect.svelte +68 -18
- package/dist/DropdownSelect.svelte.d.ts +4 -1
- package/dist/DropdownSelectGroup.svelte +15 -0
- package/dist/DropdownSelectGroup.svelte.d.ts +7 -0
- package/dist/EmptyState.svelte +6 -2
- package/dist/InputSearch.svelte +45 -5
- package/dist/InputSelect.svelte +12 -3
- package/dist/InputText.svelte +25 -8
- package/dist/InputToggle.svelte +23 -6
- package/dist/StepIcon.svelte +35 -0
- package/dist/StepIcon.svelte.d.ts +4 -0
- package/dist/StepIconList.svelte +24 -31
- package/dist/TagStatus.svelte +1 -1
- package/dist/button/button.svelte +34 -3
- package/dist/button/button.svelte.d.ts +29 -0
- package/dist/clickOutside.d.ts +5 -2
- package/dist/clickOutside.js +9 -3
- package/dist/data-table/cells/boolean-cell.svelte +29 -0
- package/dist/data-table/cells/boolean-cell.svelte.d.ts +8 -0
- package/dist/data-table/cells/cell-skeleton.svelte +35 -0
- package/dist/data-table/cells/cell-skeleton.svelte.d.ts +4 -0
- package/dist/data-table/cells/currency-cell.svelte +10 -0
- package/dist/data-table/cells/currency-cell.svelte.d.ts +8 -0
- package/dist/data-table/cells/date-cell.svelte +10 -0
- package/dist/data-table/cells/date-cell.svelte.d.ts +8 -0
- package/dist/data-table/cells/tag-cell.svelte +12 -0
- package/dist/data-table/cells/tag-cell.svelte.d.ts +8 -0
- package/dist/data-table/cells/text-cell.svelte +10 -0
- package/dist/data-table/cells/text-cell.svelte.d.ts +8 -0
- package/dist/data-table/cells/uuid-cell.svelte +17 -0
- package/dist/data-table/cells/uuid-cell.svelte.d.ts +8 -0
- package/dist/data-table/column-definitions.d.ts +12 -0
- package/dist/data-table/column-definitions.js +42 -0
- package/dist/data-table/column-sizing-helpers.d.ts +6 -0
- package/dist/data-table/column-sizing-helpers.js +24 -0
- package/dist/data-table/create-columns.d.ts +3 -0
- package/dist/data-table/create-columns.js +67 -0
- package/dist/data-table/data-table-cell.svelte +94 -0
- package/dist/data-table/data-table-cell.svelte.d.ts +25 -0
- package/dist/data-table/data-table-header-cell.svelte +188 -0
- package/dist/data-table/data-table-header-cell.svelte.d.ts +25 -0
- package/dist/data-table/data-table-helpers.d.ts +10 -0
- package/dist/data-table/data-table-helpers.js +124 -0
- package/dist/data-table/data-table-pagination.svelte +214 -0
- package/dist/data-table/data-table-pagination.svelte.d.ts +4 -0
- package/dist/data-table/data-table-row.svelte +57 -0
- package/dist/data-table/data-table-row.svelte.d.ts +25 -0
- package/dist/data-table/data-table-svelte.svelte.d.ts +40 -0
- package/dist/data-table/data-table-svelte.svelte.js +115 -0
- package/dist/data-table/data-table-toolbar.svelte +19 -0
- package/dist/data-table/data-table-toolbar.svelte.d.ts +32 -0
- package/dist/data-table/data-table-types.d.ts +194 -0
- package/dist/data-table/data-table-types.js +1 -0
- package/dist/data-table/data-table-view-options.svelte +126 -0
- package/dist/data-table/data-table-view-options.svelte.d.ts +29 -0
- package/dist/data-table/data-table.svelte +428 -0
- package/dist/data-table/data-table.svelte.d.ts +25 -0
- package/dist/data-table/flex-render.svelte +40 -0
- package/dist/data-table/flex-render.svelte.d.ts +33 -0
- package/dist/data-table/index.d.ts +13 -0
- package/dist/data-table/index.js +13 -0
- package/dist/data-table/render-helpers.d.ts +90 -0
- package/dist/data-table/render-helpers.js +99 -0
- package/dist/data-table/table-setup.d.ts +39 -0
- package/dist/data-table/table-setup.js +151 -0
- package/dist/data-table/table-styles.d.ts +17 -0
- package/dist/data-table/table-styles.js +70 -0
- package/dist/drawer-dnd-helpers.d.ts +30 -0
- package/dist/drawer-dnd-helpers.js +72 -0
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.js +3 -0
- package/dist/index.d.ts +15 -3
- package/dist/index.js +28 -5
- package/dist/skeleton/index.d.ts +5 -0
- package/dist/skeleton/index.js +7 -0
- package/dist/skeleton/skeleton-avatar.svelte +14 -0
- package/dist/skeleton/skeleton-avatar.svelte.d.ts +7 -0
- package/dist/skeleton/skeleton-card.svelte +22 -0
- package/dist/skeleton/skeleton-card.svelte.d.ts +9 -0
- package/dist/skeleton/skeleton-list.svelte +25 -0
- package/dist/skeleton/skeleton-list.svelte.d.ts +8 -0
- package/dist/skeleton/skeleton.svelte +17 -0
- package/dist/skeleton/skeleton.svelte.d.ts +5 -0
- package/dist/svg/IconDelivery.svelte +1 -1
- package/dist/svg/IconOrder.svelte +1 -1
- package/dist/svg/IconPayment.svelte +1 -1
- package/dist/table/table-cell.svelte +4 -2
- package/dist/table/table-head.svelte +4 -2
- package/dist/table/table-header.svelte +1 -1
- package/dist/table/table-row.svelte +4 -2
- package/dist/table/table.svelte +2 -2
- package/dist/tailwind.theme.css +30 -6
- package/dist/tooltip/index.d.ts +2 -1
- package/dist/tooltip/index.js +3 -2
- package/dist/tooltip/tooltip-auto-hide.svelte +31 -0
- package/dist/tooltip/tooltip-auto-hide.svelte.d.ts +7 -0
- package/dist/types.d.ts +51 -73
- package/package.json +14 -8
- package/dist/BaseTable.svelte +0 -391
- package/dist/BaseTable.svelte.d.ts +0 -4
- package/dist/BaseTableCellContent.svelte +0 -58
- package/dist/BaseTableCellContent.svelte.d.ts +0 -4
- package/dist/BaseTableCheckbox.svelte +0 -33
- package/dist/BaseTableCheckbox.svelte.d.ts +0 -4
- package/dist/BaseTableHeaderContent.svelte +0 -67
- package/dist/BaseTableHeaderContent.svelte.d.ts +0 -4
- package/dist/BaseTableRow.svelte +0 -127
- package/dist/BaseTableRow.svelte.d.ts +0 -4
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
<script lang="ts" generics="TData">
|
|
2
|
+
import type { DataTableHeaderCellProps } from './data-table-types.js'
|
|
3
|
+
import type { Column, Header } from '@tanstack/table-core'
|
|
4
|
+
import type { HTMLAttributes } from 'svelte/elements'
|
|
5
|
+
import * as Table from '../table/index.js'
|
|
6
|
+
import BaseDropdown from '../BaseDropdown.svelte'
|
|
7
|
+
import BaseTableHeaderOrderBy from '../BaseTableHeaderOrderBy.svelte'
|
|
8
|
+
import FlexRender from './flex-render.svelte'
|
|
9
|
+
import { Icon } from '@steeze-ui/svelte-icon'
|
|
10
|
+
import { ArrowUp, ArrowDown } from '@invopop/ui-icons'
|
|
11
|
+
import { cn } from '../utils.js'
|
|
12
|
+
import { calculateFrozenOffset } from './data-table-helpers.js'
|
|
13
|
+
import { getHeaderStyle, getHeaderClasses } from './table-styles.js'
|
|
14
|
+
import clsx from 'clsx'
|
|
15
|
+
|
|
16
|
+
let {
|
|
17
|
+
header,
|
|
18
|
+
index,
|
|
19
|
+
headers,
|
|
20
|
+
frozenColumns,
|
|
21
|
+
columnDropdowns = $bindable({}),
|
|
22
|
+
onSortingChange,
|
|
23
|
+
onFilterChange,
|
|
24
|
+
onFreezeChange,
|
|
25
|
+
loading = false
|
|
26
|
+
}: DataTableHeaderCellProps<TData> = $props()
|
|
27
|
+
|
|
28
|
+
const isLastScrollable = $derived(index === headers.length - 2)
|
|
29
|
+
const isFirstHeader = $derived(index === 0)
|
|
30
|
+
const isLastHeader = $derived(index === headers.length - 1)
|
|
31
|
+
const prevHeader = $derived(index > 0 ? headers[index - 1] : null)
|
|
32
|
+
const isFrozen = $derived(frozenColumns.has(header.id))
|
|
33
|
+
const lastFrozenHeaderId = $derived(
|
|
34
|
+
headers
|
|
35
|
+
.slice()
|
|
36
|
+
.reverse()
|
|
37
|
+
.find((h) => frozenColumns.has(h.id))?.id
|
|
38
|
+
)
|
|
39
|
+
const isLastFrozenHeader = $derived(isFrozen && header.id === lastFrozenHeaderId)
|
|
40
|
+
const frozenOffset = $derived(isFrozen ? calculateFrozenOffset(header.id, headers, frozenColumns) : 0)
|
|
41
|
+
const hasSelectColumn = $derived(headers.some((h) => h.id === 'select'))
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
{#snippet ResizeHandle({
|
|
45
|
+
side,
|
|
46
|
+
targetHeader,
|
|
47
|
+
label
|
|
48
|
+
}: {
|
|
49
|
+
side: 'left' | 'right'
|
|
50
|
+
targetHeader: Header<TData, unknown>
|
|
51
|
+
label: string
|
|
52
|
+
})}
|
|
53
|
+
<div
|
|
54
|
+
role="button"
|
|
55
|
+
tabindex="0"
|
|
56
|
+
aria-label={label}
|
|
57
|
+
class={cn(
|
|
58
|
+
'absolute top-0 h-full w-6 cursor-col-resize select-none touch-none group z-0',
|
|
59
|
+
side === 'left' ? 'left-0 -ml-3' : 'right-0 -mr-3'
|
|
60
|
+
)}
|
|
61
|
+
onmousedown={targetHeader.getResizeHandler()}
|
|
62
|
+
ontouchstart={targetHeader.getResizeHandler()}
|
|
63
|
+
>
|
|
64
|
+
<div
|
|
65
|
+
class={cn(
|
|
66
|
+
'absolute top-0 h-full w-0.5 bg-border-default-secondary transition-opacity opacity-0',
|
|
67
|
+
side === 'left' ? 'left-1.5' : 'right-1.5',
|
|
68
|
+
!targetHeader.column.getIsResizing() && 'group-hover:opacity-100'
|
|
69
|
+
)}
|
|
70
|
+
></div>
|
|
71
|
+
</div>
|
|
72
|
+
{/snippet}
|
|
73
|
+
|
|
74
|
+
{#snippet ColumnHeader({
|
|
75
|
+
column,
|
|
76
|
+
title,
|
|
77
|
+
class: className,
|
|
78
|
+
isFirst = false,
|
|
79
|
+
hasSelectColumn = false,
|
|
80
|
+
...restProps
|
|
81
|
+
}: {
|
|
82
|
+
column: Column<TData>
|
|
83
|
+
title?: string
|
|
84
|
+
isFirst?: boolean
|
|
85
|
+
hasSelectColumn?: boolean
|
|
86
|
+
} & HTMLAttributes<HTMLDivElement>)}
|
|
87
|
+
{@const isCurrency = column.columnDef.meta?.cellType === 'currency'}
|
|
88
|
+
{@const needsEdgePadding = isFirst && !hasSelectColumn}
|
|
89
|
+
<div
|
|
90
|
+
class={cn(
|
|
91
|
+
'flex items-center w-full [th[data-last-frozen=true]_&]:border-r [th[data-last-frozen=true]_&]:border-border',
|
|
92
|
+
className
|
|
93
|
+
)}
|
|
94
|
+
oncontextmenu={(e) => {
|
|
95
|
+
e.preventDefault()
|
|
96
|
+
columnDropdowns[column.id]?.toggle()
|
|
97
|
+
}}
|
|
98
|
+
{...restProps}
|
|
99
|
+
>
|
|
100
|
+
<BaseDropdown bind:this={columnDropdowns[column.id]} fullWidth usePortal={false}>
|
|
101
|
+
{#snippet trigger()}
|
|
102
|
+
<button
|
|
103
|
+
class={clsx(
|
|
104
|
+
'data-[state=open]:bg-accent hover:bg-background-default-secondary w-full flex items-center gap-1 py-2.5',
|
|
105
|
+
{
|
|
106
|
+
'justify-end': isCurrency,
|
|
107
|
+
'text-left': !isCurrency,
|
|
108
|
+
'pl-4 pr-3': needsEdgePadding,
|
|
109
|
+
'px-3': !needsEdgePadding
|
|
110
|
+
}
|
|
111
|
+
)}
|
|
112
|
+
>
|
|
113
|
+
<span>
|
|
114
|
+
{title || ''}
|
|
115
|
+
</span>
|
|
116
|
+
{#if column.getIsSorted() === 'desc'}
|
|
117
|
+
<Icon src={ArrowDown} class="size-4" />
|
|
118
|
+
{:else if column.getIsSorted() === 'asc'}
|
|
119
|
+
<Icon src={ArrowUp} class="size-4" />
|
|
120
|
+
{/if}
|
|
121
|
+
</button>
|
|
122
|
+
{/snippet}
|
|
123
|
+
<BaseTableHeaderOrderBy
|
|
124
|
+
sortDirection={column.getIsSorted() === 'asc' ? 'asc' : 'desc'}
|
|
125
|
+
isActive={column.getIsSorted() !== false}
|
|
126
|
+
isFrozen={frozenColumns.has(column.id)}
|
|
127
|
+
showSortOptions={column.getCanSort()}
|
|
128
|
+
showFilterOption={!column.columnDef.meta?.disableColumnFilter}
|
|
129
|
+
onOrderBy={(direction) => {
|
|
130
|
+
column.toggleSorting(direction === 'desc')
|
|
131
|
+
if (onSortingChange) {
|
|
132
|
+
onSortingChange(column.id, direction)
|
|
133
|
+
}
|
|
134
|
+
columnDropdowns[column.id]?.toggle()
|
|
135
|
+
}}
|
|
136
|
+
onHide={() => {
|
|
137
|
+
column.toggleVisibility(false)
|
|
138
|
+
columnDropdowns[column.id]?.toggle()
|
|
139
|
+
}}
|
|
140
|
+
onFilter={() => {
|
|
141
|
+
onFilterChange?.(column.id)
|
|
142
|
+
columnDropdowns[column.id]?.toggle()
|
|
143
|
+
}}
|
|
144
|
+
onFreeze={() => {
|
|
145
|
+
onFreezeChange?.(column.id)
|
|
146
|
+
columnDropdowns[column.id]?.toggle()
|
|
147
|
+
}}
|
|
148
|
+
/>
|
|
149
|
+
</BaseDropdown>
|
|
150
|
+
</div>
|
|
151
|
+
{/snippet}
|
|
152
|
+
|
|
153
|
+
<Table.Head
|
|
154
|
+
colspan={header.colSpan}
|
|
155
|
+
style={getHeaderStyle(header, isLastScrollable, isFrozen, frozenOffset)}
|
|
156
|
+
class={getHeaderClasses(
|
|
157
|
+
header,
|
|
158
|
+
isLastScrollable,
|
|
159
|
+
isFirstHeader,
|
|
160
|
+
isLastHeader,
|
|
161
|
+
isFrozen,
|
|
162
|
+
isLastFrozenHeader
|
|
163
|
+
)}
|
|
164
|
+
data-last-frozen={isFrozen && isLastFrozenHeader ? 'true' : undefined}
|
|
165
|
+
>
|
|
166
|
+
{#if !header.isPlaceholder}
|
|
167
|
+
{#if typeof header.column.columnDef.header === 'string'}
|
|
168
|
+
{@render ColumnHeader({
|
|
169
|
+
column: header.column as Column<TData>,
|
|
170
|
+
title: header.column.columnDef.header as string,
|
|
171
|
+
isFirst: isFirstHeader,
|
|
172
|
+
hasSelectColumn
|
|
173
|
+
})}
|
|
174
|
+
{:else if header.id === 'select'}
|
|
175
|
+
<div class={cn('flex items-center', loading && 'opacity-30')}>
|
|
176
|
+
<FlexRender content={header.column.columnDef.header} context={header.getContext()} />
|
|
177
|
+
</div>
|
|
178
|
+
{:else}
|
|
179
|
+
<FlexRender content={header.column.columnDef.header} context={header.getContext()} />
|
|
180
|
+
{/if}
|
|
181
|
+
{/if}
|
|
182
|
+
{#if prevHeader && prevHeader.column.getCanResize()}
|
|
183
|
+
{@render ResizeHandle({ side: 'left', targetHeader: prevHeader, label: 'Resize previous column' })}
|
|
184
|
+
{/if}
|
|
185
|
+
{#if header.column.getCanResize()}
|
|
186
|
+
{@render ResizeHandle({ side: 'right', targetHeader: header, label: 'Resize column' })}
|
|
187
|
+
{/if}
|
|
188
|
+
</Table.Head>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { DataTableHeaderCellProps } from './data-table-types.js';
|
|
2
|
+
declare function $$render<TData>(): {
|
|
3
|
+
props: DataTableHeaderCellProps<TData>;
|
|
4
|
+
exports: {};
|
|
5
|
+
bindings: "columnDropdowns";
|
|
6
|
+
slots: {};
|
|
7
|
+
events: {};
|
|
8
|
+
};
|
|
9
|
+
declare class __sveltets_Render<TData> {
|
|
10
|
+
props(): ReturnType<typeof $$render<TData>>['props'];
|
|
11
|
+
events(): ReturnType<typeof $$render<TData>>['events'];
|
|
12
|
+
slots(): ReturnType<typeof $$render<TData>>['slots'];
|
|
13
|
+
bindings(): "columnDropdowns";
|
|
14
|
+
exports(): {};
|
|
15
|
+
}
|
|
16
|
+
interface $$IsomorphicComponent {
|
|
17
|
+
new <TData>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<TData>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<TData>['props']>, ReturnType<__sveltets_Render<TData>['events']>, ReturnType<__sveltets_Render<TData>['slots']>> & {
|
|
18
|
+
$$bindings?: ReturnType<__sveltets_Render<TData>['bindings']>;
|
|
19
|
+
} & ReturnType<__sveltets_Render<TData>['exports']>;
|
|
20
|
+
<TData>(internal: unknown, props: ReturnType<__sveltets_Render<TData>['props']> & {}): ReturnType<__sveltets_Render<TData>['exports']>;
|
|
21
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
22
|
+
}
|
|
23
|
+
declare const DataTableHeaderCell: $$IsomorphicComponent;
|
|
24
|
+
type DataTableHeaderCell<TData> = InstanceType<typeof DataTableHeaderCell<TData>>;
|
|
25
|
+
export default DataTableHeaderCell;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Table, Row } from '@tanstack/table-core';
|
|
2
|
+
import type BaseDropdown from '../BaseDropdown.svelte';
|
|
3
|
+
export declare function reorderFrozenColumn<TData>(columnId: string, table: Table<TData>, frozenColumns: Set<string>): void;
|
|
4
|
+
export declare function reorderUnfrozenColumn<TData>(columnId: string, table: Table<TData>, frozenColumns: Set<string>): void;
|
|
5
|
+
export declare function calculateFrozenOffset(columnId: string, headers: any[], frozenColumns: Set<string>): number;
|
|
6
|
+
export declare function handleScrollEvent(event: Event, lastScrollLeft: number, columnDropdowns: Record<string, BaseDropdown>): number;
|
|
7
|
+
export declare function shouldIgnoreKeyEvent(event: KeyboardEvent): boolean;
|
|
8
|
+
export declare function handleArrowDown<TData>(currentIndex: number, rows: Row<TData>[], shiftKey: boolean, enableSelection: boolean, onScroll: () => void): number;
|
|
9
|
+
export declare function handleArrowUp<TData>(currentIndex: number, rows: Row<TData>[], shiftKey: boolean, enableSelection: boolean, onScroll: () => void): number;
|
|
10
|
+
export declare function handleSelectKey<TData>(currentIndex: number, rows: Row<TData>[], enableSelection: boolean): void;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
export function reorderFrozenColumn(columnId, table, frozenColumns) {
|
|
2
|
+
const currentOrder = table.getState().columnOrder.length > 0
|
|
3
|
+
? table.getState().columnOrder
|
|
4
|
+
: table.getAllLeafColumns().map((col) => col.id);
|
|
5
|
+
const newOrder = [...currentOrder];
|
|
6
|
+
const columnIndex = newOrder.indexOf(columnId);
|
|
7
|
+
if (columnIndex > -1) {
|
|
8
|
+
newOrder.splice(columnIndex, 1);
|
|
9
|
+
const selectIndex = newOrder.indexOf('select');
|
|
10
|
+
const insertIndex = selectIndex >= 0 ? selectIndex + 1 : 0;
|
|
11
|
+
let lastFrozenIndex = insertIndex;
|
|
12
|
+
for (let i = insertIndex; i < newOrder.length; i++) {
|
|
13
|
+
if (frozenColumns.has(newOrder[i])) {
|
|
14
|
+
lastFrozenIndex = i + 1;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
newOrder.splice(lastFrozenIndex, 0, columnId);
|
|
21
|
+
table.setColumnOrder(newOrder);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function reorderUnfrozenColumn(columnId, table, frozenColumns) {
|
|
25
|
+
const currentOrder = table.getState().columnOrder.length > 0
|
|
26
|
+
? table.getState().columnOrder
|
|
27
|
+
: table.getAllLeafColumns().map((col) => col.id);
|
|
28
|
+
const newOrder = [...currentOrder];
|
|
29
|
+
const columnIndex = newOrder.indexOf(columnId);
|
|
30
|
+
if (columnIndex > -1) {
|
|
31
|
+
newOrder.splice(columnIndex, 1);
|
|
32
|
+
const selectIndex = newOrder.indexOf('select');
|
|
33
|
+
const insertIndex = selectIndex >= 0 ? selectIndex + 1 : 0;
|
|
34
|
+
// Find the first unfrozen column position (after all frozen columns)
|
|
35
|
+
let firstUnfrozenIndex = insertIndex;
|
|
36
|
+
for (let i = insertIndex; i < newOrder.length; i++) {
|
|
37
|
+
if (frozenColumns.has(newOrder[i])) {
|
|
38
|
+
firstUnfrozenIndex = i + 1;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
newOrder.splice(firstUnfrozenIndex, 0, columnId);
|
|
45
|
+
table.setColumnOrder(newOrder);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export function calculateFrozenOffset(columnId, headers, frozenColumns) {
|
|
49
|
+
let offset = 0;
|
|
50
|
+
// Find the position of current column
|
|
51
|
+
for (const header of headers) {
|
|
52
|
+
if (header.id === columnId) {
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
// Only add width of previous frozen columns that are visible (or select column)
|
|
56
|
+
const isVisible = header.column?.getIsVisible?.() ?? true;
|
|
57
|
+
if (isVisible && (frozenColumns.has(header.id) || header.id === 'select')) {
|
|
58
|
+
offset += header.getSize();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return offset;
|
|
62
|
+
}
|
|
63
|
+
export function handleScrollEvent(event, lastScrollLeft, columnDropdowns) {
|
|
64
|
+
const target = event.target;
|
|
65
|
+
if (target.scrollLeft !== lastScrollLeft) {
|
|
66
|
+
// Close all column dropdowns
|
|
67
|
+
Object.values(columnDropdowns).forEach((dropdown) => {
|
|
68
|
+
if (dropdown) {
|
|
69
|
+
dropdown.close();
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
return target.scrollLeft;
|
|
73
|
+
}
|
|
74
|
+
return lastScrollLeft;
|
|
75
|
+
}
|
|
76
|
+
// Keyboard navigation helpers
|
|
77
|
+
export function shouldIgnoreKeyEvent(event) {
|
|
78
|
+
const targetTag = event.target.tagName;
|
|
79
|
+
return targetTag === 'INPUT' || targetTag === 'TEXTAREA';
|
|
80
|
+
}
|
|
81
|
+
export function handleArrowDown(currentIndex, rows, shiftKey, enableSelection, onScroll) {
|
|
82
|
+
if (currentIndex === -1 && rows.length > 0) {
|
|
83
|
+
// No row focused, focus first row
|
|
84
|
+
const newIndex = 0;
|
|
85
|
+
onScroll();
|
|
86
|
+
if (shiftKey && enableSelection) {
|
|
87
|
+
rows[newIndex].toggleSelected(true);
|
|
88
|
+
}
|
|
89
|
+
return newIndex;
|
|
90
|
+
}
|
|
91
|
+
else if (currentIndex < rows.length - 1) {
|
|
92
|
+
// Move down
|
|
93
|
+
const newIndex = currentIndex + 1;
|
|
94
|
+
onScroll();
|
|
95
|
+
if (shiftKey && enableSelection) {
|
|
96
|
+
rows[newIndex].toggleSelected(true);
|
|
97
|
+
}
|
|
98
|
+
return newIndex;
|
|
99
|
+
}
|
|
100
|
+
return currentIndex;
|
|
101
|
+
}
|
|
102
|
+
export function handleArrowUp(currentIndex, rows, shiftKey, enableSelection, onScroll) {
|
|
103
|
+
// Deselect current row first when going up with shift
|
|
104
|
+
if (shiftKey && enableSelection && currentIndex >= 0) {
|
|
105
|
+
rows[currentIndex].toggleSelected(false);
|
|
106
|
+
}
|
|
107
|
+
if (currentIndex === -1 && rows.length > 0) {
|
|
108
|
+
// No row focused, focus first row
|
|
109
|
+
onScroll();
|
|
110
|
+
return 0;
|
|
111
|
+
}
|
|
112
|
+
else if (currentIndex > 0) {
|
|
113
|
+
// Move up
|
|
114
|
+
const newIndex = currentIndex - 1;
|
|
115
|
+
onScroll();
|
|
116
|
+
return newIndex;
|
|
117
|
+
}
|
|
118
|
+
return currentIndex;
|
|
119
|
+
}
|
|
120
|
+
export function handleSelectKey(currentIndex, rows, enableSelection) {
|
|
121
|
+
if (currentIndex >= 0 && currentIndex < rows.length && enableSelection) {
|
|
122
|
+
rows[currentIndex].toggleSelected();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Button from '../button/button.svelte'
|
|
3
|
+
import InputSelect from '../InputSelect.svelte'
|
|
4
|
+
import InputText from '../InputText.svelte'
|
|
5
|
+
import { ArrowLeft, ArrowRight, ScrollLeft, ScrollRight } from '@invopop/ui-icons'
|
|
6
|
+
import { cn } from '../utils.js'
|
|
7
|
+
import clsx from 'clsx'
|
|
8
|
+
import type { DataTablePaginationProps } from './data-table-types.js'
|
|
9
|
+
|
|
10
|
+
let {
|
|
11
|
+
table,
|
|
12
|
+
id,
|
|
13
|
+
class: className,
|
|
14
|
+
showRowsPerPage = true,
|
|
15
|
+
rowsPerPageOptions = [10, 25, 50, 100],
|
|
16
|
+
itemsLabel = 'items',
|
|
17
|
+
children,
|
|
18
|
+
onPageChange,
|
|
19
|
+
onPageSizeChange,
|
|
20
|
+
data,
|
|
21
|
+
rowCount,
|
|
22
|
+
manualPagination,
|
|
23
|
+
disabled = false
|
|
24
|
+
}: DataTablePaginationProps<any> = $props()
|
|
25
|
+
|
|
26
|
+
let currentPage = $derived(table.getState().pagination.pageIndex + 1)
|
|
27
|
+
let rowsPerPage = $derived(table.getState().pagination.pageSize)
|
|
28
|
+
let totalItems = $derived.by(() => {
|
|
29
|
+
// Use direct props for reactivity
|
|
30
|
+
if (manualPagination && rowCount !== undefined) {
|
|
31
|
+
return rowCount
|
|
32
|
+
}
|
|
33
|
+
// For client-side pagination, use data length directly
|
|
34
|
+
return data?.length ?? 0
|
|
35
|
+
})
|
|
36
|
+
// Calculate totalPages from reactive values instead of calling table.getPageCount()
|
|
37
|
+
let totalPages = $derived(Math.ceil(totalItems / rowsPerPage) || 1)
|
|
38
|
+
|
|
39
|
+
let pageInputValue = $derived(`${currentPage}`)
|
|
40
|
+
|
|
41
|
+
function handlePageInput(value: string) {
|
|
42
|
+
const numValue = parseInt(value)
|
|
43
|
+
if (numValue >= 1 && numValue <= totalPages) {
|
|
44
|
+
if (manualPagination) {
|
|
45
|
+
table.setPagination({ pageIndex: numValue - 1, pageSize: rowsPerPage })
|
|
46
|
+
} else {
|
|
47
|
+
table.setPageIndex(numValue - 1)
|
|
48
|
+
}
|
|
49
|
+
onPageChange?.(numValue)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function handlePageBlur(event: Event) {
|
|
54
|
+
const target = event.target as HTMLInputElement
|
|
55
|
+
const value = parseInt(target.value)
|
|
56
|
+
if (isNaN(value) || value < 1) {
|
|
57
|
+
target.value = `${currentPage}`
|
|
58
|
+
} else if (value > totalPages) {
|
|
59
|
+
target.value = `${totalPages}`
|
|
60
|
+
if (manualPagination) {
|
|
61
|
+
table.setPagination({ pageIndex: totalPages - 1, pageSize: rowsPerPage })
|
|
62
|
+
} else {
|
|
63
|
+
table.setPageIndex(totalPages - 1)
|
|
64
|
+
}
|
|
65
|
+
onPageChange?.(totalPages)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function formatNumber(num: number): string {
|
|
70
|
+
return new Intl.NumberFormat('en-US').format(num)
|
|
71
|
+
}
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<div
|
|
75
|
+
{id}
|
|
76
|
+
class={cn(
|
|
77
|
+
'flex items-center justify-between h-11 px-4 py-[5px] bg-background backdrop-blur-[10px] border-t border-border',
|
|
78
|
+
className
|
|
79
|
+
)}
|
|
80
|
+
>
|
|
81
|
+
<div class={clsx('flex items-center gap-3', {
|
|
82
|
+
'pointer-events-none opacity-30': disabled
|
|
83
|
+
})}>
|
|
84
|
+
<div class="flex items-center gap-2">
|
|
85
|
+
<div class="flex items-center gap-1.5">
|
|
86
|
+
<div class="flex items-center">
|
|
87
|
+
<Button
|
|
88
|
+
variant="ghost"
|
|
89
|
+
size="md"
|
|
90
|
+
icon={ScrollLeft}
|
|
91
|
+
onclick={() => {
|
|
92
|
+
if (manualPagination) {
|
|
93
|
+
table.setPagination({ pageIndex: 0, pageSize: rowsPerPage })
|
|
94
|
+
} else {
|
|
95
|
+
table.setPageIndex(0)
|
|
96
|
+
}
|
|
97
|
+
onPageChange?.(1)
|
|
98
|
+
}}
|
|
99
|
+
disabled={currentPage === 1}
|
|
100
|
+
class={cn(currentPage === 1 && 'pointer-events-none opacity-30')}
|
|
101
|
+
aria-label="First page"
|
|
102
|
+
/>
|
|
103
|
+
<Button
|
|
104
|
+
variant="ghost"
|
|
105
|
+
size="md"
|
|
106
|
+
icon={ArrowLeft}
|
|
107
|
+
onclick={() => {
|
|
108
|
+
const newPage = currentPage - 1
|
|
109
|
+
if (manualPagination) {
|
|
110
|
+
// For manual pagination, bypass TanStack's navigation and use setPagination directly
|
|
111
|
+
// to avoid clamping issues with stale pageCount
|
|
112
|
+
table.setPagination({ pageIndex: newPage - 1, pageSize: rowsPerPage })
|
|
113
|
+
} else {
|
|
114
|
+
table.previousPage()
|
|
115
|
+
}
|
|
116
|
+
onPageChange?.(newPage)
|
|
117
|
+
}}
|
|
118
|
+
disabled={currentPage === 1}
|
|
119
|
+
class={cn(currentPage === 1 && 'pointer-events-none opacity-30')}
|
|
120
|
+
aria-label="Previous page"
|
|
121
|
+
/>
|
|
122
|
+
</div>
|
|
123
|
+
<div class="flex items-center gap-1.5">
|
|
124
|
+
<div
|
|
125
|
+
class="w-12 [&>div]:gap-0 [&_input]:[appearance:textfield] [&_input]:[&::-webkit-outer-spin-button]:appearance-none [&_input]:[&::-webkit-inner-spin-button]:appearance-none"
|
|
126
|
+
>
|
|
127
|
+
<InputText
|
|
128
|
+
bind:value={pageInputValue}
|
|
129
|
+
type="number"
|
|
130
|
+
min="1"
|
|
131
|
+
max={totalPages}
|
|
132
|
+
size="sm"
|
|
133
|
+
oninput={handlePageInput}
|
|
134
|
+
onblur={handlePageBlur}
|
|
135
|
+
/>
|
|
136
|
+
</div>
|
|
137
|
+
<span class="text-base text-foreground-default-secondary whitespace-nowrap">
|
|
138
|
+
/ {totalPages}
|
|
139
|
+
</span>
|
|
140
|
+
</div>
|
|
141
|
+
<div class="flex items-center">
|
|
142
|
+
<Button
|
|
143
|
+
variant="ghost"
|
|
144
|
+
size="md"
|
|
145
|
+
icon={ArrowRight}
|
|
146
|
+
onclick={() => {
|
|
147
|
+
const newPage = currentPage + 1
|
|
148
|
+
if (manualPagination) {
|
|
149
|
+
// For manual pagination, bypass TanStack's navigation and use setPagination directly
|
|
150
|
+
// to avoid clamping issues with stale pageCount
|
|
151
|
+
table.setPagination({ pageIndex: newPage - 1, pageSize: rowsPerPage })
|
|
152
|
+
} else {
|
|
153
|
+
table.nextPage()
|
|
154
|
+
}
|
|
155
|
+
onPageChange?.(newPage)
|
|
156
|
+
}}
|
|
157
|
+
disabled={currentPage === totalPages}
|
|
158
|
+
class={cn(currentPage === totalPages && 'pointer-events-none opacity-30')}
|
|
159
|
+
aria-label="Next page"
|
|
160
|
+
/>
|
|
161
|
+
<Button
|
|
162
|
+
variant="ghost"
|
|
163
|
+
size="md"
|
|
164
|
+
icon={ScrollRight}
|
|
165
|
+
onclick={() => {
|
|
166
|
+
if (manualPagination) {
|
|
167
|
+
table.setPagination({ pageIndex: totalPages - 1, pageSize: rowsPerPage })
|
|
168
|
+
} else {
|
|
169
|
+
table.setPageIndex(totalPages - 1)
|
|
170
|
+
}
|
|
171
|
+
onPageChange?.(totalPages)
|
|
172
|
+
}}
|
|
173
|
+
disabled={currentPage === totalPages}
|
|
174
|
+
class={cn(currentPage === totalPages && 'pointer-events-none opacity-30')}
|
|
175
|
+
aria-label="Last page"
|
|
176
|
+
/>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
{#if showRowsPerPage}
|
|
180
|
+
<div class="w-[105px]">
|
|
181
|
+
<InputSelect
|
|
182
|
+
value={`${rowsPerPage}`}
|
|
183
|
+
options={rowsPerPageOptions.map((size) => ({
|
|
184
|
+
value: `${size}`,
|
|
185
|
+
label: `${size} rows`
|
|
186
|
+
}))}
|
|
187
|
+
size="sm"
|
|
188
|
+
onchange={(value) => {
|
|
189
|
+
const size = Number(value)
|
|
190
|
+
table.setPageSize(size)
|
|
191
|
+
table.setPageIndex(0)
|
|
192
|
+
onPageSizeChange?.(size)
|
|
193
|
+
onPageChange?.(1)
|
|
194
|
+
}}
|
|
195
|
+
placeholder="Rows per page"
|
|
196
|
+
disablePlaceholder={true}
|
|
197
|
+
aria-label="Rows per page"
|
|
198
|
+
/>
|
|
199
|
+
</div>
|
|
200
|
+
{/if}
|
|
201
|
+
</div>
|
|
202
|
+
{#if totalItems > 0}
|
|
203
|
+
<span class="text-base text-foreground-default-secondary">
|
|
204
|
+
{formatNumber(totalItems)}
|
|
205
|
+
{itemsLabel}
|
|
206
|
+
</span>
|
|
207
|
+
{/if}
|
|
208
|
+
</div>
|
|
209
|
+
{#if children}
|
|
210
|
+
<div class="flex items-center gap-2">
|
|
211
|
+
{@render children()}
|
|
212
|
+
</div>
|
|
213
|
+
{/if}
|
|
214
|
+
</div>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DataTablePaginationProps } from './data-table-types.js';
|
|
2
|
+
declare const DataTablePagination: import("svelte").Component<DataTablePaginationProps<any>, {}, "">;
|
|
3
|
+
type DataTablePagination = ReturnType<typeof DataTablePagination>;
|
|
4
|
+
export default DataTablePagination;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<script lang="ts" generics="TData">
|
|
2
|
+
import type { DataTableRowProps } from './data-table-types.js'
|
|
3
|
+
import * as Table from '../table/index.js'
|
|
4
|
+
import DataTableCell from './data-table-cell.svelte'
|
|
5
|
+
import { cn } from '../utils.js'
|
|
6
|
+
import clsx from 'clsx'
|
|
7
|
+
|
|
8
|
+
let {
|
|
9
|
+
row,
|
|
10
|
+
rowIndex,
|
|
11
|
+
frozenColumns,
|
|
12
|
+
focusedRowIndex,
|
|
13
|
+
loading = false,
|
|
14
|
+
onRowClick,
|
|
15
|
+
getRowClassName,
|
|
16
|
+
getRowState,
|
|
17
|
+
StickyCellWrapper
|
|
18
|
+
}: DataTableRowProps<TData> = $props()
|
|
19
|
+
|
|
20
|
+
const rowState = $derived(getRowState?.(row.original as TData))
|
|
21
|
+
const isError = $derived(rowState?.isError ?? false)
|
|
22
|
+
const isSuccess = $derived(rowState?.isSuccess ?? false)
|
|
23
|
+
const isWarning = $derived(rowState?.isWarning ?? false)
|
|
24
|
+
const dataState = $derived.by(() => {
|
|
25
|
+
if (row.getIsSelected()) return 'selected'
|
|
26
|
+
if (isError) return 'error'
|
|
27
|
+
if (isWarning) return 'warning'
|
|
28
|
+
if (isSuccess) return 'success'
|
|
29
|
+
return undefined
|
|
30
|
+
})
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<Table.Row
|
|
34
|
+
data-state={dataState}
|
|
35
|
+
data-row-index={rowIndex}
|
|
36
|
+
data-focused={focusedRowIndex === rowIndex ? 'true' : undefined}
|
|
37
|
+
class={cn(
|
|
38
|
+
clsx('shadow-[inset_0_-1px_0_0_var(--color-border)]', {
|
|
39
|
+
'cursor-pointer': onRowClick && !loading,
|
|
40
|
+
'pointer-events-none': loading
|
|
41
|
+
}),
|
|
42
|
+
getRowClassName?.(row.original as TData)
|
|
43
|
+
)}
|
|
44
|
+
onclick={() => !loading && onRowClick?.(row.original as TData)}
|
|
45
|
+
>
|
|
46
|
+
{#each row.getVisibleCells() as cell, index (cell.id)}
|
|
47
|
+
<DataTableCell
|
|
48
|
+
{cell}
|
|
49
|
+
{index}
|
|
50
|
+
visibleCells={row.getVisibleCells()}
|
|
51
|
+
allCells={row.getAllCells()}
|
|
52
|
+
{frozenColumns}
|
|
53
|
+
{loading}
|
|
54
|
+
{StickyCellWrapper}
|
|
55
|
+
/>
|
|
56
|
+
{/each}
|
|
57
|
+
</Table.Row>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { DataTableRowProps } from './data-table-types.js';
|
|
2
|
+
declare function $$render<TData>(): {
|
|
3
|
+
props: DataTableRowProps<TData>;
|
|
4
|
+
exports: {};
|
|
5
|
+
bindings: "";
|
|
6
|
+
slots: {};
|
|
7
|
+
events: {};
|
|
8
|
+
};
|
|
9
|
+
declare class __sveltets_Render<TData> {
|
|
10
|
+
props(): ReturnType<typeof $$render<TData>>['props'];
|
|
11
|
+
events(): ReturnType<typeof $$render<TData>>['events'];
|
|
12
|
+
slots(): ReturnType<typeof $$render<TData>>['slots'];
|
|
13
|
+
bindings(): "";
|
|
14
|
+
exports(): {};
|
|
15
|
+
}
|
|
16
|
+
interface $$IsomorphicComponent {
|
|
17
|
+
new <TData>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<TData>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<TData>['props']>, ReturnType<__sveltets_Render<TData>['events']>, ReturnType<__sveltets_Render<TData>['slots']>> & {
|
|
18
|
+
$$bindings?: ReturnType<__sveltets_Render<TData>['bindings']>;
|
|
19
|
+
} & ReturnType<__sveltets_Render<TData>['exports']>;
|
|
20
|
+
<TData>(internal: unknown, props: ReturnType<__sveltets_Render<TData>['props']> & {}): ReturnType<__sveltets_Render<TData>['exports']>;
|
|
21
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
22
|
+
}
|
|
23
|
+
declare const DataTableRow: $$IsomorphicComponent;
|
|
24
|
+
type DataTableRow<TData> = InstanceType<typeof DataTableRow<TData>>;
|
|
25
|
+
export default DataTableRow;
|