vs-datatable 0.1.1 → 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 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. Built with TypeScript and Bootstrap 5 styling.
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 slot support for customizing headers, cells, and layouts
12
- - 📱 **Responsive** - Mobile-friendly design with Bootstrap 5
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?: { field: string; order: 'asc' | 'desc'; priority?: number }[];
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: SortOption[] }` | Fired when sorting changes |
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 Classes
386
+ ### CSS Variables System
276
387
 
277
- The component uses Bootstrap 5 classes and provides several customization points:
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 table styling
281
- .vs-table {
282
- .sort-icon {
283
- color: #007bff;
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
- .sort-badge {
287
- background-color: #28a745;
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
- // Custom pagination
292
- .pagination {
293
- .page-link {
294
- border-radius: 0.375rem;
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
- ```scss
312
- // Override Bootstrap variables
313
- $primary: #your-color;
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 bg-${getStatusColor(item.status)}`">
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="btn btn-sm btn-outline-primary" @click="editUser(item)">
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="mt-3">
357
- <button class="btn btn-danger" @click="deleteSelected">
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 5.3+
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
 
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-73862aa1]{--vs-table-wrapper-overflow: auto}.vs-table-wrapper[data-v-73862aa1]{overflow:var(--vs-table-wrapper-overflow)}.vs-header-content[data-v-73862aa1]{display:flex;align-items:center;gap:var(--vs-spacing-sm)}.vs-header-label[data-v-73862aa1]{flex:1}.vs-checkbox-column[data-v-73862aa1]{width:50px;text-align:center}.vs-row-clickable[data-v-73862aa1]{cursor:pointer}.vs-row-selected[data-v-73862aa1]{background-color:rgba(var(--vs-primary),.1)}.vs-table-footer[data-v-73862aa1]{display:flex;align-items:center;justify-content:space-between;padding:var(--vs-spacing-sm) 0}.vs-table-info[data-v-73862aa1]{color:var(--vs-secondary);font-size:var(--vs-font-size-md)}.vs-search-container[data-v-73862aa1]{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)}