@beeblock/svelar-datatable 0.1.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/README.md +149 -0
- package/dist/SvelarDatatablePlugin.d.ts +13 -0
- package/dist/export/ExportManager.d.ts +4 -0
- package/dist/export/clipboard.d.ts +2 -0
- package/dist/export/csv.d.ts +4 -0
- package/dist/export/excel.d.ts +2 -0
- package/dist/export/index.d.ts +6 -0
- package/dist/export/pdf.d.ts +2 -0
- package/dist/export/print.d.ts +2 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +19 -0
- package/dist/server/DataTableController.d.ts +4 -0
- package/dist/server/DataTableRequest.d.ts +3 -0
- package/dist/server/DataTableService.d.ts +25 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.js +1 -0
- package/dist/state/DataTableStore.d.ts +64 -0
- package/dist/state/ServerDataTableStore.d.ts +23 -0
- package/dist/state/index.d.ts +2 -0
- package/dist/types.d.ts +208 -0
- package/dist/types.js +0 -0
- package/dist/ui/index.d.ts +20 -0
- package/package.json +45 -0
- package/src/ui/DataTable.svelte +385 -0
- package/src/ui/DataTableBody.svelte +180 -0
- package/src/ui/DataTableBubbleEditor.svelte +93 -0
- package/src/ui/DataTableButtons.svelte +139 -0
- package/src/ui/DataTableCell.svelte +381 -0
- package/src/ui/DataTableColumnToggle.svelte +111 -0
- package/src/ui/DataTableEditor.svelte +27 -0
- package/src/ui/DataTableEditorField.svelte +190 -0
- package/src/ui/DataTableEditorForm.svelte +94 -0
- package/src/ui/DataTableEmpty.svelte +40 -0
- package/src/ui/DataTableExpandedRow.svelte +37 -0
- package/src/ui/DataTableFooter.svelte +65 -0
- package/src/ui/DataTableHead.svelte +169 -0
- package/src/ui/DataTableLoading.svelte +44 -0
- package/src/ui/DataTableModalEditor.svelte +126 -0
- package/src/ui/DataTablePagination.svelte +205 -0
- package/src/ui/DataTableRow.svelte +192 -0
- package/src/ui/DataTableSearch.svelte +95 -0
- package/src/ui/DataTableToolbar.svelte +164 -0
- package/src/ui/index.ts +20 -0
- package/src/ui/virtual/VirtualScroller.svelte +62 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ColumnDef, ButtonDef, ExportFormat, DataTableStore, EditorMode, DataTableClassNames } from '../index.js';
|
|
3
|
+
import DataTableSearch from './DataTableSearch.svelte';
|
|
4
|
+
import DataTableColumnToggle from './DataTableColumnToggle.svelte';
|
|
5
|
+
import DataTableButtons from './DataTableButtons.svelte';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
store: DataTableStore;
|
|
9
|
+
columns: ColumnDef[];
|
|
10
|
+
searchable?: boolean;
|
|
11
|
+
searchDebounceMs?: number;
|
|
12
|
+
buttons?: (ButtonDef | ExportFormat)[];
|
|
13
|
+
editorMode?: EditorMode;
|
|
14
|
+
classNames?: DataTableClassNames;
|
|
15
|
+
onCreateClick?: () => void;
|
|
16
|
+
onEditClick?: (rows: any[]) => void;
|
|
17
|
+
onDeleteClick?: (rows: any[]) => void;
|
|
18
|
+
}
|
|
19
|
+
let {
|
|
20
|
+
store,
|
|
21
|
+
columns,
|
|
22
|
+
searchable = true,
|
|
23
|
+
searchDebounceMs = 300,
|
|
24
|
+
buttons,
|
|
25
|
+
editorMode,
|
|
26
|
+
classNames = {},
|
|
27
|
+
onCreateClick,
|
|
28
|
+
onEditClick,
|
|
29
|
+
onDeleteClick,
|
|
30
|
+
}: Props = $props();
|
|
31
|
+
|
|
32
|
+
let state = $state(store.getState());
|
|
33
|
+
$effect(() => {
|
|
34
|
+
return store.subscribe(() => {
|
|
35
|
+
state = store.getState();
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
let selectedCount = $derived(state.selectedIds.size);
|
|
40
|
+
|
|
41
|
+
function handleCreate() {
|
|
42
|
+
if (onCreateClick) {
|
|
43
|
+
onCreateClick();
|
|
44
|
+
} else {
|
|
45
|
+
// Create always uses modal — bubble/inline don't support create forms
|
|
46
|
+
store.openEditor(null, null, 'modal');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function handleEdit() {
|
|
51
|
+
const selected = store.getSelectedRows();
|
|
52
|
+
if (onEditClick) {
|
|
53
|
+
onEditClick(selected);
|
|
54
|
+
} else if (selected.length === 1) {
|
|
55
|
+
const mode = editorMode === 'bubble' ? 'bubble' : 'modal';
|
|
56
|
+
store.openEditor(store.getRowId(selected[0]), null, mode);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function handleDelete() {
|
|
61
|
+
const selected = store.getSelectedRows();
|
|
62
|
+
if (onDeleteClick) {
|
|
63
|
+
onDeleteClick(selected);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<div class="sdt-toolbar {classNames.toolbar ?? ''}">
|
|
69
|
+
<div class="sdt-toolbar-left {classNames.toolbarLeft ?? ''}">
|
|
70
|
+
{#if searchable}
|
|
71
|
+
<DataTableSearch {store} debounceMs={searchDebounceMs} searchInputClass={classNames.searchInput} />
|
|
72
|
+
{/if}
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<div class="sdt-toolbar-right {classNames.toolbarRight ?? ''}">
|
|
76
|
+
{#if editorMode && editorMode !== 'excel'}
|
|
77
|
+
<button type="button" class="sdt-toolbar-btn sdt-toolbar-btn-create {classNames.btnCreate ?? ''}" onclick={handleCreate}>
|
|
78
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14">
|
|
79
|
+
<path d="M12 5v14M5 12h14" />
|
|
80
|
+
</svg>
|
|
81
|
+
New
|
|
82
|
+
</button>
|
|
83
|
+
{#if selectedCount === 1}
|
|
84
|
+
<button type="button" class="sdt-toolbar-btn sdt-toolbar-btn-edit {classNames.btnEdit ?? ''}" onclick={handleEdit}>
|
|
85
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14">
|
|
86
|
+
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
|
|
87
|
+
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
|
|
88
|
+
</svg>
|
|
89
|
+
Edit
|
|
90
|
+
</button>
|
|
91
|
+
{/if}
|
|
92
|
+
{#if selectedCount > 0 && onDeleteClick}
|
|
93
|
+
<button type="button" class="sdt-toolbar-btn sdt-toolbar-btn-delete {classNames.btnDelete ?? ''}" onclick={handleDelete}>
|
|
94
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14">
|
|
95
|
+
<path d="M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
|
|
96
|
+
</svg>
|
|
97
|
+
Delete ({selectedCount})
|
|
98
|
+
</button>
|
|
99
|
+
{/if}
|
|
100
|
+
{/if}
|
|
101
|
+
{#if buttons && buttons.length > 0}
|
|
102
|
+
<DataTableButtons {buttons} {store} {columns} />
|
|
103
|
+
{/if}
|
|
104
|
+
<DataTableColumnToggle {store} {columns} />
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<style>
|
|
109
|
+
.sdt-toolbar {
|
|
110
|
+
display: flex;
|
|
111
|
+
align-items: center;
|
|
112
|
+
justify-content: space-between;
|
|
113
|
+
padding: 0.75rem 1rem;
|
|
114
|
+
gap: 0.75rem;
|
|
115
|
+
flex-wrap: wrap;
|
|
116
|
+
}
|
|
117
|
+
.sdt-toolbar-left {
|
|
118
|
+
display: flex;
|
|
119
|
+
align-items: center;
|
|
120
|
+
gap: 0.5rem;
|
|
121
|
+
flex: 1;
|
|
122
|
+
min-width: 0;
|
|
123
|
+
}
|
|
124
|
+
.sdt-toolbar-right {
|
|
125
|
+
display: flex;
|
|
126
|
+
align-items: center;
|
|
127
|
+
gap: 0.5rem;
|
|
128
|
+
flex-shrink: 0;
|
|
129
|
+
}
|
|
130
|
+
.sdt-toolbar-btn {
|
|
131
|
+
display: inline-flex;
|
|
132
|
+
align-items: center;
|
|
133
|
+
gap: 0.25rem;
|
|
134
|
+
padding: 0.5rem 0.75rem;
|
|
135
|
+
border-radius: 0.375rem;
|
|
136
|
+
font-size: 0.8125rem;
|
|
137
|
+
font-weight: 500;
|
|
138
|
+
cursor: pointer;
|
|
139
|
+
border: 1px solid transparent;
|
|
140
|
+
transition: background-color 0.15s;
|
|
141
|
+
}
|
|
142
|
+
.sdt-toolbar-btn-create {
|
|
143
|
+
background: var(--sdt-primary, #3b82f6);
|
|
144
|
+
color: #fff;
|
|
145
|
+
}
|
|
146
|
+
.sdt-toolbar-btn-create:hover {
|
|
147
|
+
background: var(--sdt-primary-hover, #2563eb);
|
|
148
|
+
}
|
|
149
|
+
.sdt-toolbar-btn-edit {
|
|
150
|
+
background: var(--sdt-input-bg, #fff);
|
|
151
|
+
border-color: var(--sdt-border, #e5e7eb);
|
|
152
|
+
color: var(--sdt-text, #111827);
|
|
153
|
+
}
|
|
154
|
+
.sdt-toolbar-btn-edit:hover {
|
|
155
|
+
background: var(--sdt-hover, #f3f4f6);
|
|
156
|
+
}
|
|
157
|
+
.sdt-toolbar-btn-delete {
|
|
158
|
+
background: #ef4444;
|
|
159
|
+
color: #fff;
|
|
160
|
+
}
|
|
161
|
+
.sdt-toolbar-btn-delete:hover {
|
|
162
|
+
background: #dc2626;
|
|
163
|
+
}
|
|
164
|
+
</style>
|
package/src/ui/index.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export { default as DataTable } from './DataTable.svelte';
|
|
2
|
+
export { default as DataTableHead } from './DataTableHead.svelte';
|
|
3
|
+
export { default as DataTableBody } from './DataTableBody.svelte';
|
|
4
|
+
export { default as DataTableRow } from './DataTableRow.svelte';
|
|
5
|
+
export { default as DataTableCell } from './DataTableCell.svelte';
|
|
6
|
+
export { default as DataTableExpandedRow } from './DataTableExpandedRow.svelte';
|
|
7
|
+
export { default as DataTableFooter } from './DataTableFooter.svelte';
|
|
8
|
+
export { default as DataTablePagination } from './DataTablePagination.svelte';
|
|
9
|
+
export { default as DataTableSearch } from './DataTableSearch.svelte';
|
|
10
|
+
export { default as DataTableToolbar } from './DataTableToolbar.svelte';
|
|
11
|
+
export { default as DataTableColumnToggle } from './DataTableColumnToggle.svelte';
|
|
12
|
+
export { default as DataTableButtons } from './DataTableButtons.svelte';
|
|
13
|
+
export { default as DataTableEditor } from './DataTableEditor.svelte';
|
|
14
|
+
export { default as DataTableEditorForm } from './DataTableEditorForm.svelte';
|
|
15
|
+
export { default as DataTableEditorField } from './DataTableEditorField.svelte';
|
|
16
|
+
export { default as DataTableModalEditor } from './DataTableModalEditor.svelte';
|
|
17
|
+
export { default as DataTableBubbleEditor } from './DataTableBubbleEditor.svelte';
|
|
18
|
+
export { default as DataTableLoading } from './DataTableLoading.svelte';
|
|
19
|
+
export { default as DataTableEmpty } from './DataTableEmpty.svelte';
|
|
20
|
+
export { default as VirtualScroller } from './virtual/VirtualScroller.svelte';
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
items: any[];
|
|
6
|
+
rowHeight: number;
|
|
7
|
+
containerHeight?: number;
|
|
8
|
+
overscan?: number;
|
|
9
|
+
renderRow: Snippet<[{ item: any; index: number; style: string }]>;
|
|
10
|
+
}
|
|
11
|
+
let {
|
|
12
|
+
items,
|
|
13
|
+
rowHeight,
|
|
14
|
+
containerHeight = 500,
|
|
15
|
+
overscan = 5,
|
|
16
|
+
renderRow,
|
|
17
|
+
}: Props = $props();
|
|
18
|
+
|
|
19
|
+
let scrollTop = $state(0);
|
|
20
|
+
let containerEl: HTMLDivElement | undefined = $state();
|
|
21
|
+
|
|
22
|
+
let totalHeight = $derived(items.length * rowHeight);
|
|
23
|
+
|
|
24
|
+
let visibleStart = $derived(Math.max(0, Math.floor(scrollTop / rowHeight) - overscan));
|
|
25
|
+
let visibleEnd = $derived(
|
|
26
|
+
Math.min(items.length, Math.ceil((scrollTop + containerHeight) / rowHeight) + overscan)
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
let visibleItems = $derived(
|
|
30
|
+
items.slice(visibleStart, visibleEnd).map((item, i) => ({
|
|
31
|
+
item,
|
|
32
|
+
index: visibleStart + i,
|
|
33
|
+
style: `position: absolute; top: ${(visibleStart + i) * rowHeight}px; width: 100%; height: ${rowHeight}px;`,
|
|
34
|
+
}))
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
function handleScroll(e: Event) {
|
|
38
|
+
scrollTop = (e.target as HTMLDivElement).scrollTop;
|
|
39
|
+
}
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<div
|
|
43
|
+
class="sdt-virtual-container"
|
|
44
|
+
style="height: {containerHeight}px; overflow-y: auto;"
|
|
45
|
+
onscroll={handleScroll}
|
|
46
|
+
bind:this={containerEl}
|
|
47
|
+
>
|
|
48
|
+
<div class="sdt-virtual-spacer" style="height: {totalHeight}px; position: relative;">
|
|
49
|
+
{#each visibleItems as { item, index, style } (index)}
|
|
50
|
+
{@render renderRow({ item, index, style })}
|
|
51
|
+
{/each}
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<style>
|
|
56
|
+
.sdt-virtual-container {
|
|
57
|
+
overflow-x: auto;
|
|
58
|
+
}
|
|
59
|
+
.sdt-virtual-spacer {
|
|
60
|
+
width: 100%;
|
|
61
|
+
}
|
|
62
|
+
</style>
|