@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
package/README.md
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# @beeblock/svelar-datatable
|
|
2
|
+
|
|
3
|
+
Full-featured DataTable plugin for [Svelar](https://svelar.dev) / SvelteKit 2 with Svelte 5.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Sorting** — single-click or multi-sort (Shift+click)
|
|
8
|
+
- **Searching** — global search with debounce
|
|
9
|
+
- **Pagination** — configurable per-page options, page navigation
|
|
10
|
+
- **Selection** — single, multi, range (Shift+click), select all
|
|
11
|
+
- **Column management** — visibility toggle, reorder, resize
|
|
12
|
+
- **Editing** — four modes: modal, bubble (popover), inline (double-click), excel (spreadsheet)
|
|
13
|
+
- **Export** — CSV, clipboard, print (Excel/PDF with optional deps)
|
|
14
|
+
- **Virtual scroll** — render 10,000+ rows with only visible DOM nodes
|
|
15
|
+
- **Row grouping** — group rows by any column value
|
|
16
|
+
- **Per-column filters** — programmatic column-level filtering
|
|
17
|
+
- **Custom cells** — Svelte 5 snippets for full cell rendering control
|
|
18
|
+
- **Row customization** — `rowClass`, `rowId` function, expandable detail rows
|
|
19
|
+
- **Server-side** — jQuery DataTables wire protocol compatible, GET or POST
|
|
20
|
+
- **State persistence** — save sort/filter/page/column state to localStorage
|
|
21
|
+
- **Tailwind CSS** — `classNames` prop for pure Tailwind customization of every element
|
|
22
|
+
- **CSS variables** — 12+ CSS custom properties for quick theming
|
|
23
|
+
- **Footer aggregation** — per-column footer with custom functions
|
|
24
|
+
- **Responsive** — horizontal scroll for small screens
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install @beeblock/svelar-datatable
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Peer dependencies: `@beeblock/svelar >= 0.4.0`, `svelte ^5.0.0`
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
```svelte
|
|
37
|
+
<script lang="ts">
|
|
38
|
+
import { DataTable } from '@beeblock/svelar-datatable/ui';
|
|
39
|
+
import type { ColumnDef } from '@beeblock/svelar-datatable';
|
|
40
|
+
|
|
41
|
+
const columns: ColumnDef[] = [
|
|
42
|
+
{ key: 'id', header: 'ID', type: 'number', sortable: true },
|
|
43
|
+
{ key: 'name', header: 'Name', sortable: true, searchable: true },
|
|
44
|
+
{ key: 'email', header: 'Email', sortable: true, searchable: true },
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
const data = [
|
|
48
|
+
{ id: 1, name: 'Alice', email: 'alice@example.com' },
|
|
49
|
+
{ id: 2, name: 'Bob', email: 'bob@example.com' },
|
|
50
|
+
];
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<DataTable {data} {columns} sortable searchable paginate perPage={10} />
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Server-Side
|
|
57
|
+
|
|
58
|
+
```svelte
|
|
59
|
+
<!-- Frontend -->
|
|
60
|
+
<DataTable
|
|
61
|
+
serverUrl="/api/datatable/users"
|
|
62
|
+
columns={columns}
|
|
63
|
+
sortable
|
|
64
|
+
searchable
|
|
65
|
+
paginate
|
|
66
|
+
perPage={25}
|
|
67
|
+
/>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
// src/routes/api/datatable/users/+server.ts
|
|
72
|
+
import { DataTableController } from '@beeblock/svelar-datatable/server';
|
|
73
|
+
import { User } from '$lib/models/User';
|
|
74
|
+
|
|
75
|
+
const dt = new DataTableController(User);
|
|
76
|
+
export const GET = dt.handle();
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The server controller handles pagination, sorting, searching, and filtering — compatible with the jQuery DataTables wire protocol.
|
|
80
|
+
|
|
81
|
+
## Editing
|
|
82
|
+
|
|
83
|
+
Four editor modes, configured via `editorMode`:
|
|
84
|
+
|
|
85
|
+
```svelte
|
|
86
|
+
<!-- Modal editor -->
|
|
87
|
+
<DataTable editorMode="modal" editorFields={fields} onEdit={save} onCreate={create} />
|
|
88
|
+
|
|
89
|
+
<!-- Bubble (popover anchored to row) -->
|
|
90
|
+
<DataTable editorMode="bubble" editorFields={fields} onEdit={save} />
|
|
91
|
+
|
|
92
|
+
<!-- Inline (double-click a cell) -->
|
|
93
|
+
<DataTable editorMode="inline" onCellEdit={saveCellEdit} />
|
|
94
|
+
|
|
95
|
+
<!-- Excel (spreadsheet navigation) -->
|
|
96
|
+
<DataTable editorMode="excel" onCellEdit={saveCellEdit} />
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Tailwind Customization
|
|
100
|
+
|
|
101
|
+
Style every element with Tailwind utility classes via the `classNames` prop:
|
|
102
|
+
|
|
103
|
+
```svelte
|
|
104
|
+
<DataTable
|
|
105
|
+
{data}
|
|
106
|
+
{columns}
|
|
107
|
+
striped={false}
|
|
108
|
+
hover={false}
|
|
109
|
+
classNames={{
|
|
110
|
+
container: '!bg-slate-900 !border-slate-800',
|
|
111
|
+
thead: '!bg-slate-950',
|
|
112
|
+
th: '!bg-slate-950 !text-slate-400 !tracking-widest',
|
|
113
|
+
tr: 'hover:!bg-slate-800',
|
|
114
|
+
td: '!text-slate-300 !border-b-slate-800',
|
|
115
|
+
pagination: '!border-t-slate-800 !text-slate-400 !bg-slate-900',
|
|
116
|
+
pageButton: '!bg-slate-800 !text-slate-400 !border-slate-700',
|
|
117
|
+
searchInput: '!bg-slate-800 !text-slate-200 !border-slate-700',
|
|
118
|
+
}}
|
|
119
|
+
/>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
All 39 keys from `DataTableClassNames` are wired: `container`, `toolbar`, `toolbarLeft`, `toolbarRight`, `searchInput`, `thead`, `th`, `tbody`, `tr`, `trSelected`, `trEven`, `td`, `tfoot`, `tf`, `pagination`, `paginationInfo`, `paginationControls`, `pageButton`, `pageButtonActive`, `perPageSelect`, `btn`, `btnCreate`, `btnEdit`, `btnDelete`, `editorModal`, `editorBackdrop`, `editorField`, `editorInput`, `editorLabel`, `loading`, `empty`, `error`.
|
|
123
|
+
|
|
124
|
+
## Imports
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
// UI component (Svelte source — not compiled)
|
|
128
|
+
import { DataTable } from '@beeblock/svelar-datatable/ui';
|
|
129
|
+
|
|
130
|
+
// Types
|
|
131
|
+
import type {
|
|
132
|
+
ColumnDef, EditorFieldDef, ButtonDef, DataTableClassNames,
|
|
133
|
+
DataTableConfig, DataTableState, ExportFormat,
|
|
134
|
+
} from '@beeblock/svelar-datatable';
|
|
135
|
+
|
|
136
|
+
// Stores
|
|
137
|
+
import { DataTableStore, ServerDataTableStore } from '@beeblock/svelar-datatable';
|
|
138
|
+
|
|
139
|
+
// Server controller (API routes)
|
|
140
|
+
import { DataTableController, DataTableService } from '@beeblock/svelar-datatable/server';
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Documentation
|
|
144
|
+
|
|
145
|
+
Full documentation with all props, callbacks, store API, and examples: [svelar.dev/docs/datatable](https://svelar.dev/docs/datatable)
|
|
146
|
+
|
|
147
|
+
## License
|
|
148
|
+
|
|
149
|
+
MIT
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DataTableConfig } from './types.js';
|
|
2
|
+
interface DatatablePluginConfig {
|
|
3
|
+
prefix?: string;
|
|
4
|
+
defaults?: Partial<DataTableConfig>;
|
|
5
|
+
}
|
|
6
|
+
export declare class SvelarDatatablePlugin {
|
|
7
|
+
private _config;
|
|
8
|
+
constructor(config?: DatatablePluginConfig);
|
|
9
|
+
get name(): string;
|
|
10
|
+
get config(): DatatablePluginConfig;
|
|
11
|
+
static defaults(): Partial<DataTableConfig>;
|
|
12
|
+
}
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ColumnDef } from '../types.js';
|
|
2
|
+
export declare function generateCSV<T>(data: T[], columns: ColumnDef<T>[]): string;
|
|
3
|
+
export declare function downloadCSV(content: string, filename?: string): void;
|
|
4
|
+
export declare function downloadBlob(blob: Blob, filename: string): void;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { generateCSV, downloadCSV, downloadBlob } from './csv.js';
|
|
2
|
+
export { copyToClipboard } from './clipboard.js';
|
|
3
|
+
export { printTable } from './print.js';
|
|
4
|
+
export { exportExcel } from './excel.js';
|
|
5
|
+
export { exportPdf } from './pdf.js';
|
|
6
|
+
export { ExportManager } from './ExportManager.js';
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { SvelarDatatablePlugin } from './SvelarDatatablePlugin.js';
|
|
2
|
+
export { DataTableStore } from './state/DataTableStore.js';
|
|
3
|
+
export { ServerDataTableStore } from './state/ServerDataTableStore.js';
|
|
4
|
+
export { ExportManager } from './export/ExportManager.js';
|
|
5
|
+
export type { ColumnDef, ColumnType, FilterOperator, SelectionMode, EditorMode, FieldType, ExportFormat, SortState, FilterState, PaginationState, DataTableRequest, DataTableResponse, EditorFieldDef, ButtonDef, DataTableConfig, DataTableClassNames, DataTableState, } from './types.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
var y={prefix:"/api",defaults:{perPage:15,perPageOptions:[10,15,25,50,100],searchDebounceMs:300,sortable:!0,searchable:!0,paginate:!0,selectable:"none",striped:!0,hover:!0,bordered:!1,compact:!1,responsive:!0,virtualScroll:!1,virtualRowHeight:48}},S=class{_config;constructor(t={}){this._config={...y,...t,defaults:{...y.defaults,...t.defaults}}}get name(){return"svelar-datatable"}get config(){return this._config}static defaults(){return{...y.defaults}}};var m=class{_state;_listeners=new Set;_columns=[];_rowIdFn;_stateSaveKey=null;_lastSelectedId=null;_paginate=!0;constructor(t){this._columns=t.columns,this._stateSaveKey=t.stateSaveKey??null,this._paginate=t.paginate!==!1,this._rowIdFn=typeof t.rowId=="function"?t.rowId:s=>s[t.rowId??"id"];let e=this._loadState();this._state={allRows:t.data??[],filteredRows:[],sortedRows:[],paginatedRows:[],sort:e?.sort??[],filters:e?.filters??[],globalSearch:e?.globalSearch??"",pagination:{page:e?.page??1,perPage:t.perPage??15,total:0,lastPage:1},selectedIds:new Set,columnVisibility:e?.columnVisibility??Object.fromEntries(t.columns.map(s=>[s.key,s.visible!==!1])),columnOrder:e?.columnOrder??t.columns.map(s=>s.key),loading:!1,error:null,editingRowId:null,editingColumn:null,editorMode:null,formData:{},validationErrors:{},draw:0,excelFocusedCell:null,excelEditingCell:null,excelEditValue:""},this._recompute()}subscribe(t){return this._listeners.add(t),()=>this._listeners.delete(t)}getState(){return this._state}_notify(){for(let t of this._listeners)t()}_saveState(){if(this._stateSaveKey)try{let t={sort:this._state.sort,filters:this._state.filters,globalSearch:this._state.globalSearch,page:this._state.pagination.page,columnVisibility:this._state.columnVisibility,columnOrder:this._state.columnOrder};localStorage.setItem(this._stateSaveKey,JSON.stringify(t))}catch{}}_loadState(){if(!this._stateSaveKey)return null;try{let t=localStorage.getItem(this._stateSaveKey);return t?JSON.parse(t):null}catch{return null}}setData(t){this._state={...this._state,allRows:t},this._recompute()}setSort(t){this._state={...this._state,sort:t,pagination:{...this._state.pagination,page:1}},this._recompute(),this._saveState()}toggleSort(t,e=!1){let s=this._state.sort.find(i=>i.column===t),a;s?s.direction==="asc"?a=e?this._state.sort.map(i=>i.column===t?{...i,direction:"desc"}:i):[{column:t,direction:"desc"}]:a=e?this._state.sort.filter(i=>i.column!==t):[]:a=e?[...this._state.sort,{column:t,direction:"asc"}]:[{column:t,direction:"asc"}],this.setSort(a)}setGlobalSearch(t){this._state={...this._state,globalSearch:t,pagination:{...this._state.pagination,page:1}},this._recompute(),this._saveState()}setFilters(t){this._state={...this._state,filters:t,pagination:{...this._state.pagination,page:1}},this._recompute(),this._saveState()}setColumnFilter(t,e,s="like"){let a=this._state.filters.filter(i=>i.column!==t);e!==""&&e!==null&&e!==void 0&&a.push({column:t,value:e,operator:s}),this.setFilters(a)}setPage(t){let e=this._state.pagination.lastPage,s=Math.max(1,Math.min(t,e));this._state={...this._state,pagination:{...this._state.pagination,page:s}},this._recompute(),this._saveState()}setPerPage(t){this._state={...this._state,pagination:{...this._state.pagination,perPage:t,page:1}},this._recompute(),this._saveState()}toggleColumnVisibility(t){let e={...this._state.columnVisibility};e[t]=!e[t],this._state={...this._state,columnVisibility:e},this._notify(),this._saveState()}setColumnVisibility(t){this._state={...this._state,columnVisibility:t},this._notify(),this._saveState()}reorderColumns(t){this._state={...this._state,columnOrder:t},this._notify(),this._saveState()}getRowId(t){return this._rowIdFn(t)}toggleSelect(t){let e=new Set(this._state.selectedIds);e.has(t)?e.delete(t):e.add(t),this._lastSelectedId=t,this._state={...this._state,selectedIds:e},this._notify()}getLastSelectedId(){return this._lastSelectedId}selectSingle(t){this._state={...this._state,selectedIds:new Set([t])},this._notify()}selectAll(){let t=this._state.filteredRows.map(e=>this.getRowId(e));this._state={...this._state,selectedIds:new Set(t)},this._notify()}deselectAll(){this._state={...this._state,selectedIds:new Set},this._notify()}selectRange(t,e){let s=this._state.sortedRows,a=s.findIndex(n=>this.getRowId(n)===t),i=s.findIndex(n=>this.getRowId(n)===e);if(a===-1||i===-1)return;let o=Math.min(a,i),u=Math.max(a,i),r=new Set(this._state.selectedIds);for(let n=o;n<=u;n++)r.add(this.getRowId(s[n]));this._state={...this._state,selectedIds:r},this._notify()}getSelectedRows(){return this._state.allRows.filter(t=>this._state.selectedIds.has(this.getRowId(t)))}openEditor(t,e,s){let a=t!==null?this._state.allRows.find(o=>this.getRowId(o)===t):null,i=a?{...a}:{};this._state={...this._state,editingRowId:t,editingColumn:e,editorMode:s,formData:i,validationErrors:{}},this._notify()}closeEditor(){this._state={...this._state,editingRowId:null,editingColumn:null,editorMode:null,formData:{},validationErrors:{}},this._notify()}setFormField(t,e){this._state={...this._state,formData:{...this._state.formData,[t]:e}},this._notify()}setValidationErrors(t){this._state={...this._state,validationErrors:t},this._notify()}setLoading(t){this._state={...this._state,loading:t},this._notify()}setError(t){this._state={...this._state,error:t},this._notify()}setServerResponse(t){this._state={...this._state,allRows:t.data,filteredRows:t.data,sortedRows:t.data,paginatedRows:t.data,loading:!1,draw:t.draw,pagination:{...this._state.pagination,total:t.recordsFiltered,lastPage:Math.ceil(t.recordsFiltered/this._state.pagination.perPage)||1}},this._notify()}resetState(){if(this._state={...this._state,sort:[],filters:[],globalSearch:"",pagination:{...this._state.pagination,page:1},selectedIds:new Set},this._recompute(),this._stateSaveKey)try{localStorage.removeItem(this._stateSaveKey)}catch{}}focusCell(t,e){this._state={...this._state,excelFocusedCell:{rowIndex:t,columnKey:e}},this._notify()}startCellEdit(){let t=this._state.excelFocusedCell;if(!t)return;let e=this._state.paginatedRows[t.rowIndex];if(!e)return;let s=this._columns.find(a=>a.key===t.columnKey);!s||s.editable===!1||(this._state={...this._state,excelEditingCell:{...t},excelEditValue:e[t.columnKey]!=null?String(e[t.columnKey]):""},this._notify())}setExcelEditValue(t){this._state={...this._state,excelEditValue:t}}commitCellEdit(){let t=this._state.excelEditingCell;if(!t)return null;let e=this._state.paginatedRows[t.rowIndex];if(!e)return this.cancelCellEdit(),null;let s=this._columns.find(o=>o.key===t.columnKey),a=e[t.columnKey],i=this._state.excelEditValue;return s?.type==="number"?i=i===""?null:Number(i):s?.type==="boolean"?i=i==="true"||i==="1":i===""&&(i=null),this._state={...this._state,excelEditingCell:null,excelEditValue:""},i===a||i===null&&a==null?(this._notify(),null):(e[t.columnKey]=i,this._notify(),{row:e,columnKey:t.columnKey,oldValue:a,newValue:i})}cancelCellEdit(){this._state={...this._state,excelEditingCell:null,excelEditValue:""},this._notify()}clearExcelFocus(){this._state={...this._state,excelFocusedCell:null,excelEditingCell:null,excelEditValue:""},this._notify()}getVisibleColumns(){return this._state.columnOrder.filter(t=>this._state.columnVisibility[t]!==!1).map(t=>this._columns.find(e=>e.key===t)).filter(Boolean)}navigateCell(t){let e=this._state.excelFocusedCell,s=this._state.paginatedRows,a=this.getVisibleColumns();if(a.length===0)return null;if(!e){let r=a.find(n=>n.editable!==!1);return r&&s.length>0&&this.focusCell(0,r.key),null}let{rowIndex:i,columnKey:o}=e,u=a.findIndex(r=>r.key===o);if(u===-1)return null;switch(t){case"left":{for(let r=u-1;r>=0;r--)if(a[r].editable!==!1)return this.focusCell(i,a[r].key),null;for(let r=a.length-1;r>=0;r--)if(a[r].editable!==!1)return i>0?(this.focusCell(i-1,a[r].key),null):"page-prev";return null}case"right":{for(let r=u+1;r<a.length;r++)if(a[r].editable!==!1)return this.focusCell(i,a[r].key),null;for(let r=0;r<a.length;r++)if(a[r].editable!==!1)return i<s.length-1?(this.focusCell(i+1,a[r].key),null):"page-next";return null}case"up":return i>0?(this.focusCell(i-1,o),null):"page-prev";case"down":return i<s.length-1?(this.focusCell(i+1,o),null):"page-next"}}_recompute(){let t=[...this._state.allRows];if(this._state.globalSearch){let n=this._state.globalSearch.toLowerCase(),d=this._columns.filter(l=>l.searchable!==!1);t=t.filter(l=>d.some(p=>{let h=l[p.key];return h!=null&&String(h).toLowerCase().includes(n)}))}for(let n of this._state.filters)t=t.filter(d=>{let l=d[n.column];switch(n.operator){case"=":return l==n.value;case"!=":return l!=n.value;case">":return l>n.value;case"<":return l<n.value;case">=":return l>=n.value;case"<=":return l<=n.value;case"like":return l!=null&&String(l).toLowerCase().includes(String(n.value).toLowerCase());case"not_like":return l==null||!String(l).toLowerCase().includes(String(n.value).toLowerCase());case"in":return Array.isArray(n.value)&&n.value.includes(l);case"between":return Array.isArray(n.value)&&l>=n.value[0]&&l<=n.value[1];case"null":return l==null;case"not_null":return l!=null;default:return!0}});let e=t;this._state.sort.length>0&&(t=[...t].sort((n,d)=>{for(let l of this._state.sort){let p=this._columns.find(k=>k.key===l.column),h=n[l.column],g=d[l.column],f=0;if(h==null?f=-1:g==null?f=1:p?.type==="number"?f=Number(h)-Number(g):p?.type==="date"?f=new Date(h).getTime()-new Date(g).getTime():p?.type==="boolean"?f=(h?1:0)-(g?1:0):f=String(h).localeCompare(String(g)),f!==0)return l.direction==="desc"?-f:f}return 0}));let s=t,a=e.length,i=this._state.pagination.perPage,o,u,r;if(this._paginate){r=Math.ceil(a/i)||1,u=Math.min(this._state.pagination.page,r);let n=(u-1)*i;o=s.slice(n,n+i)}else o=s,u=1,r=1;this._state={...this._state,filteredRows:e,sortedRows:s,paginatedRows:o,pagination:{page:u,perPage:i,total:a,lastPage:r}},this._notify()}};function I(c="XSRF-TOKEN"){if(typeof document>"u")return null;let t=c.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),e=document.cookie.match(new RegExp(`(?:^|;\\s*)${t}=([^;]*)`));return e?decodeURIComponent(e[1]):null}var v=class extends m{_serverUrl;_serverMethod;_drawCounter=0;_abortController=null;_fetchDebounceTimer=null;_serverColumns;_csrfCookieName;_csrfHeaderName;constructor(t){super(t),this._serverUrl=t.serverUrl,this._serverMethod=t.serverMethod??"GET",this._csrfCookieName=t.csrfCookieName??"XSRF-TOKEN",this._csrfHeaderName=t.csrfHeaderName??"X-CSRF-Token",this._serverColumns=t.columns.map(e=>({data:e.key,name:e.key,searchable:e.searchable!==!1,orderable:e.sortable!==!1}))}_buildHeaders(t={}){let e={...t},s=I(this._csrfCookieName);return s&&(e[this._csrfHeaderName]=s),e}setSort(t){let e=this.getState();this._state={...e,sort:t,pagination:{...e.pagination,page:1}},this._debouncedFetch()}setGlobalSearch(t){let e=this.getState();this._state={...e,globalSearch:t,pagination:{...e.pagination,page:1}},this._debouncedFetch()}setFilters(t){let e=this.getState();this._state={...e,filters:t,pagination:{...e.pagination,page:1}},this._debouncedFetch()}setPage(t){let e=this.getState();this._state={...e,pagination:{...e.pagination,page:t}},this._fetchFromServer()}setPerPage(t){let e=this.getState();this._state={...e,pagination:{...e.pagination,perPage:t,page:1}},this._fetchFromServer()}_debouncedFetch(){this._fetchDebounceTimer&&clearTimeout(this._fetchDebounceTimer),this._fetchDebounceTimer=setTimeout(()=>this._fetchFromServer(),300)}async _fetchFromServer(){this._abortController&&this._abortController.abort(),this._abortController=new AbortController;let t=this.getState();this.setLoading(!0),this.setError(null);let e=++this._drawCounter,s={draw:e,start:(t.pagination.page-1)*t.pagination.perPage,length:t.pagination.perPage,search:{value:t.globalSearch,regex:!1},order:t.sort.map(a=>({column:this._serverColumns.findIndex(i=>i.data===a.column),dir:a.direction})),columns:this._serverColumns.map(a=>({...a,search:{value:t.filters.find(i=>i.column===a.data)?.value??"",regex:!1}}))};try{let a;if(this._serverMethod==="POST")a=await fetch(this._serverUrl,{method:"POST",headers:this._buildHeaders({"Content-Type":"application/json"}),body:JSON.stringify(s),signal:this._abortController.signal});else{let o=new URLSearchParams;o.set("draw",String(s.draw)),o.set("start",String(s.start)),o.set("length",String(s.length)),o.set("search[value]",s.search.value),o.set("search[regex]",String(s.search.regex)),s.order.forEach((r,n)=>{o.set(`order[${n}][column]`,String(r.column)),o.set(`order[${n}][dir]`,r.dir)}),s.columns.forEach((r,n)=>{o.set(`columns[${n}][data]`,r.data),o.set(`columns[${n}][name]`,r.name),o.set(`columns[${n}][searchable]`,String(r.searchable)),o.set(`columns[${n}][orderable]`,String(r.orderable)),o.set(`columns[${n}][search][value]`,r.search.value),o.set(`columns[${n}][search][regex]`,String(r.search.regex))});let u=`${this._serverUrl}?${o.toString()}`;a=await fetch(u,{signal:this._abortController.signal})}if(!a.ok)throw new Error(`Server responded with ${a.status}`);let i=await a.json();i.draw===e&&this.setServerResponse(i)}catch(a){if(a.name==="AbortError")return;this.setLoading(!1),this.setError(a.message??"Failed to fetch data")}}async initialFetch(){await this._fetchFromServer()}destroy(){this._abortController&&this._abortController.abort(),this._fetchDebounceTimer&&clearTimeout(this._fetchDebounceTimer)}};function x(c){if(c==null)return"";let t=String(c);return t.includes(",")||t.includes('"')||t.includes(`
|
|
2
|
+
`)||t.includes("\r")?'"'+t.replace(/"/g,'""')+'"':t}function T(c,t){let e=t.filter(i=>i.visible!==!1),s=e.map(i=>x(i.header)).join(","),a=c.map(i=>e.map(o=>x(i[o.key])).join(","));return"\uFEFF"+[s,...a].join(`\r
|
|
3
|
+
`)}function D(c,t="export.csv"){let e=new Blob([c],{type:"text/csv;charset=utf-8;"});w(e,t)}function w(c,t){let e=URL.createObjectURL(c),s=document.createElement("a");s.href=e,s.download=t,document.body.appendChild(s),s.click(),document.body.removeChild(s),URL.revokeObjectURL(e)}async function R(c,t){let e=t.filter(o=>o.visible!==!1),s=e.map(o=>o.header).join(" "),a=c.map(o=>e.map(u=>{let r=o[u.key];return r==null?"":String(r)}).join(" ")),i=[s,...a].join(`
|
|
4
|
+
`);await navigator.clipboard.writeText(i)}function b(c,t,e){let s=t.filter(r=>r.visible!==!1),a=s.map(r=>`<th style="border:1px solid #ddd;padding:8px 12px;text-align:left;background:#f5f5f5;font-weight:600;">${_(r.header)}</th>`).join(""),i=c.map(r=>`<tr>${s.map(d=>{let l=r[d.key];return`<td style="border:1px solid #ddd;padding:8px 12px;">${_(l==null?"":String(l))}</td>`}).join("")}</tr>`).join(""),o=`<!DOCTYPE html>
|
|
5
|
+
<html><head>
|
|
6
|
+
<title>${_(e??"Data Export")}</title>
|
|
7
|
+
<style>
|
|
8
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; margin: 20px; }
|
|
9
|
+
table { border-collapse: collapse; width: 100%; font-size: 13px; }
|
|
10
|
+
h1 { font-size: 18px; margin-bottom: 16px; }
|
|
11
|
+
@media print { h1 { margin-top: 0; } }
|
|
12
|
+
</style>
|
|
13
|
+
</head><body>
|
|
14
|
+
${e?`<h1>${_(e)}</h1>`:""}
|
|
15
|
+
<table>
|
|
16
|
+
<thead><tr>${a}</tr></thead>
|
|
17
|
+
<tbody>${i}</tbody>
|
|
18
|
+
</table>
|
|
19
|
+
</body></html>`,u=window.open("","_blank");u&&(u.document.write(o),u.document.close(),u.focus(),setTimeout(()=>u.print(),250))}function _(c){return c.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}async function E(c,t,e="export.xlsx"){let s;try{s=await import("exceljs")}catch{throw new Error("exceljs is required for Excel export. Install it: npm install exceljs")}let a=new s.Workbook,i=a.addWorksheet("Data"),o=t.filter(d=>d.visible!==!1);i.columns=o.map(d=>({header:d.header,key:d.key,width:20}));let u=i.getRow(1);u.font={bold:!0},u.fill={type:"pattern",pattern:"solid",fgColor:{argb:"FFF0F0F0"}};for(let d of c){let l={};for(let p of o)l[p.key]=d[p.key]??"";i.addRow(l)}let r=await a.xlsx.writeBuffer(),n=new Blob([r],{type:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"});w(n,e)}function F(c,t,e="export.pdf"){b(c,t,e.replace(".pdf",""))}var C=class{async export(t,e,s,a){switch(t){case"csv":{let i=T(e,s);D(i,a??"export.csv");break}case"excel":await E(e,s,a??"export.xlsx");break;case"pdf":F(e,s,a??"export.pdf");break;case"clipboard":await R(e,s);break;case"print":b(e,s);break}}};export{m as DataTableStore,C as ExportManager,v as ServerDataTableStore,S as SvelarDatatablePlugin};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { DataTableRequest, DataTableResponse } from '../types.js';
|
|
2
|
+
export interface DataTableServiceOptions {
|
|
3
|
+
searchable?: string[];
|
|
4
|
+
orderable?: string[];
|
|
5
|
+
baseQuery?: (query: any) => any;
|
|
6
|
+
scopes?: Record<string, (query: any) => any>;
|
|
7
|
+
computedColumns?: Record<string, string>;
|
|
8
|
+
}
|
|
9
|
+
export declare class DataTableService<T = any> {
|
|
10
|
+
private _model;
|
|
11
|
+
private _searchable;
|
|
12
|
+
private _orderable;
|
|
13
|
+
private _baseQueryFn;
|
|
14
|
+
private _scopes;
|
|
15
|
+
private _computedColumns;
|
|
16
|
+
private _activeScopes;
|
|
17
|
+
constructor(model: any, options?: DataTableServiceOptions);
|
|
18
|
+
searchable(columns: string[]): this;
|
|
19
|
+
orderable(columns: string[]): this;
|
|
20
|
+
setBaseQuery(fn: (query: any) => any): this;
|
|
21
|
+
addScope(name: string, fn: (query: any) => any): this;
|
|
22
|
+
applyScope(name: string): this;
|
|
23
|
+
addComputedColumn(name: string, sqlExpr: string): this;
|
|
24
|
+
process(request: DataTableRequest): Promise<DataTableResponse<T>>;
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var h=class{_model;_searchable=[];_orderable=[];_baseQueryFn=null;_scopes={};_computedColumns={};_activeScopes=[];constructor(e,t){this._model=e,t?.searchable&&(this._searchable=t.searchable),t?.orderable&&(this._orderable=t.orderable),t?.baseQuery&&(this._baseQueryFn=t.baseQuery),t?.scopes&&(this._scopes=t.scopes),t?.computedColumns&&(this._computedColumns=t.computedColumns)}searchable(e){return this._searchable=e,this}orderable(e){return this._orderable=e,this}setBaseQuery(e){return this._baseQueryFn=e,this}addScope(e,t){return this._scopes[e]=t,this}applyScope(e){return this._activeScopes.push(e),this}addComputedColumn(e,t){return this._computedColumns[e]=t,this}async process(e){try{let t=this._model.query();this._baseQueryFn&&this._baseQueryFn(t);let u=await t.count(),a=this._model.query();this._baseQueryFn&&this._baseQueryFn(a);for(let r of this._activeScopes){let o=this._scopes[r];o&&o(a)}for(let[r,o]of Object.entries(this._computedColumns))a=a.selectRaw(`(${o}) as ${r}`);if(e.search.value){let r=e.search.value,o=this._searchable.length>0?this._searchable:e.columns.filter(i=>i.searchable).map(i=>i.data);o.length>0&&(a=a.whereNested(i=>{for(let l=0;l<o.length;l++){let m=o[l];l===0?i.where(m,"LIKE",`%${r}%`):i.orWhere(m,"LIKE",`%${r}%`)}}))}for(let r of e.columns)r.search.value&&r.searchable&&(a=a.where(r.data,"LIKE",`%${r.search.value}%`));let c=await a.clone().count();for(let r of e.order){let o=e.columns[r.column];o&&(this._orderable.length>0?this._orderable:e.columns.filter(l=>l.orderable).map(l=>l.data)).includes(o.data)&&(a=a.orderBy(o.data,r.dir))}a=a.offset(e.start).limit(e.length);let d=await a.get();return{draw:e.draw,recordsTotal:u,recordsFiltered:c,data:d}}catch(t){return{draw:e.draw,recordsTotal:0,recordsFiltered:0,data:[],error:t.message??"Server error"}}}};function p(s){let e=parseInt(s.get("draw")??"1",10),t=parseInt(s.get("start")??"0",10),u=parseInt(s.get("length")??"15",10),a={value:s.get("search[value]")??"",regex:s.get("search[regex]")==="true"},n=[],c=0;for(;s.has(`order[${c}][column]`);)n.push({column:parseInt(s.get(`order[${c}][column]`)??"0",10),dir:s.get(`order[${c}][dir]`)??"asc"}),c++;let d=[],r=0;for(;s.has(`columns[${r}][data]`);)d.push({data:s.get(`columns[${r}][data]`)??"",name:s.get(`columns[${r}][name]`)??"",searchable:s.get(`columns[${r}][searchable]`)!=="false",orderable:s.get(`columns[${r}][orderable]`)!=="false",search:{value:s.get(`columns[${r}][search][value]`)??"",regex:s.get(`columns[${r}][search][regex]`)==="true"}}),r++;return{draw:e,start:t,length:u,search:a,order:n,columns:d}}async function y(s){return await s.json()}var b=class{static async handle(e,t,u){try{let a=new h(t,u),n;e.request.method==="POST"?n=await y(e.request):n=p(e.url.searchParams);let c=await a.process(n);return new Response(JSON.stringify(c),{status:200,headers:{"Content-Type":"application/json"}})}catch(a){let n={draw:0,recordsTotal:0,recordsFiltered:0,data:[],error:a.message??"Internal server error"};return new Response(JSON.stringify(n),{status:500,headers:{"Content-Type":"application/json"}})}}};export{b as DataTableController,h as DataTableService,p as parseDataTableRequest,y as parseDataTableRequestFromBody};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { ColumnDef, DataTableConfig, DataTableState, FilterState, SortState } from '../types.js';
|
|
2
|
+
type Listener = () => void;
|
|
3
|
+
export declare class DataTableStore<T = any> {
|
|
4
|
+
private _state;
|
|
5
|
+
private _listeners;
|
|
6
|
+
private _columns;
|
|
7
|
+
private _rowIdFn;
|
|
8
|
+
private _stateSaveKey;
|
|
9
|
+
private _lastSelectedId;
|
|
10
|
+
private _paginate;
|
|
11
|
+
constructor(config: DataTableConfig<T>);
|
|
12
|
+
subscribe(listener: Listener): () => void;
|
|
13
|
+
getState(): DataTableState<T>;
|
|
14
|
+
private _notify;
|
|
15
|
+
private _saveState;
|
|
16
|
+
private _loadState;
|
|
17
|
+
setData(rows: T[]): void;
|
|
18
|
+
setSort(sort: SortState[]): void;
|
|
19
|
+
toggleSort(column: string, multiSort?: boolean): void;
|
|
20
|
+
setGlobalSearch(search: string): void;
|
|
21
|
+
setFilters(filters: FilterState[]): void;
|
|
22
|
+
setColumnFilter(column: string, value: any, operator?: FilterState['operator']): void;
|
|
23
|
+
setPage(page: number): void;
|
|
24
|
+
setPerPage(perPage: number): void;
|
|
25
|
+
toggleColumnVisibility(column: string): void;
|
|
26
|
+
setColumnVisibility(visibility: Record<string, boolean>): void;
|
|
27
|
+
reorderColumns(columnOrder: string[]): void;
|
|
28
|
+
getRowId(row: T): string | number;
|
|
29
|
+
toggleSelect(rowId: string | number): void;
|
|
30
|
+
getLastSelectedId(): string | number | null;
|
|
31
|
+
selectSingle(rowId: string | number): void;
|
|
32
|
+
selectAll(): void;
|
|
33
|
+
deselectAll(): void;
|
|
34
|
+
selectRange(fromId: string | number, toId: string | number): void;
|
|
35
|
+
getSelectedRows(): T[];
|
|
36
|
+
openEditor(rowId: string | number | null, column: string | null, mode: 'inline' | 'bubble' | 'modal'): void;
|
|
37
|
+
closeEditor(): void;
|
|
38
|
+
setFormField(name: string, value: any): void;
|
|
39
|
+
setValidationErrors(errors: Record<string, string>): void;
|
|
40
|
+
setLoading(loading: boolean): void;
|
|
41
|
+
setError(error: string | null): void;
|
|
42
|
+
setServerResponse(response: {
|
|
43
|
+
data: T[];
|
|
44
|
+
recordsTotal: number;
|
|
45
|
+
recordsFiltered: number;
|
|
46
|
+
draw: number;
|
|
47
|
+
}): void;
|
|
48
|
+
resetState(): void;
|
|
49
|
+
focusCell(rowIndex: number, columnKey: string): void;
|
|
50
|
+
startCellEdit(): void;
|
|
51
|
+
setExcelEditValue(value: string): void;
|
|
52
|
+
commitCellEdit(): {
|
|
53
|
+
row: any;
|
|
54
|
+
columnKey: string;
|
|
55
|
+
oldValue: any;
|
|
56
|
+
newValue: any;
|
|
57
|
+
} | null;
|
|
58
|
+
cancelCellEdit(): void;
|
|
59
|
+
clearExcelFocus(): void;
|
|
60
|
+
getVisibleColumns(): ColumnDef[];
|
|
61
|
+
navigateCell(direction: 'up' | 'down' | 'left' | 'right'): 'page-next' | 'page-prev' | null;
|
|
62
|
+
private _recompute;
|
|
63
|
+
}
|
|
64
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { DataTableConfig, FilterState, SortState } from '../types.js';
|
|
2
|
+
import { DataTableStore } from './DataTableStore.js';
|
|
3
|
+
export declare class ServerDataTableStore<T = any> extends DataTableStore<T> {
|
|
4
|
+
private _serverUrl;
|
|
5
|
+
private _serverMethod;
|
|
6
|
+
private _drawCounter;
|
|
7
|
+
private _abortController;
|
|
8
|
+
private _fetchDebounceTimer;
|
|
9
|
+
private _serverColumns;
|
|
10
|
+
private _csrfCookieName;
|
|
11
|
+
private _csrfHeaderName;
|
|
12
|
+
constructor(config: DataTableConfig<T>);
|
|
13
|
+
private _buildHeaders;
|
|
14
|
+
setSort(sort: SortState[]): void;
|
|
15
|
+
setGlobalSearch(search: string): void;
|
|
16
|
+
setFilters(filters: FilterState[]): void;
|
|
17
|
+
setPage(page: number): void;
|
|
18
|
+
setPerPage(perPage: number): void;
|
|
19
|
+
private _debouncedFetch;
|
|
20
|
+
_fetchFromServer(): Promise<void>;
|
|
21
|
+
initialFetch(): Promise<void>;
|
|
22
|
+
destroy(): void;
|
|
23
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import type { Component } from 'svelte';
|
|
2
|
+
export type ColumnType = 'string' | 'number' | 'date' | 'boolean' | 'html' | 'custom';
|
|
3
|
+
export type FilterOperator = '=' | '!=' | '>' | '<' | '>=' | '<=' | 'like' | 'not_like' | 'in' | 'between' | 'null' | 'not_null';
|
|
4
|
+
export type SelectionMode = 'none' | 'single' | 'multi';
|
|
5
|
+
export type EditorMode = 'inline' | 'bubble' | 'modal' | 'excel';
|
|
6
|
+
export type FieldType = 'text' | 'textarea' | 'number' | 'select' | 'multi-select' | 'checkbox' | 'radio' | 'date' | 'datetime' | 'upload' | 'hidden' | 'readonly';
|
|
7
|
+
export type ExportFormat = 'csv' | 'excel' | 'pdf' | 'clipboard' | 'print';
|
|
8
|
+
export interface ColumnDef<T = any> {
|
|
9
|
+
key: string;
|
|
10
|
+
header: string;
|
|
11
|
+
type?: ColumnType;
|
|
12
|
+
sortable?: boolean;
|
|
13
|
+
searchable?: boolean;
|
|
14
|
+
filterable?: boolean;
|
|
15
|
+
visible?: boolean;
|
|
16
|
+
width?: string;
|
|
17
|
+
minWidth?: string;
|
|
18
|
+
maxWidth?: string;
|
|
19
|
+
className?: string;
|
|
20
|
+
headerClassName?: string;
|
|
21
|
+
orderable?: boolean;
|
|
22
|
+
defaultSort?: 'asc' | 'desc';
|
|
23
|
+
footer?: string | ((rows: T[]) => string | number);
|
|
24
|
+
editable?: boolean;
|
|
25
|
+
editorField?: EditorFieldDef;
|
|
26
|
+
}
|
|
27
|
+
export interface SortState {
|
|
28
|
+
column: string;
|
|
29
|
+
direction: 'asc' | 'desc';
|
|
30
|
+
}
|
|
31
|
+
export interface FilterState {
|
|
32
|
+
column: string;
|
|
33
|
+
value: any;
|
|
34
|
+
operator: FilterOperator;
|
|
35
|
+
}
|
|
36
|
+
export interface PaginationState {
|
|
37
|
+
page: number;
|
|
38
|
+
perPage: number;
|
|
39
|
+
total: number;
|
|
40
|
+
lastPage: number;
|
|
41
|
+
}
|
|
42
|
+
export interface DataTableRequest {
|
|
43
|
+
draw: number;
|
|
44
|
+
start: number;
|
|
45
|
+
length: number;
|
|
46
|
+
search: {
|
|
47
|
+
value: string;
|
|
48
|
+
regex: boolean;
|
|
49
|
+
};
|
|
50
|
+
order: {
|
|
51
|
+
column: number;
|
|
52
|
+
dir: 'asc' | 'desc';
|
|
53
|
+
}[];
|
|
54
|
+
columns: {
|
|
55
|
+
data: string;
|
|
56
|
+
name: string;
|
|
57
|
+
searchable: boolean;
|
|
58
|
+
orderable: boolean;
|
|
59
|
+
search: {
|
|
60
|
+
value: string;
|
|
61
|
+
regex: boolean;
|
|
62
|
+
};
|
|
63
|
+
}[];
|
|
64
|
+
}
|
|
65
|
+
export interface DataTableResponse<T = any> {
|
|
66
|
+
draw: number;
|
|
67
|
+
recordsTotal: number;
|
|
68
|
+
recordsFiltered: number;
|
|
69
|
+
data: T[];
|
|
70
|
+
error?: string;
|
|
71
|
+
}
|
|
72
|
+
export interface EditorFieldDef {
|
|
73
|
+
name: string;
|
|
74
|
+
type: FieldType;
|
|
75
|
+
label: string;
|
|
76
|
+
placeholder?: string;
|
|
77
|
+
options?: {
|
|
78
|
+
label: string;
|
|
79
|
+
value: any;
|
|
80
|
+
}[];
|
|
81
|
+
multiple?: boolean;
|
|
82
|
+
required?: boolean;
|
|
83
|
+
disabled?: boolean;
|
|
84
|
+
className?: string;
|
|
85
|
+
dependsOn?: string;
|
|
86
|
+
dependsOnValue?: any;
|
|
87
|
+
showWhen?: (formData: Record<string, any>) => boolean;
|
|
88
|
+
defaultValue?: any;
|
|
89
|
+
}
|
|
90
|
+
export interface ButtonDef {
|
|
91
|
+
key: string;
|
|
92
|
+
label: string;
|
|
93
|
+
icon?: Component<any>;
|
|
94
|
+
action?: string | ((selectedRows: any[], allData: any[]) => void | Promise<void>);
|
|
95
|
+
variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost';
|
|
96
|
+
disabled?: boolean | ((selectedRows: any[]) => boolean);
|
|
97
|
+
collection?: ButtonDef[];
|
|
98
|
+
}
|
|
99
|
+
export interface DataTableClassNames {
|
|
100
|
+
container?: string;
|
|
101
|
+
toolbar?: string;
|
|
102
|
+
toolbarLeft?: string;
|
|
103
|
+
toolbarRight?: string;
|
|
104
|
+
searchInput?: string;
|
|
105
|
+
table?: string;
|
|
106
|
+
thead?: string;
|
|
107
|
+
th?: string;
|
|
108
|
+
thSortable?: string;
|
|
109
|
+
tbody?: string;
|
|
110
|
+
tr?: string;
|
|
111
|
+
trSelected?: string;
|
|
112
|
+
trEven?: string;
|
|
113
|
+
td?: string;
|
|
114
|
+
tfoot?: string;
|
|
115
|
+
tf?: string;
|
|
116
|
+
pagination?: string;
|
|
117
|
+
paginationInfo?: string;
|
|
118
|
+
paginationControls?: string;
|
|
119
|
+
pageButton?: string;
|
|
120
|
+
pageButtonActive?: string;
|
|
121
|
+
perPageSelect?: string;
|
|
122
|
+
btn?: string;
|
|
123
|
+
btnCreate?: string;
|
|
124
|
+
btnEdit?: string;
|
|
125
|
+
btnDelete?: string;
|
|
126
|
+
editorModal?: string;
|
|
127
|
+
editorBackdrop?: string;
|
|
128
|
+
editorField?: string;
|
|
129
|
+
editorInput?: string;
|
|
130
|
+
editorLabel?: string;
|
|
131
|
+
loading?: string;
|
|
132
|
+
empty?: string;
|
|
133
|
+
error?: string;
|
|
134
|
+
}
|
|
135
|
+
export interface DataTableConfig<T = any> {
|
|
136
|
+
data?: T[];
|
|
137
|
+
serverUrl?: string;
|
|
138
|
+
serverMethod?: 'GET' | 'POST';
|
|
139
|
+
csrfCookieName?: string;
|
|
140
|
+
csrfHeaderName?: string;
|
|
141
|
+
columns: ColumnDef<T>[];
|
|
142
|
+
sortable?: boolean;
|
|
143
|
+
searchable?: boolean;
|
|
144
|
+
paginate?: boolean;
|
|
145
|
+
selectable?: SelectionMode;
|
|
146
|
+
perPage?: number;
|
|
147
|
+
perPageOptions?: number[];
|
|
148
|
+
searchDebounceMs?: number;
|
|
149
|
+
stateSaveKey?: string;
|
|
150
|
+
rowId?: string | ((row: T) => string | number);
|
|
151
|
+
rowClass?: string | ((row: T, index: number) => string);
|
|
152
|
+
buttons?: (ButtonDef | ExportFormat)[];
|
|
153
|
+
editorMode?: EditorMode;
|
|
154
|
+
editorFields?: EditorFieldDef[];
|
|
155
|
+
onSort?: (sort: SortState[]) => void;
|
|
156
|
+
onFilter?: (filters: FilterState[]) => void;
|
|
157
|
+
onPageChange?: (page: number, perPage: number) => void;
|
|
158
|
+
onSelect?: (selectedRows: T[]) => void;
|
|
159
|
+
onRowClick?: (row: T, event: MouseEvent) => void;
|
|
160
|
+
onEdit?: (row: T, data: Record<string, any>) => void | Promise<void>;
|
|
161
|
+
onCellEdit?: (row: T, columnKey: string, newValue: any, oldValue: any) => void | Promise<void>;
|
|
162
|
+
onCreate?: (data: Record<string, any>) => void | Promise<void>;
|
|
163
|
+
onDelete?: (rows: T[]) => void | Promise<void>;
|
|
164
|
+
virtualScroll?: boolean;
|
|
165
|
+
virtualRowHeight?: number;
|
|
166
|
+
responsive?: boolean;
|
|
167
|
+
groupBy?: string;
|
|
168
|
+
expandable?: boolean;
|
|
169
|
+
emptyText?: string;
|
|
170
|
+
loadingText?: string;
|
|
171
|
+
className?: string;
|
|
172
|
+
compact?: boolean;
|
|
173
|
+
striped?: boolean;
|
|
174
|
+
hover?: boolean;
|
|
175
|
+
bordered?: boolean;
|
|
176
|
+
classNames?: DataTableClassNames;
|
|
177
|
+
unstyled?: boolean;
|
|
178
|
+
}
|
|
179
|
+
export interface DataTableState<T = any> {
|
|
180
|
+
allRows: T[];
|
|
181
|
+
filteredRows: T[];
|
|
182
|
+
sortedRows: T[];
|
|
183
|
+
paginatedRows: T[];
|
|
184
|
+
sort: SortState[];
|
|
185
|
+
filters: FilterState[];
|
|
186
|
+
globalSearch: string;
|
|
187
|
+
pagination: PaginationState;
|
|
188
|
+
selectedIds: Set<string | number>;
|
|
189
|
+
columnVisibility: Record<string, boolean>;
|
|
190
|
+
columnOrder: string[];
|
|
191
|
+
loading: boolean;
|
|
192
|
+
error: string | null;
|
|
193
|
+
editingRowId: string | number | null;
|
|
194
|
+
editingColumn: string | null;
|
|
195
|
+
editorMode: EditorMode | null;
|
|
196
|
+
formData: Record<string, any>;
|
|
197
|
+
validationErrors: Record<string, string>;
|
|
198
|
+
draw: number;
|
|
199
|
+
excelFocusedCell: {
|
|
200
|
+
rowIndex: number;
|
|
201
|
+
columnKey: string;
|
|
202
|
+
} | null;
|
|
203
|
+
excelEditingCell: {
|
|
204
|
+
rowIndex: number;
|
|
205
|
+
columnKey: string;
|
|
206
|
+
} | null;
|
|
207
|
+
excelEditValue: string;
|
|
208
|
+
}
|
package/dist/types.js
ADDED
|
File without changes
|
|
@@ -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';
|