vs-datatable 0.1.0 → 0.2.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 +734 -2
- package/dist/index.css +1 -1
- package/dist/vs-datatable.es.js +334 -340
- package/dist/vs-datatable.umd.js +2 -21
- package/package.json +2 -5
- package/src/styles/base.scss +416 -0
- package/src/styles/bootstrap.scss +6 -6
- package/src/styles/index.scss +2 -2
- package/src/styles/themes.scss +215 -0
- package/src/styles/vs-datatable.scss +0 -40
package/README.md
CHANGED
|
@@ -1,2 +1,734 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
1
|
+
# VsDataTable
|
|
2
|
+
|
|
3
|
+
A lightweight, feature-rich Vue 3 data table component with sorting, pagination, search, and row selection capabilities. **Completely library-independent** with extensive customization options and zero external dependencies.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔍 **Search & Filter** - Built-in search functionality with customizable search input
|
|
8
|
+
- 📊 **Sorting** - Multi-column sorting with visual indicators and priority support
|
|
9
|
+
- 📄 **Pagination** - Server-side and client-side pagination with customizable controls
|
|
10
|
+
- ✅ **Row Selection** - Single and multi-row selection with checkbox controls
|
|
11
|
+
- 🎨 **Highly Customizable** - Extensive CSS variables, themes, and slot support
|
|
12
|
+
- 📱 **Responsive** - Mobile-friendly design with no external dependencies
|
|
13
|
+
- 🚀 **Performance** - Optimized for large datasets with server-side support
|
|
14
|
+
- 🎯 **TypeScript** - Full TypeScript support with type definitions
|
|
15
|
+
- 🎭 **Zero Dependencies** - No Bootstrap, FontAwesome, or other external libraries
|
|
16
|
+
- 🎨 **Theme System** - Built-in themes and easy customization via CSS variables
|
|
17
|
+
|
|
18
|
+
## Key Features
|
|
19
|
+
|
|
20
|
+
### 🎭 **Zero Dependencies**
|
|
21
|
+
- No Bootstrap, FontAwesome, or other external libraries
|
|
22
|
+
- Completely self-contained with custom CSS
|
|
23
|
+
- Smaller bundle size and faster loading
|
|
24
|
+
|
|
25
|
+
### 🎨 **Advanced Customization**
|
|
26
|
+
- CSS custom properties for easy theming
|
|
27
|
+
- Built-in theme system with multiple themes
|
|
28
|
+
- Component-level CSS class customization
|
|
29
|
+
- Flexible design system
|
|
30
|
+
|
|
31
|
+
### 🚀 **Enhanced Performance**
|
|
32
|
+
- Optimized rendering with better key management
|
|
33
|
+
- Improved sorting and pagination
|
|
34
|
+
- Better memory management
|
|
35
|
+
- Faster initial load
|
|
36
|
+
|
|
37
|
+
### 🛠️ **Developer Experience**
|
|
38
|
+
- Better TypeScript support
|
|
39
|
+
- More intuitive prop names
|
|
40
|
+
- Enhanced slot system
|
|
41
|
+
- Comprehensive documentation
|
|
42
|
+
|
|
43
|
+
### 🔄 **Flexible Sorting**
|
|
44
|
+
- Client-side and server-side sorting support
|
|
45
|
+
- Multi-column sorting with priority
|
|
46
|
+
- Visual sort indicators with SVG icons
|
|
47
|
+
- v-model:sort support for reactive sorting
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm install vs-datatable
|
|
53
|
+
# or
|
|
54
|
+
yarn add vs-datatable
|
|
55
|
+
# or
|
|
56
|
+
pnpm add vs-datatable
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Quick Start
|
|
60
|
+
|
|
61
|
+
### Basic Usage
|
|
62
|
+
|
|
63
|
+
```vue
|
|
64
|
+
<template>
|
|
65
|
+
<VsDataTable
|
|
66
|
+
:columns="columns"
|
|
67
|
+
:rows="data"
|
|
68
|
+
:loading="loading"
|
|
69
|
+
@row-click="handleRowClick"
|
|
70
|
+
/>
|
|
71
|
+
</template>
|
|
72
|
+
|
|
73
|
+
<script setup lang="ts">
|
|
74
|
+
import { VsDataTable } from 'vs-datatable'
|
|
75
|
+
|
|
76
|
+
const columns = [
|
|
77
|
+
{ label: 'Name', field: 'name', sortable: true },
|
|
78
|
+
{ label: 'Email', field: 'email', sortable: true },
|
|
79
|
+
{ label: 'Role', field: 'role', width: '20%' },
|
|
80
|
+
{ label: 'Status', field: 'status', sortable: true }
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
const data = [
|
|
84
|
+
{ id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin', status: 'Active' },
|
|
85
|
+
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User', status: 'Inactive' }
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
const loading = ref(false)
|
|
89
|
+
|
|
90
|
+
const handleRowClick = (row: any, index: number) => {
|
|
91
|
+
console.log('Row clicked:', row, index)
|
|
92
|
+
}
|
|
93
|
+
</script>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Global Registration
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
// main.ts
|
|
100
|
+
import { createApp } from 'vue'
|
|
101
|
+
import VsDataTable from 'vs-datatable'
|
|
102
|
+
import 'vs-datatable/style.css'
|
|
103
|
+
|
|
104
|
+
const app = createApp(App)
|
|
105
|
+
app.use(VsDataTable)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Props
|
|
109
|
+
|
|
110
|
+
| Prop | Type | Default | Description |
|
|
111
|
+
|------|------|---------|-------------|
|
|
112
|
+
| `columns` | `Column[]` | **required** | Array of column definitions |
|
|
113
|
+
| `rows` | `any[]` | `[]` | Array of data objects |
|
|
114
|
+
| `loading` | `boolean` | `false` | Shows loading state |
|
|
115
|
+
| `showSearch` | `boolean` | `true` | Enable/disable search functionality |
|
|
116
|
+
| `showRowEntries` | `boolean` | `true` | Show "Showing X to Y of Z entries" |
|
|
117
|
+
| `itemSelected` | `any[] \| null` | `null` | Controlled selection state |
|
|
118
|
+
| `tablename` | `string` | `"default-table"` | Unique identifier for the table |
|
|
119
|
+
| `tableClass` | `string \| string[] \| Record<string, any>` | - | Custom CSS classes for table |
|
|
120
|
+
| `rowClass` | `string \| string[] \| Record<string, any>` | - | Custom CSS classes for rows |
|
|
121
|
+
| `serverOptions` | `ServerOptions \| null` | `null` | Server-side configuration |
|
|
122
|
+
| `serverItemsLength` | `number` | - | Total number of items for server-side pagination |
|
|
123
|
+
| `sort` | `Sort[]` | `[]` | Initial sort configuration |
|
|
124
|
+
| `containerClass` | `string \| string[] \| Record<string, any>` | - | Custom CSS classes for table container |
|
|
125
|
+
| `headerClass` | `string \| string[] \| Record<string, any>` | - | Custom CSS classes for table headers |
|
|
126
|
+
| `cellClass` | `string \| string[] \| Record<string, any>` | - | Custom CSS classes for table cells |
|
|
127
|
+
| `searchClass` | `string \| string[] \| Record<string, any>` | - | Custom CSS classes for search input |
|
|
128
|
+
| `paginationClass` | `string \| string[] \| Record<string, any>` | - | Custom CSS classes for pagination |
|
|
129
|
+
| `searchPlaceholder` | `string` | `'Search...'` | Placeholder text for search input |
|
|
130
|
+
| `loadingText` | `string` | `'Loading...'` | Text shown during loading state |
|
|
131
|
+
| `noDataText` | `string` | `'No data available'` | Text shown when no data |
|
|
132
|
+
| `noDataDescription` | `string` | `'Try adjusting your search criteria'` | Description for no data state |
|
|
133
|
+
| `entriesText` | `string` | `'entries'` | Text for pagination info |
|
|
134
|
+
| `maxVisiblePages` | `number` | `5` | Maximum visible pagination pages |
|
|
135
|
+
| `rowKey` | `string \| ((item: any, index: number) => string \| number)` | `'id'` | Key field for row identification |
|
|
136
|
+
|
|
137
|
+
### Column Definition
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
interface Column {
|
|
141
|
+
label: string; // Display name
|
|
142
|
+
field: string; // Data field path (supports nested: 'user.profile.name')
|
|
143
|
+
width?: string; // Column width percentage
|
|
144
|
+
sortable?: boolean; // Enable sorting
|
|
145
|
+
isKey?: boolean; // Primary key field
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Server Options
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
interface ServerOptions {
|
|
153
|
+
page: number;
|
|
154
|
+
rowsPerPage: number;
|
|
155
|
+
sort?: Sort[];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
interface Sort {
|
|
159
|
+
field: string;
|
|
160
|
+
order: 'asc' | 'desc';
|
|
161
|
+
priority?: number;
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Events
|
|
166
|
+
|
|
167
|
+
| Event | Payload | Description |
|
|
168
|
+
|-------|---------|-------------|
|
|
169
|
+
| `row-click` | `(row: any, index: number)` | Fired when a row is clicked |
|
|
170
|
+
| `input-typed` | `(value: string)` | Fired when search input changes |
|
|
171
|
+
| `page-updated` | `(page: number)` | Fired when page changes |
|
|
172
|
+
| `sort-changed` | `{ sort: Sort[] }` | Fired when sorting changes |
|
|
173
|
+
| `update:itemSelected` | `(items: any[])` | Fired when selection changes |
|
|
174
|
+
| `update:serverOptions` | `(options: ServerOptions)` | Fired when server options change |
|
|
175
|
+
| `update:serverItemsLength` | `(length: number)` | Fired when total items count changes |
|
|
176
|
+
| `update:sort` | `(sort: Sort[])` | v-model:sort support for reactive sorting |
|
|
177
|
+
|
|
178
|
+
## Slots
|
|
179
|
+
|
|
180
|
+
### Header Slots
|
|
181
|
+
```vue
|
|
182
|
+
<template #header-name="{ column }">
|
|
183
|
+
<i class="fa fa-user"></i> {{ column.label }}
|
|
184
|
+
</template>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Cell Slots
|
|
188
|
+
```vue
|
|
189
|
+
<template #cell-status="{ item, value }">
|
|
190
|
+
<span :class="`badge bg-${value === 'Active' ? 'success' : 'danger'}`">
|
|
191
|
+
{{ value }}
|
|
192
|
+
</span>
|
|
193
|
+
</template>
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Custom Areas
|
|
197
|
+
```vue
|
|
198
|
+
<template #filterArea>
|
|
199
|
+
<select class="form-select">
|
|
200
|
+
<option>Filter by status</option>
|
|
201
|
+
</select>
|
|
202
|
+
</template>
|
|
203
|
+
|
|
204
|
+
<template #no-data>
|
|
205
|
+
<div class="text-center">
|
|
206
|
+
<h4>No data found</h4>
|
|
207
|
+
<p>Try adjusting your search criteria</p>
|
|
208
|
+
</div>
|
|
209
|
+
</template>
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Advanced Usage
|
|
213
|
+
|
|
214
|
+
### Server-Side Pagination & Sorting
|
|
215
|
+
|
|
216
|
+
```vue
|
|
217
|
+
<template>
|
|
218
|
+
<VsDataTable
|
|
219
|
+
:columns="columns"
|
|
220
|
+
:rows="data"
|
|
221
|
+
:server-options="serverOptions"
|
|
222
|
+
:server-items-length="totalItems"
|
|
223
|
+
:loading="loading"
|
|
224
|
+
@update:server-options="handleServerOptionsChange"
|
|
225
|
+
@sort-changed="handleSortChange"
|
|
226
|
+
/>
|
|
227
|
+
</template>
|
|
228
|
+
|
|
229
|
+
<script setup lang="ts">
|
|
230
|
+
const serverOptions = ref({
|
|
231
|
+
page: 1,
|
|
232
|
+
rowsPerPage: 10,
|
|
233
|
+
sort: []
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
const totalItems = ref(0)
|
|
237
|
+
const data = ref([])
|
|
238
|
+
const loading = ref(false)
|
|
239
|
+
|
|
240
|
+
const handleServerOptionsChange = async (options: ServerOptions) => {
|
|
241
|
+
loading.value = true
|
|
242
|
+
try {
|
|
243
|
+
const response = await fetchData(options)
|
|
244
|
+
data.value = response.data
|
|
245
|
+
totalItems.value = response.total
|
|
246
|
+
serverOptions.value = options
|
|
247
|
+
} finally {
|
|
248
|
+
loading.value = false
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const handleSortChange = ({ sort }) => {
|
|
253
|
+
console.log('Sort changed:', sort)
|
|
254
|
+
}
|
|
255
|
+
</script>
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Client-Side Sorting with v-model
|
|
259
|
+
|
|
260
|
+
```vue
|
|
261
|
+
<template>
|
|
262
|
+
<VsDataTable
|
|
263
|
+
:columns="columns"
|
|
264
|
+
:rows="data"
|
|
265
|
+
v-model:sort="sortState"
|
|
266
|
+
@sort-changed="handleSortChange"
|
|
267
|
+
/>
|
|
268
|
+
</template>
|
|
269
|
+
|
|
270
|
+
<script setup lang="ts">
|
|
271
|
+
const sortState = ref([
|
|
272
|
+
{ field: 'name', order: 'asc', priority: 1 }
|
|
273
|
+
])
|
|
274
|
+
|
|
275
|
+
const handleSortChange = ({ sort }) => {
|
|
276
|
+
console.log('Sort changed:', sort)
|
|
277
|
+
// sortState is automatically updated via v-model:sort
|
|
278
|
+
}
|
|
279
|
+
</script>
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Row Selection
|
|
283
|
+
|
|
284
|
+
```vue
|
|
285
|
+
<template>
|
|
286
|
+
<VsDataTable
|
|
287
|
+
:columns="columns"
|
|
288
|
+
:rows="data"
|
|
289
|
+
v-model:item-selected="selectedItems"
|
|
290
|
+
@update:item-selected="handleSelectionChange"
|
|
291
|
+
/>
|
|
292
|
+
</template>
|
|
293
|
+
|
|
294
|
+
<script setup lang="ts">
|
|
295
|
+
const selectedItems = ref([])
|
|
296
|
+
|
|
297
|
+
const handleSelectionChange = (items: any[]) => {
|
|
298
|
+
console.log('Selected items:', items)
|
|
299
|
+
// Handle bulk operations
|
|
300
|
+
}
|
|
301
|
+
</script>
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Custom Cell Rendering
|
|
305
|
+
|
|
306
|
+
```vue
|
|
307
|
+
<template>
|
|
308
|
+
<VsDataTable :columns="columns" :rows="data">
|
|
309
|
+
<!-- Custom avatar cell -->
|
|
310
|
+
<template #cell-avatar="{ item }">
|
|
311
|
+
<img :src="item.avatar" :alt="item.name" class="rounded-circle" width="32" height="32">
|
|
312
|
+
</template>
|
|
313
|
+
|
|
314
|
+
<!-- Custom actions cell -->
|
|
315
|
+
<template #cell-actions="{ item }">
|
|
316
|
+
<div class="btn-group btn-group-sm">
|
|
317
|
+
<button class="btn btn-outline-primary" @click="editItem(item)">
|
|
318
|
+
<i class="fa fa-edit"></i>
|
|
319
|
+
</button>
|
|
320
|
+
<button class="btn btn-outline-danger" @click="deleteItem(item)">
|
|
321
|
+
<i class="fa fa-trash"></i>
|
|
322
|
+
</button>
|
|
323
|
+
</div>
|
|
324
|
+
</template>
|
|
325
|
+
</VsDataTable>
|
|
326
|
+
</template>
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Nested Object Support
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
const columns = [
|
|
333
|
+
{ label: 'Name', field: 'name' },
|
|
334
|
+
{ label: 'Company', field: 'company.name' }, // Nested object
|
|
335
|
+
{ label: 'Address', field: 'address.street' }, // Deep nesting
|
|
336
|
+
{ label: 'Contact', field: 'contact.email' } // Multiple levels
|
|
337
|
+
]
|
|
338
|
+
|
|
339
|
+
const data = [
|
|
340
|
+
{
|
|
341
|
+
id: 1,
|
|
342
|
+
name: 'John Doe',
|
|
343
|
+
company: { name: 'Acme Corp' },
|
|
344
|
+
address: { street: '123 Main St' },
|
|
345
|
+
contact: { email: 'john@acme.com' }
|
|
346
|
+
}
|
|
347
|
+
]
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### Multi-Column Sorting
|
|
351
|
+
|
|
352
|
+
```vue
|
|
353
|
+
<template>
|
|
354
|
+
<VsDataTable
|
|
355
|
+
:columns="columns"
|
|
356
|
+
:rows="data"
|
|
357
|
+
v-model:sort="sortState"
|
|
358
|
+
@sort-changed="handleSortChange"
|
|
359
|
+
/>
|
|
360
|
+
</template>
|
|
361
|
+
|
|
362
|
+
<script setup lang="ts">
|
|
363
|
+
const sortState = ref([
|
|
364
|
+
{ field: 'name', order: 'asc', priority: 1 },
|
|
365
|
+
{ field: 'age', order: 'desc', priority: 2 }
|
|
366
|
+
])
|
|
367
|
+
|
|
368
|
+
const handleSortChange = ({ sort }) => {
|
|
369
|
+
console.log('Multi-column sort:', sort)
|
|
370
|
+
// Sort by name first (priority 1), then by age (priority 2)
|
|
371
|
+
}
|
|
372
|
+
</script>
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Sort Icons and Visual Indicators
|
|
376
|
+
|
|
377
|
+
The component includes built-in SVG sort icons that automatically show the current sort state:
|
|
378
|
+
|
|
379
|
+
- **Ascending**: Up arrow icon when column is sorted ascending
|
|
380
|
+
- **Descending**: Down arrow icon when column is sorted descending
|
|
381
|
+
- **Priority Badge**: Shows sort priority number for multi-column sorting
|
|
382
|
+
- **Hover Effects**: Visual feedback on sortable columns
|
|
383
|
+
|
|
384
|
+
## Styling & Customization
|
|
385
|
+
|
|
386
|
+
### CSS Variables System
|
|
387
|
+
|
|
388
|
+
VsDataTable uses CSS custom properties for easy customization. Override any variable to change the appearance:
|
|
389
|
+
|
|
390
|
+
```css
|
|
391
|
+
:root {
|
|
392
|
+
/* Colors */
|
|
393
|
+
--vs-primary: #007bff;
|
|
394
|
+
--vs-secondary: #6c757d;
|
|
395
|
+
--vs-success: #28a745;
|
|
396
|
+
--vs-danger: #dc3545;
|
|
397
|
+
--vs-warning: #ffc107;
|
|
398
|
+
--vs-info: #17a2b8;
|
|
399
|
+
|
|
400
|
+
/* Table Colors */
|
|
401
|
+
--vs-table-bg: #ffffff;
|
|
402
|
+
--vs-table-border: #dee2e6;
|
|
403
|
+
--vs-table-header-bg: #f8f9fa;
|
|
404
|
+
--vs-table-header-color: #495057;
|
|
405
|
+
--vs-table-hover-bg: #f5f5f5;
|
|
406
|
+
|
|
407
|
+
/* Typography */
|
|
408
|
+
--vs-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
409
|
+
--vs-font-size: 14px;
|
|
410
|
+
--vs-font-weight-normal: 400;
|
|
411
|
+
--vs-font-weight-bold: 600;
|
|
412
|
+
|
|
413
|
+
/* Spacing */
|
|
414
|
+
--vs-spacing-xs: 4px;
|
|
415
|
+
--vs-spacing-sm: 8px;
|
|
416
|
+
--vs-spacing-md: 16px;
|
|
417
|
+
--vs-spacing-lg: 24px;
|
|
418
|
+
--vs-spacing-xl: 32px;
|
|
419
|
+
|
|
420
|
+
/* Border Radius */
|
|
421
|
+
--vs-border-radius: 4px;
|
|
422
|
+
--vs-border-radius-sm: 2px;
|
|
423
|
+
--vs-border-radius-lg: 8px;
|
|
424
|
+
|
|
425
|
+
/* Shadows */
|
|
426
|
+
--vs-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
427
|
+
--vs-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
428
|
+
--vs-shadow-lg: 0 4px 8px rgba(0, 0, 0, 0.15);
|
|
429
|
+
|
|
430
|
+
/* Transitions */
|
|
431
|
+
--vs-transition: all 0.2s ease-in-out;
|
|
432
|
+
--vs-transition-fast: all 0.15s ease-in-out;
|
|
433
|
+
}
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### Built-in Themes
|
|
437
|
+
|
|
438
|
+
Apply themes using CSS classes:
|
|
439
|
+
|
|
440
|
+
```vue
|
|
441
|
+
<!-- Dark Theme -->
|
|
442
|
+
<VsDataTable class="vs-theme-dark" />
|
|
443
|
+
|
|
444
|
+
<!-- Minimal Theme -->
|
|
445
|
+
<VsDataTable class="vs-theme-minimal" />
|
|
446
|
+
|
|
447
|
+
<!-- Colorful Theme -->
|
|
448
|
+
<VsDataTable class="vs-theme-colorful" />
|
|
449
|
+
|
|
450
|
+
<!-- Corporate Theme -->
|
|
451
|
+
<VsDataTable class="vs-theme-corporate" />
|
|
452
|
+
|
|
453
|
+
<!-- Compact Theme -->
|
|
454
|
+
<VsDataTable class="vs-theme-compact" />
|
|
455
|
+
|
|
456
|
+
<!-- Rounded Theme -->
|
|
457
|
+
<VsDataTable class="vs-theme-rounded" />
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### Custom Theme Creation
|
|
461
|
+
|
|
462
|
+
Create your own theme by extending the base styles:
|
|
463
|
+
|
|
464
|
+
```scss
|
|
465
|
+
// Custom Brand Theme
|
|
466
|
+
.vs-datatable.vs-theme-brand {
|
|
467
|
+
--vs-primary: #ff6b35;
|
|
468
|
+
--vs-secondary: #004e89;
|
|
469
|
+
--vs-table-bg: #ffffff;
|
|
470
|
+
--vs-table-header-bg: linear-gradient(135deg, #ff6b35, #004e89);
|
|
471
|
+
--vs-table-header-color: #ffffff;
|
|
472
|
+
|
|
473
|
+
.vs-table-container {
|
|
474
|
+
border: 2px solid var(--vs-primary);
|
|
475
|
+
border-radius: 16px;
|
|
476
|
+
box-shadow: 0 8px 32px rgba(255, 107, 53, 0.2);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.vs-pagination-button.vs-active {
|
|
480
|
+
background: var(--vs-primary);
|
|
481
|
+
transform: scale(1.05);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
### Component-Level Customization
|
|
487
|
+
|
|
488
|
+
Customize individual components with CSS classes:
|
|
489
|
+
|
|
490
|
+
```vue
|
|
491
|
+
<VsDataTable
|
|
492
|
+
:columns="columns"
|
|
493
|
+
:rows="data"
|
|
494
|
+
container-class="my-custom-container"
|
|
495
|
+
table-class="my-custom-table"
|
|
496
|
+
header-class="my-custom-header"
|
|
497
|
+
cell-class="my-custom-cell"
|
|
498
|
+
search-class="my-custom-search"
|
|
499
|
+
pagination-class="my-custom-pagination"
|
|
500
|
+
/>
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### Advanced Customization
|
|
504
|
+
|
|
505
|
+
```scss
|
|
506
|
+
// Custom table styling
|
|
507
|
+
.vs-datatable {
|
|
508
|
+
.vs-table {
|
|
509
|
+
border: 2px solid var(--vs-primary);
|
|
510
|
+
border-radius: 12px;
|
|
511
|
+
overflow: hidden;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
.vs-table thead th {
|
|
515
|
+
background: linear-gradient(135deg, var(--vs-primary), var(--vs-secondary));
|
|
516
|
+
color: white;
|
|
517
|
+
font-weight: 700;
|
|
518
|
+
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
.vs-table tbody tr:hover {
|
|
522
|
+
background: linear-gradient(90deg, var(--vs-table-hover-bg), transparent);
|
|
523
|
+
transform: scale(1.01);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.vs-pagination-button {
|
|
527
|
+
border-radius: 50%;
|
|
528
|
+
transition: all 0.3s ease;
|
|
529
|
+
|
|
530
|
+
&:hover:not(:disabled) {
|
|
531
|
+
transform: translateY(-2px);
|
|
532
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
&.vs-active {
|
|
536
|
+
background: var(--vs-primary);
|
|
537
|
+
transform: scale(1.1);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
### Import Styles
|
|
544
|
+
|
|
545
|
+
```typescript
|
|
546
|
+
// Import default styles
|
|
547
|
+
import 'vs-datatable/style.css'
|
|
548
|
+
|
|
549
|
+
// Or import SCSS for advanced customization
|
|
550
|
+
import 'vs-datatable/style.scss'
|
|
551
|
+
|
|
552
|
+
// Import specific theme
|
|
553
|
+
import 'vs-datatable/style.css'
|
|
554
|
+
// Then apply theme class: <VsDataTable class="vs-theme-dark" />
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
## Examples
|
|
558
|
+
|
|
559
|
+
### Complete Example with Server-Side Data
|
|
560
|
+
|
|
561
|
+
```vue
|
|
562
|
+
<template>
|
|
563
|
+
<div class="container">
|
|
564
|
+
<h2>User Management</h2>
|
|
565
|
+
|
|
566
|
+
<VsDataTable
|
|
567
|
+
:columns="columns"
|
|
568
|
+
:rows="users"
|
|
569
|
+
:server-options="serverOptions"
|
|
570
|
+
:server-items-length="totalUsers"
|
|
571
|
+
:loading="loading"
|
|
572
|
+
v-model:item-selected="selectedUsers"
|
|
573
|
+
@update:server-options="fetchUsers"
|
|
574
|
+
@row-click="viewUser"
|
|
575
|
+
@sort-changed="handleSort"
|
|
576
|
+
>
|
|
577
|
+
<!-- Custom status cell -->
|
|
578
|
+
<template #cell-status="{ item }">
|
|
579
|
+
<span :class="`status-badge status-${getStatusColor(item.status)}`">
|
|
580
|
+
{{ item.status }}
|
|
581
|
+
</span>
|
|
582
|
+
</template>
|
|
583
|
+
|
|
584
|
+
<!-- Custom actions -->
|
|
585
|
+
<template #cell-actions="{ item }">
|
|
586
|
+
<button class="action-btn" @click="editUser(item)">
|
|
587
|
+
Edit
|
|
588
|
+
</button>
|
|
589
|
+
</template>
|
|
590
|
+
</VsDataTable>
|
|
591
|
+
|
|
592
|
+
<!-- Bulk actions -->
|
|
593
|
+
<div v-if="selectedUsers.length" class="bulk-actions">
|
|
594
|
+
<button class="delete-btn" @click="deleteSelected">
|
|
595
|
+
Delete {{ selectedUsers.length }} users
|
|
596
|
+
</button>
|
|
597
|
+
</div>
|
|
598
|
+
</div>
|
|
599
|
+
</template>
|
|
600
|
+
|
|
601
|
+
<script setup lang="ts">
|
|
602
|
+
import { ref, onMounted } from 'vue'
|
|
603
|
+
import { VsDataTable } from 'vs-datatable'
|
|
604
|
+
|
|
605
|
+
const columns = [
|
|
606
|
+
{ label: 'ID', field: 'id', width: '10%' },
|
|
607
|
+
{ label: 'Name', field: 'name', sortable: true },
|
|
608
|
+
{ label: 'Email', field: 'email', sortable: true },
|
|
609
|
+
{ label: 'Status', field: 'status', sortable: true },
|
|
610
|
+
{ label: 'Actions', field: 'actions', width: '15%' }
|
|
611
|
+
]
|
|
612
|
+
|
|
613
|
+
const users = ref([])
|
|
614
|
+
const selectedUsers = ref([])
|
|
615
|
+
const loading = ref(false)
|
|
616
|
+
const totalUsers = ref(0)
|
|
617
|
+
|
|
618
|
+
const serverOptions = ref({
|
|
619
|
+
page: 1,
|
|
620
|
+
rowsPerPage: 10,
|
|
621
|
+
sort: []
|
|
622
|
+
})
|
|
623
|
+
|
|
624
|
+
const fetchUsers = async (options) => {
|
|
625
|
+
loading.value = true
|
|
626
|
+
try {
|
|
627
|
+
const response = await api.getUsers({
|
|
628
|
+
page: options.page,
|
|
629
|
+
limit: options.rowsPerPage,
|
|
630
|
+
sort: options.sort
|
|
631
|
+
})
|
|
632
|
+
users.value = response.data
|
|
633
|
+
totalUsers.value = response.total
|
|
634
|
+
} finally {
|
|
635
|
+
loading.value = false
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
const handleSort = ({ sort }) => {
|
|
640
|
+
console.log('Sorting by:', sort)
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const getStatusColor = (status) => {
|
|
644
|
+
return status === 'Active' ? 'success' : 'danger'
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
const viewUser = (user) => {
|
|
648
|
+
console.log('Viewing user:', user)
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
const editUser = (user) => {
|
|
652
|
+
console.log('Editing user:', user)
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const deleteSelected = () => {
|
|
656
|
+
console.log('Deleting users:', selectedUsers.value)
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
onMounted(() => {
|
|
660
|
+
fetchUsers(serverOptions.value)
|
|
661
|
+
})
|
|
662
|
+
</script>
|
|
663
|
+
|
|
664
|
+
<style scoped>
|
|
665
|
+
.status-badge {
|
|
666
|
+
padding: 4px 8px;
|
|
667
|
+
border-radius: 4px;
|
|
668
|
+
font-size: 12px;
|
|
669
|
+
font-weight: 500;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
.status-success {
|
|
673
|
+
background: #d4edda;
|
|
674
|
+
color: #155724;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
.status-danger {
|
|
678
|
+
background: #f8d7da;
|
|
679
|
+
color: #721c24;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
.action-btn {
|
|
683
|
+
padding: 4px 8px;
|
|
684
|
+
border: 1px solid #007bff;
|
|
685
|
+
background: transparent;
|
|
686
|
+
color: #007bff;
|
|
687
|
+
border-radius: 4px;
|
|
688
|
+
cursor: pointer;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
.bulk-actions {
|
|
692
|
+
margin-top: 16px;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
.delete-btn {
|
|
696
|
+
padding: 8px 16px;
|
|
697
|
+
background: #dc3545;
|
|
698
|
+
color: white;
|
|
699
|
+
border: none;
|
|
700
|
+
border-radius: 4px;
|
|
701
|
+
cursor: pointer;
|
|
702
|
+
}
|
|
703
|
+
</style>
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
## Browser Support
|
|
707
|
+
|
|
708
|
+
- Chrome 60+
|
|
709
|
+
- Firefox 60+
|
|
710
|
+
- Safari 12+
|
|
711
|
+
- Edge 79+
|
|
712
|
+
|
|
713
|
+
## Dependencies
|
|
714
|
+
|
|
715
|
+
- Vue 3.2+ (peer dependency)
|
|
716
|
+
- **Zero external dependencies** - No Bootstrap, FontAwesome, or other libraries required
|
|
717
|
+
|
|
718
|
+
## License
|
|
719
|
+
|
|
720
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
721
|
+
|
|
722
|
+
## Contributing
|
|
723
|
+
|
|
724
|
+
1. Fork the repository
|
|
725
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
726
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
727
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
728
|
+
5. Open a Pull Request
|
|
729
|
+
|
|
730
|
+
## Support
|
|
731
|
+
|
|
732
|
+
- 📧 Email: [oregunwasegun@gmail.com]
|
|
733
|
+
- 🐛 Issues: [GitHub Issues](https://github.com/oregs/vs-datatable/issues)
|
|
734
|
+
- 📖 Documentation: [GitHub Wiki](https://github.com/oregs/vs-datatable/wiki)
|