vs-datatable 0.1.1 → 0.3.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 +312 -35
- package/dist/favicon.ico +0 -0
- package/dist/index.css +1 -0
- package/dist/vs-datatable.es.js +420 -0
- package/dist/vs-datatable.umd.js +2 -0
- package/package.json +2 -5
- package/src/styles/base.scss +416 -0
- package/src/styles/index.scss +2 -2
- package/src/styles/themes.scss +215 -0
- package/src/styles/bootstrap.scss +0 -7
- package/src/styles/vs-datatable.scss +0 -40
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# VsDataTable
|
|
2
2
|
|
|
3
|
-
A lightweight, feature-rich Vue 3 data table component with sorting, pagination, search, and row selection capabilities.
|
|
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
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
@@ -8,10 +8,43 @@ A lightweight, feature-rich Vue 3 data table component with sorting, pagination,
|
|
|
8
8
|
- 📊 **Sorting** - Multi-column sorting with visual indicators and priority support
|
|
9
9
|
- 📄 **Pagination** - Server-side and client-side pagination with customizable controls
|
|
10
10
|
- ✅ **Row Selection** - Single and multi-row selection with checkbox controls
|
|
11
|
-
- 🎨 **Customizable** - Extensive
|
|
12
|
-
- 📱 **Responsive** - Mobile-friendly design with
|
|
11
|
+
- 🎨 **Highly Customizable** - Extensive CSS variables, themes, and slot support
|
|
12
|
+
- 📱 **Responsive** - Mobile-friendly design with no external dependencies
|
|
13
13
|
- 🚀 **Performance** - Optimized for large datasets with server-side support
|
|
14
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
|
|
15
48
|
|
|
16
49
|
## Installation
|
|
17
50
|
|
|
@@ -87,6 +120,19 @@ app.use(VsDataTable)
|
|
|
87
120
|
| `rowClass` | `string \| string[] \| Record<string, any>` | - | Custom CSS classes for rows |
|
|
88
121
|
| `serverOptions` | `ServerOptions \| null` | `null` | Server-side configuration |
|
|
89
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 |
|
|
90
136
|
|
|
91
137
|
### Column Definition
|
|
92
138
|
|
|
@@ -106,7 +152,13 @@ interface Column {
|
|
|
106
152
|
interface ServerOptions {
|
|
107
153
|
page: number;
|
|
108
154
|
rowsPerPage: number;
|
|
109
|
-
sort?:
|
|
155
|
+
sort?: Sort[];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
interface Sort {
|
|
159
|
+
field: string;
|
|
160
|
+
order: 'asc' | 'desc';
|
|
161
|
+
priority?: number;
|
|
110
162
|
}
|
|
111
163
|
```
|
|
112
164
|
|
|
@@ -117,10 +169,11 @@ interface ServerOptions {
|
|
|
117
169
|
| `row-click` | `(row: any, index: number)` | Fired when a row is clicked |
|
|
118
170
|
| `input-typed` | `(value: string)` | Fired when search input changes |
|
|
119
171
|
| `page-updated` | `(page: number)` | Fired when page changes |
|
|
120
|
-
| `sort-changed` | `{ sort:
|
|
172
|
+
| `sort-changed` | `{ sort: Sort[] }` | Fired when sorting changes |
|
|
121
173
|
| `update:itemSelected` | `(items: any[])` | Fired when selection changes |
|
|
122
174
|
| `update:serverOptions` | `(options: ServerOptions)` | Fired when server options change |
|
|
123
175
|
| `update:serverItemsLength` | `(length: number)` | Fired when total items count changes |
|
|
176
|
+
| `update:sort` | `(sort: Sort[])` | v-model:sort support for reactive sorting |
|
|
124
177
|
|
|
125
178
|
## Slots
|
|
126
179
|
|
|
@@ -202,6 +255,30 @@ const handleSortChange = ({ sort }) => {
|
|
|
202
255
|
</script>
|
|
203
256
|
```
|
|
204
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
|
+
|
|
205
282
|
### Row Selection
|
|
206
283
|
|
|
207
284
|
```vue
|
|
@@ -270,28 +347,195 @@ const data = [
|
|
|
270
347
|
]
|
|
271
348
|
```
|
|
272
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
|
+
|
|
273
384
|
## Styling & Customization
|
|
274
385
|
|
|
275
|
-
### CSS
|
|
386
|
+
### CSS Variables System
|
|
276
387
|
|
|
277
|
-
|
|
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:
|
|
278
463
|
|
|
279
464
|
```scss
|
|
280
|
-
// Custom
|
|
281
|
-
.vs-
|
|
282
|
-
|
|
283
|
-
|
|
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);
|
|
284
477
|
}
|
|
285
478
|
|
|
286
|
-
.
|
|
287
|
-
background
|
|
479
|
+
.vs-pagination-button.vs-active {
|
|
480
|
+
background: var(--vs-primary);
|
|
481
|
+
transform: scale(1.05);
|
|
288
482
|
}
|
|
289
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
|
|
290
504
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|
+
}
|
|
295
539
|
}
|
|
296
540
|
}
|
|
297
541
|
```
|
|
@@ -302,19 +546,12 @@ The component uses Bootstrap 5 classes and provides several customization points
|
|
|
302
546
|
// Import default styles
|
|
303
547
|
import 'vs-datatable/style.css'
|
|
304
548
|
|
|
305
|
-
// Or import SCSS for customization
|
|
549
|
+
// Or import SCSS for advanced customization
|
|
306
550
|
import 'vs-datatable/style.scss'
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
### Custom SCSS Variables
|
|
310
551
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
$secondary: #your-color;
|
|
315
|
-
|
|
316
|
-
// Import the component styles
|
|
317
|
-
@import 'vs-datatable/style.scss';
|
|
552
|
+
// Import specific theme
|
|
553
|
+
import 'vs-datatable/style.css'
|
|
554
|
+
// Then apply theme class: <VsDataTable class="vs-theme-dark" />
|
|
318
555
|
```
|
|
319
556
|
|
|
320
557
|
## Examples
|
|
@@ -339,22 +576,22 @@ $secondary: #your-color;
|
|
|
339
576
|
>
|
|
340
577
|
<!-- Custom status cell -->
|
|
341
578
|
<template #cell-status="{ item }">
|
|
342
|
-
<span :class="`badge
|
|
579
|
+
<span :class="`status-badge status-${getStatusColor(item.status)}`">
|
|
343
580
|
{{ item.status }}
|
|
344
581
|
</span>
|
|
345
582
|
</template>
|
|
346
583
|
|
|
347
584
|
<!-- Custom actions -->
|
|
348
585
|
<template #cell-actions="{ item }">
|
|
349
|
-
<button class="
|
|
586
|
+
<button class="action-btn" @click="editUser(item)">
|
|
350
587
|
Edit
|
|
351
588
|
</button>
|
|
352
589
|
</template>
|
|
353
590
|
</VsDataTable>
|
|
354
591
|
|
|
355
592
|
<!-- Bulk actions -->
|
|
356
|
-
<div v-if="selectedUsers.length" class="
|
|
357
|
-
<button class="btn
|
|
593
|
+
<div v-if="selectedUsers.length" class="bulk-actions">
|
|
594
|
+
<button class="delete-btn" @click="deleteSelected">
|
|
358
595
|
Delete {{ selectedUsers.length }} users
|
|
359
596
|
</button>
|
|
360
597
|
</div>
|
|
@@ -423,6 +660,47 @@ onMounted(() => {
|
|
|
423
660
|
fetchUsers(serverOptions.value)
|
|
424
661
|
})
|
|
425
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>
|
|
426
704
|
```
|
|
427
705
|
|
|
428
706
|
## Browser Support
|
|
@@ -434,9 +712,8 @@ onMounted(() => {
|
|
|
434
712
|
|
|
435
713
|
## Dependencies
|
|
436
714
|
|
|
437
|
-
- Vue 3.2+
|
|
438
|
-
- Bootstrap
|
|
439
|
-
- Font Awesome 7.0+
|
|
715
|
+
- Vue 3.2+ (peer dependency)
|
|
716
|
+
- **Zero external dependencies** - No Bootstrap, FontAwesome, or other libraries required
|
|
440
717
|
|
|
441
718
|
## License
|
|
442
719
|
|
package/dist/favicon.ico
ADDED
|
Binary file
|
package/dist/index.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.vs-pagination[data-v-a6d89ca2]{display:flex;align-items:center;justify-content:center;gap:var(--vs-spacing-sm);flex-wrap:wrap}.vs-pagination-button[data-v-a6d89ca2]{display:inline-flex;align-items:center;justify-content:center;min-width:32px;height:32px;padding:0 var(--vs-spacing-sm);border:1px solid var(--vs-table-border);background-color:var(--vs-table-bg);color:var(--vs-dark);text-decoration:none;border-radius:var(--vs-border-radius);font-size:var(--vs-font-size-sm);transition:var(--vs-transition-fast);cursor:pointer}.vs-pagination-button[data-v-a6d89ca2]:hover:not(:disabled){background-color:var(--vs-table-hover-bg);border-color:var(--vs-primary)}.vs-pagination-button.vs-active[data-v-a6d89ca2]{background-color:var(--vs-primary);border-color:var(--vs-primary);color:#fff}.vs-pagination-button[data-v-a6d89ca2]:disabled{opacity:.5;cursor:not-allowed}.vs-pagination-nav[data-v-a6d89ca2]{font-weight:var(--vs-font-weight-bold)}.vs-pagination-ellipsis[data-v-a6d89ca2]{color:var(--vs-secondary);padding:0 var(--vs-spacing-sm);font-size:var(--vs-font-size-sm)}@media (max-width: 768px){.vs-pagination-button[data-v-a6d89ca2]{min-width:28px;height:28px;font-size:12px}}.vs-datatable[data-v-fa66e546]{--vs-table-wrapper-overflow: auto}.vs-table-wrapper[data-v-fa66e546]{overflow:var(--vs-table-wrapper-overflow)}.vs-header-content[data-v-fa66e546]{display:flex;align-items:center;gap:var(--vs-spacing-sm)}.vs-header-label[data-v-fa66e546]{flex:1}.vs-checkbox-column[data-v-fa66e546]{width:50px;text-align:center}.vs-row-clickable[data-v-fa66e546]{cursor:pointer}.vs-row-selected[data-v-fa66e546]{background-color:rgba(var(--vs-primary),.1)}.vs-table-footer[data-v-fa66e546]{display:flex;align-items:center;justify-content:space-between;padding:var(--vs-spacing-sm) 0}.vs-table-info[data-v-fa66e546]{color:var(--vs-secondary);font-size:var(--vs-font-size-md)}.vs-search-container[data-v-fa66e546]{margin-bottom:var(--vs-spacing-md)}:root{--vs-primary: #007bff;--vs-secondary: #6c757d;--vs-success: #28a745;--vs-danger: #dc3545;--vs-warning: #ffc107;--vs-info: #17a2b8;--vs-light: #f8f9fa;--vs-dark: #343a40;--vs-table-bg: #ffffff;--vs-table-border: #dee2e6;--vs-table-header-bg: #f8f9fa;--vs-table-header-color: #495057;--vs-table-hover-bg: #f5f5f5;--vs-table-stripe-bg: #fafafa;--vs-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;--vs-font-size: 14px;--vs-font-size-sm: 12px;--vs-font-size-lg: 16px;--vs-font-weight-normal: 400;--vs-font-weight-bold: 600;--vs-spacing-xs: 4px;--vs-spacing-sm: 8px;--vs-spacing-md: 16px;--vs-spacing-lg: 24px;--vs-spacing-xl: 32px;--vs-border-radius: 4px;--vs-border-radius-sm: 2px;--vs-border-radius-lg: 8px;--vs-shadow-sm: 0 1px 3px rgba(0, 0, 0, .1);--vs-shadow: 0 2px 4px rgba(0, 0, 0, .1);--vs-shadow-lg: 0 4px 8px rgba(0, 0, 0, .15);--vs-transition: all .2s ease-in-out;--vs-transition-fast: all .15s ease-in-out;--vs-z-dropdown: 1000;--vs-z-sticky: 1020;--vs-z-fixed: 1030;--vs-z-modal: 1040;--vs-z-popover: 1050;--vs-z-tooltip: 1060}.vs-datatable{font-family:var(--vs-font-family);font-size:var(--vs-font-size);line-height:1.5;color:var(--vs-dark);background-color:var(--vs-table-bg);box-sizing:border-box}.vs-datatable *,.vs-datatable *:before,.vs-datatable *:after{box-sizing:border-box}.vs-table-container{position:relative;background-color:var(--vs-table-bg);border:1px solid var(--vs-table-border);border-radius:var(--vs-border-radius);overflow:hidden;box-shadow:var(--vs-shadow-sm)}.vs-table{width:100%;margin:0;border-collapse:separate;border-spacing:0;background-color:var(--vs-table-bg)}.vs-table thead{background-color:var(--vs-table-header-bg)}.vs-table thead th{padding:var(--vs-spacing-sm);font-weight:var(--vs-font-weight-bold);color:var(--vs-table-header-color);text-align:left;vertical-align:middle;border-bottom:2px solid var(--vs-table-border);position:relative;-webkit-user-select:none;user-select:none}.vs-table thead th:first-child{border-top-left-radius:var(--vs-border-radius)}.vs-table thead th:last-child{border-top-right-radius:var(--vs-border-radius)}.vs-table tbody tr{transition:var(--vs-transition-fast);border-bottom:1px solid var(--vs-table-border)}.vs-table tbody tr:hover{background-color:var(--vs-table-hover-bg)}.vs-table tbody tr:nth-child(2n){background-color:var(--vs-table-stripe-bg)}.vs-table tbody tr:last-child{border-bottom:none}.vs-table tbody tr td{padding:var(--vs-spacing-md);vertical-align:middle;border-right:1px solid var(--vs-table-border)}.vs-table tbody tr td:last-child{border-right:none}.vs-sortable{cursor:pointer;position:relative}.vs-sortable:hover{background-color:rgba(var(--vs-primary),.1)}.vs-sortable .vs-sort-icons{display:inline-flex;flex-direction:column;margin-left:var(--vs-spacing-sm);vertical-align:middle}.vs-sortable .vs-sort-icons .vs-sort-icon{font-size:10px;line-height:1;color:var(--vs-secondary);transition:var(--vs-transition-fast);margin:-8px 0}.vs-sortable .vs-sort-icons .vs-sort-icon.vs-sort-asc,.vs-sortable .vs-sort-icons .vs-sort-icon.vs-sort-desc{color:var(--vs-primary)}.vs-sortable .vs-sort-icons .vs-sort-icon svg{fill:#e3e3e3}.vs-sortable .vs-sort-icons .vs-sort-icon.vs-active svg{fill:currentColor}.vs-sortable .vs-sort-priority{display:inline-block;background-color:var(--vs-primary);color:#fff;font-size:10px;padding:2px 6px;border-radius:var(--vs-border-radius-sm);margin-left:var(--vs-spacing-sm);font-weight:var(--vs-font-weight-bold)}.vs-loading{display:flex;align-items:center;justify-content:center;padding:var(--vs-spacing-xl);color:var(--vs-secondary)}.vs-loading .vs-spinner{width:20px;height:20px;border:2px solid var(--vs-table-border);border-top:2px solid var(--vs-primary);border-radius:50%;animation:vs-spin 1s linear infinite;margin-right:var(--vs-spacing-sm)}@keyframes vs-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.vs-no-data{text-align:center;padding:var(--vs-spacing-xl);color:var(--vs-secondary)}.vs-no-data .vs-no-data-icon{width:60px;height:60px;margin:0 auto var(--vs-spacing-md);opacity:.5}.vs-no-data .vs-no-data-message{font-size:var(--vs-font-size-lg);font-weight:var(--vs-font-weight-bold);margin-bottom:var(--vs-spacing-sm)}.vs-no-data .vs-no-data-description{font-size:var(--vs-font-size-sm);color:var(--vs-secondary)}.vs-search{position:relative;margin-bottom:var(--vs-spacing-md)}.vs-search .vs-search-input{width:100%;padding:var(--vs-spacing-sm) var(--vs-spacing-md);padding-left:40px;border:1px solid var(--vs-table-border);border-radius:var(--vs-border-radius);font-size:var(--vs-font-size);transition:var(--vs-transition-fast)}.vs-search .vs-search-input:focus{outline:none;border-color:var(--vs-primary);box-shadow:0 0 0 2px rgba(var(--vs-primary),.25)}.vs-search .vs-search-icon{position:absolute;left:var(--vs-spacing-md);top:50%;transform:translateY(-50%);color:var(--vs-secondary);pointer-events:none}.vs-pagination{display:flex;align-items:center;justify-content:center;gap:var(--vs-spacing-sm);margin-top:var(--vs-spacing-md)}.vs-pagination .vs-pagination-button{display:inline-flex;align-items:center;justify-content:center;min-width:32px;height:32px;padding:0 var(--vs-spacing-sm);border:1px solid var(--vs-table-border);background-color:var(--vs-table-bg);color:var(--vs-dark);text-decoration:none;border-radius:var(--vs-border-radius);font-size:var(--vs-font-size-sm);transition:var(--vs-transition-fast);cursor:pointer}.vs-pagination .vs-pagination-button:hover:not(:disabled){background-color:var(--vs-table-hover-bg);border-color:var(--vs-primary)}.vs-pagination .vs-pagination-button.vs-active{background-color:var(--vs-primary);border-color:var(--vs-primary);color:#fff}.vs-pagination .vs-pagination-button:disabled{opacity:.5;cursor:not-allowed}.vs-pagination .vs-pagination-ellipsis{color:var(--vs-secondary);padding:0 var(--vs-spacing-sm)}.vs-checkbox{display:inline-flex;align-items:center;cursor:pointer}.vs-checkbox input[type=checkbox]{width:16px;height:16px;margin:0;cursor:pointer;accent-color:var(--vs-primary)}.vs-checkbox label{margin:0;cursor:pointer;font-size:var(--vs-font-size-sm)}@media (max-width: 768px){.vs-table-container{overflow-x:auto}.vs-table{min-width:600px}.vs-pagination{flex-wrap:wrap;gap:var(--vs-spacing-xs)}.vs-pagination .vs-pagination-button{min-width:28px;height:28px;font-size:12px}}.vs-text-center{text-align:center}.vs-text-left{text-align:left}.vs-text-right{text-align:right}.vs-text-muted{color:var(--vs-secondary)}.vs-text-primary{color:var(--vs-primary)}.vs-text-success{color:var(--vs-success)}.vs-text-danger{color:var(--vs-danger)}.vs-text-warning{color:var(--vs-warning)}.vs-bg-primary{background-color:var(--vs-primary)}.vs-bg-success{background-color:var(--vs-success)}.vs-bg-danger{background-color:var(--vs-danger)}.vs-bg-warning{background-color:var(--vs-warning)}.vs-bg-light{background-color:var(--vs-light)}.vs-border{border:1px solid var(--vs-table-border)}.vs-border-top{border-top:1px solid var(--vs-table-border)}.vs-border-bottom{border-bottom:1px solid var(--vs-table-border)}.vs-border-left{border-left:1px solid var(--vs-table-border)}.vs-border-right{border-right:1px solid var(--vs-table-border)}.vs-rounded{border-radius:var(--vs-border-radius)}.vs-rounded-sm{border-radius:var(--vs-border-radius-sm)}.vs-rounded-lg{border-radius:var(--vs-border-radius-lg)}.vs-shadow{box-shadow:var(--vs-shadow)}.vs-shadow-sm{box-shadow:var(--vs-shadow-sm)}.vs-shadow-lg{box-shadow:var(--vs-shadow-lg)}.vs-fade-in{animation:vs-fade-in .3s ease-in-out}@keyframes vs-fade-in{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.vs-slide-in{animation:vs-slide-in .3s ease-in-out}@keyframes vs-slide-in{0%{transform:translate(-20px);opacity:0}to{transform:translate(0);opacity:1}}.vs-datatable.vs-theme-dark{--vs-primary: #0d6efd;--vs-secondary: #6c757d;--vs-success: #198754;--vs-danger: #dc3545;--vs-warning: #ffc107;--vs-info: #0dcaf0;--vs-light: #f8f9fa;--vs-dark: #212529;--vs-table-bg: #1a1a1a;--vs-table-border: #333333;--vs-table-header-bg: #2d2d2d;--vs-table-header-color: #ffffff;--vs-table-hover-bg: #333333;--vs-table-stripe-bg: #222222;color:#fff;background-color:var(--vs-table-bg)}.vs-datatable.vs-theme-minimal{--vs-primary: #000000;--vs-secondary: #666666;--vs-success: #000000;--vs-danger: #000000;--vs-warning: #000000;--vs-info: #000000;--vs-light: #f9f9f9;--vs-dark: #000000;--vs-table-bg: #ffffff;--vs-table-border: #e0e0e0;--vs-table-header-bg: #ffffff;--vs-table-header-color: #000000;--vs-table-hover-bg: #f5f5f5;--vs-table-stripe-bg: #ffffff}.vs-datatable.vs-theme-minimal .vs-table{border:none;box-shadow:none}.vs-datatable.vs-theme-minimal .vs-table thead th{border-bottom:2px solid var(--vs-table-border);font-weight:500}.vs-datatable.vs-theme-minimal .vs-table tbody tr{border-bottom:1px solid var(--vs-table-border)}.vs-datatable.vs-theme-colorful{--vs-primary: #e91e63;--vs-secondary: #9c27b0;--vs-success: #4caf50;--vs-danger: #f44336;--vs-warning: #ff9800;--vs-info: #2196f3;--vs-light: #f3e5f5;--vs-dark: #2e2e2e;--vs-table-bg: #ffffff;--vs-table-border: #e1bee7;--vs-table-header-bg: linear-gradient(135deg, #e91e63, #9c27b0);--vs-table-header-color: #ffffff;--vs-table-hover-bg: #f3e5f5;--vs-table-stripe-bg: #fafafa}.vs-datatable.vs-theme-colorful .vs-table thead th{background:var(--vs-table-header-bg);color:var(--vs-table-header-color);font-weight:600}.vs-datatable.vs-theme-colorful .vs-pagination-button.vs-active{background:linear-gradient(135deg,var(--vs-primary),var(--vs-secondary))}.vs-datatable.vs-theme-corporate{--vs-primary: #1e3a8a;--vs-secondary: #64748b;--vs-success: #059669;--vs-danger: #dc2626;--vs-warning: #d97706;--vs-info: #0891b2;--vs-light: #f8fafc;--vs-dark: #1e293b;--vs-table-bg: #ffffff;--vs-table-border: #e2e8f0;--vs-table-header-bg: #f1f5f9;--vs-table-header-color: #334155;--vs-table-hover-bg: #f8fafc;--vs-table-stripe-bg: #ffffff;font-family:Inter,-apple-system,BlinkMacSystemFont,sans-serif}.vs-datatable.vs-theme-corporate .vs-table{border-radius:8px;overflow:hidden;box-shadow:0 1px 3px #0000001a}.vs-datatable.vs-theme-corporate .vs-table thead th{font-size:13px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;padding:16px 20px}.vs-datatable.vs-theme-corporate .vs-table tbody td{padding:16px 20px;font-size:14px}.vs-datatable.vs-theme-compact{--vs-spacing-xs: 2px;--vs-spacing-sm: 4px;--vs-spacing-md: 8px;--vs-spacing-lg: 12px;--vs-spacing-xl: 16px;--vs-font-size: 12px;--vs-font-size-sm: 10px;--vs-font-size-lg: 14px}.vs-datatable.vs-theme-compact .vs-table thead th{padding:var(--vs-spacing-sm) var(--vs-spacing-md);font-size:var(--vs-font-size-sm)}.vs-datatable.vs-theme-compact .vs-table tbody td{padding:var(--vs-spacing-sm) var(--vs-spacing-md);font-size:var(--vs-font-size)}.vs-datatable.vs-theme-compact .vs-pagination-button{min-width:24px;height:24px;font-size:var(--vs-font-size-sm)}.vs-datatable.vs-theme-rounded{--vs-border-radius: 12px;--vs-border-radius-sm: 6px;--vs-border-radius-lg: 16px}.vs-datatable.vs-theme-rounded .vs-table-container{border-radius:var(--vs-border-radius);overflow:hidden}.vs-datatable.vs-theme-rounded .vs-table thead th:first-child{border-top-left-radius:var(--vs-border-radius)}.vs-datatable.vs-theme-rounded .vs-table thead th:last-child{border-top-right-radius:var(--vs-border-radius)}.vs-datatable.vs-theme-rounded .vs-pagination-button{border-radius:var(--vs-border-radius-sm)}.vs-datatable.vs-theme-brand{--vs-primary: #ff6b35;--vs-secondary: #004e89;--vs-success: #00a896;--vs-danger: #e63946;--vs-warning: #f77f00;--vs-info: #06a77d}.vs-datatable.vs-theme-brand .vs-table-container{border:2px solid var(--vs-primary);border-radius:16px;box-shadow:0 8px 32px #ff6b3533}.vs-datatable.vs-theme-brand .vs-table thead th{background:linear-gradient(135deg,var(--vs-primary),var(--vs-secondary));color:#fff;font-weight:700;text-shadow:0 1px 2px rgba(0,0,0,.1)}.vs-datatable.vs-theme-brand .vs-pagination-button.vs-active{background:var(--vs-primary);transform:scale(1.05)}.vs-datatable.vs-theme-brand .vs-pagination-button:hover:not(:disabled){border-color:var(--vs-primary);transform:translateY(-1px)}
|