vs-datatable 1.2.0 → 1.2.2

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.
@@ -1,10 +1,12 @@
1
1
  <template>
2
2
  <div class="export-dropdown">
3
- <button class="export-btn" ref="toggleBtn" @click.stop="toggleMenu">Export ▾</button>
3
+ <button class="export-btn" ref="toggleBtn" @click.stop="toggleMenu">
4
+ Export ▾
5
+ </button>
4
6
  <ul class="export-menu" ref="menu" :class="{ active: isOpen }">
5
7
  <li><button @click="exportToCSV()">Export CSV</button></li>
6
8
  <li><button @click="exportToExcel()">Export Excel</button></li>
7
- <li><button @click="exportToPDF()">Export PDF</button></li>
9
+ <li><button>Export PDF</button></li>
8
10
  </ul>
9
11
  </div>
10
12
  </template>
@@ -12,7 +14,7 @@
12
14
  <script setup lang="ts">
13
15
  import { ref, onMounted, onBeforeUnmount } from 'vue'
14
16
  import type { Column, Row } from '@/types'
15
- import { useDataTableExport } from './useDataTableExport'
17
+ import { useDataTableExport } from './useDataTableExport';
16
18
 
17
19
  const props = defineProps<{
18
20
  rows: Row[]
@@ -30,10 +32,7 @@ function closeMenu() {
30
32
  isOpen.value = false
31
33
  }
32
34
 
33
- const { exportToCSV, exportToExcel, exportToPDF } = useDataTableExport(
34
- ref(props.rows),
35
- props.columns
36
- )
35
+ const { exportToCSV, exportToExcel } = useDataTableExport(ref(props.rows), props.columns)
37
36
 
38
37
  onMounted(() => {
39
38
  document.addEventListener('click', closeMenu)
@@ -44,4 +43,6 @@ onBeforeUnmount(() => {
44
43
  })
45
44
  </script>
46
45
 
47
- <style scoped></style>
46
+ <style scoped>
47
+
48
+ </style>
@@ -4,11 +4,6 @@ import type { Ref } from 'vue'
4
4
  export function useDataTableExport<T extends Record<string, any>>(rows: Ref<T[]>, columns: any[]) {
5
5
  /** Export as CSV */
6
6
  const exportToCSV = (filename = 'table.csv') => {
7
- if (!Array.isArray(columns) || !Array.isArray(rows.value) || !rows.value.length) {
8
- console.warn('[VsDataTableExport] No data available for CSV export.')
9
- return
10
- }
11
-
12
7
  const headers = columns.map(col => col.label)
13
8
  const data = rows.value.map(row =>
14
9
  columns.map(col => JSON.stringify(row[col.field] ?? '')).join(',')
@@ -28,12 +23,7 @@ export function useDataTableExport<T extends Record<string, any>>(rows: Ref<T[]>
28
23
 
29
24
  /** Export as Excel */
30
25
  const exportToExcel = async (filename = 'table.xlsx') => {
31
- if (!Array.isArray(columns) || !Array.isArray(rows.value) || !rows.value.length) {
32
- console.warn('[VsDataTableExport] No data available for Excel export.')
33
- return
34
- }
35
-
36
- const { utils, writeFile } = await import('xlsx')
26
+ const { utils, writeFile } = await import('xlsx') // lazy import (only loads if used)
37
27
  const data = rows.value.map(row =>
38
28
  Object.fromEntries(columns.map(col => [col.label, row[col.field]]))
39
29
  )
@@ -43,44 +33,5 @@ export function useDataTableExport<T extends Record<string, any>>(rows: Ref<T[]>
43
33
  writeFile(workbook, filename)
44
34
  }
45
35
 
46
- /** Export as PDF */
47
- async function exportToPDF(filename = 'data-export.pdf') {
48
- if (!Array.isArray(columns) || !Array.isArray(rows.value) || !rows.value.length) {
49
- console.warn('[VsDataTableExport] No data available for PDF export.')
50
- return
51
- }
52
-
53
- const [{ default: jsPDF }, autoTable] = await Promise.all([
54
- import('jspdf'),
55
- import('jspdf-autotable'),
56
- ])
57
-
58
- const doc = new jsPDF()
59
- const headers = columns.map(c => c.label || c.field)
60
- const data = rows.value.map(row => columns.map(c => row[c.field] ?? ''))
61
-
62
- autoTable.default(doc, {
63
- head: [headers],
64
- body: data,
65
- styles: {
66
- fontSize: 10,
67
- cellPadding: 4,
68
- },
69
- headStyles: {
70
- fillColor: [45, 108, 223],
71
- textColor: [255, 255, 255],
72
- },
73
- startY: 20,
74
- margin: { top: 20 },
75
- theme: 'grid',
76
- })
77
-
78
- doc.save(filename)
79
- }
80
-
81
- return {
82
- exportToCSV,
83
- exportToExcel,
84
- exportToPDF
85
- }
36
+ return { exportToCSV, exportToExcel }
86
37
  }
@@ -399,6 +399,137 @@
399
399
  .vs-transition-fast { transition: var(--vs-transition-fast) !important; }
400
400
  .vs-transition-none { transition: none !important; }
401
401
 
402
+ /* Utility Classes */
403
+ .vs-text-center {
404
+ text-align: center;
405
+ }
406
+ .vs-text-left {
407
+ text-align: left;
408
+ }
409
+ .vs-text-right {
410
+ text-align: right;
411
+ }
412
+ .vs-text-muted {
413
+ color: var(--vs-secondary);
414
+ }
415
+ .vs-text-primary {
416
+ color: var(--vs-primary);
417
+ }
418
+ .vs-text-success {
419
+ color: var(--vs-success);
420
+ }
421
+ .vs-text-danger {
422
+ color: var(--vs-danger);
423
+ }
424
+ .vs-text-warning {
425
+ color: var(--vs-warning);
426
+ }
427
+
428
+ .vs-bg-primary {
429
+ background-color: var(--vs-primary);
430
+ }
431
+ .vs-bg-success {
432
+ background-color: var(--vs-success);
433
+ }
434
+ .vs-bg-danger {
435
+ background-color: var(--vs-danger);
436
+ }
437
+ .vs-bg-warning {
438
+ background-color: var(--vs-warning);
439
+ }
440
+ .vs-bg-light {
441
+ background-color: var(--vs-light);
442
+ }
443
+
444
+ .vs-border {
445
+ border: 1px solid var(--vs-table-border);
446
+ }
447
+ .vs-border-top {
448
+ border-top: 1px solid var(--vs-table-border);
449
+ }
450
+ .vs-border-bottom {
451
+ border-bottom: 1px solid var(--vs-table-border);
452
+ }
453
+ .vs-border-left {
454
+ border-left: 1px solid var(--vs-table-border);
455
+ }
456
+ .vs-border-right {
457
+ border-right: 1px solid var(--vs-table-border);
458
+ }
459
+
460
+ .vs-rounded {
461
+ border-radius: var(--vs-border-radius);
462
+ }
463
+ .vs-rounded-sm {
464
+ border-radius: var(--vs-border-radius-sm);
465
+ }
466
+ .vs-rounded-lg {
467
+ border-radius: var(--vs-border-radius-lg);
468
+ }
469
+
470
+ .vs-shadow {
471
+ box-shadow: var(--vs-shadow);
472
+ }
473
+ .vs-shadow-sm {
474
+ box-shadow: var(--vs-shadow-sm);
475
+ }
476
+ .vs-shadow-lg {
477
+ box-shadow: var(--vs-shadow-lg);
478
+ }
479
+
480
+ /* Animation Classes */
481
+ .vs-fade-in {
482
+ animation: vs-fade-in 0.3s ease-in-out;
483
+ }
484
+
485
+ @keyframes vs-fade-in {
486
+ from {
487
+ opacity: 0;
488
+ transform: translateY(-10px);
489
+ }
490
+ to {
491
+ opacity: 1;
492
+ transform: translateY(0);
493
+ }
494
+ }
495
+
496
+ .vs-slide-in {
497
+ animation: vs-slide-in 0.3s ease-in-out;
498
+ }
499
+
500
+ @keyframes vs-slide-in {
501
+ from {
502
+ transform: translateX(-20px);
503
+ opacity: 0;
504
+ }
505
+ to {
506
+ transform: translateX(0);
507
+ opacity: 1;
508
+ }
509
+ }
510
+
511
+ /* VS Button */
512
+ .vs-btn {
513
+ padding: 0.35rem 0.75rem;
514
+ border-radius: 4px;
515
+ cursor: pointer;
516
+ }
517
+ .vs-btn-primary {
518
+ background: #2d6cdf;
519
+ color: #fff;
520
+ border: none;
521
+ }
522
+ .vs-btn-secondary {
523
+ background: #f5f5f5;
524
+ border: 1px solid #ccc;
525
+ }
526
+
527
+ /**
528
+ *-------------------------------------------
529
+ * Custom CSS
530
+ *--------------------------------------------
531
+ */
532
+
402
533
  /* Reset and Base Styles */
403
534
  .vs-datatable {
404
535
  font-family: var(--vs-font-family);
@@ -434,7 +565,7 @@
434
565
  background-color: var(--vs-table-bg);
435
566
 
436
567
  thead {
437
- background-color: var(--vs-table-header-bg);
568
+ background-color: var(--vs-table-bg); // Changed from "--vs-table-header-bg"
438
569
 
439
570
  th {
440
571
  padding: var(--vs-spacing-md);
@@ -454,6 +585,17 @@
454
585
  border-top-right-radius: var(--vs-border-radius);
455
586
  }
456
587
  }
588
+
589
+ th::after {
590
+ content: "";
591
+ position: absolute;
592
+ right: 0;
593
+ top: 25%;
594
+ bottom: 25%;
595
+ width: 1px;
596
+ opacity: 0.6;
597
+ pointer-events: none;
598
+ }
457
599
  }
458
600
 
459
601
  tbody {
@@ -484,8 +626,52 @@
484
626
  }
485
627
  }
486
628
  }
629
+
630
+ .vs-expand-column {
631
+ width: 2px;
632
+ border-right: 1px solid var(--vs-border-color, #e5e7eb);
633
+ }
634
+
635
+ /* Column Grouping */
636
+ .vs-group-header {
637
+ font-weight: 600;
638
+ letter-spacing: 0.3px;
639
+ position: relative;
640
+ z-index: 2;
641
+ }
642
+
643
+ .vs-group-header::after {
644
+ content: "";
645
+ position: absolute;
646
+ right: 0;
647
+ top: 30%;
648
+ bottom: 30%;
649
+ width: 2px;
650
+ background-color: var(--vs-border-color, #d1d5db);
651
+ opacity: 0.6;
652
+ pointer-events: none;
653
+ }
654
+
655
+ .vs-header-cell {
656
+ background: var(--vs-table-header-bg, #fff);
657
+ font-weight: 500;
658
+ white-space: nowrap;
659
+ text-align: left;
660
+ padding: 0.5rem 1rem;
661
+ border-bottom: 1px solid var(--vs-border-color, #e5e7eb);
662
+ }
487
663
  }
488
664
 
665
+ /* Remove double line on last column */
666
+ .vs-table th:last-child,
667
+ .vs-table .vs-group-header:last-child {
668
+ border-right: none;
669
+ }
670
+
671
+ // .vs-table th:last-child::after {
672
+ // display: none;
673
+ // }
674
+
489
675
  /* Sortable Headers */
490
676
  .vs-sortable {
491
677
  cursor: pointer;
@@ -957,28 +1143,6 @@
957
1143
  stroke-width: 2.5;
958
1144
  }
959
1145
 
960
- /* VS Button */
961
- .vs-btn {
962
- padding: 0.35rem 0.75rem;
963
- border-radius: 4px;
964
- cursor: pointer;
965
- }
966
- .vs-btn-primary {
967
- background: #2d6cdf;
968
- color: #fff;
969
- border: none;
970
- }
971
- .vs-btn-secondary {
972
- background: #f5f5f5;
973
- border: 1px solid #ccc;
974
- }
975
- // .vs-filter-range,
976
- // .vs-filter-date {
977
- // display: flex;
978
- // gap: 0.5rem;
979
- // align-items: center;
980
- // }
981
-
982
1146
  .vs-column-filter {
983
1147
  display: inline-flex;
984
1148
  align-items: center;
@@ -1023,111 +1187,69 @@
1023
1187
  }
1024
1188
  }
1025
1189
 
1026
- /* Utility Classes */
1027
- .vs-text-center {
1028
- text-align: center;
1029
- }
1030
- .vs-text-left {
1031
- text-align: left;
1032
- }
1033
- .vs-text-right {
1034
- text-align: right;
1035
- }
1036
- .vs-text-muted {
1037
- color: var(--vs-secondary);
1038
- }
1039
- .vs-text-primary {
1040
- color: var(--vs-primary);
1041
- }
1042
- .vs-text-success {
1043
- color: var(--vs-success);
1044
- }
1045
- .vs-text-danger {
1046
- color: var(--vs-danger);
1047
- }
1048
- .vs-text-warning {
1049
- color: var(--vs-warning);
1190
+ .vs-sticky-left {
1191
+ position: sticky;
1192
+ background: white;
1193
+ z-index: 3;
1194
+ box-shadow: 2px 0 4px rgba(0, 0, 0, 0.05);
1050
1195
  }
1051
1196
 
1052
- .vs-bg-primary {
1053
- background-color: var(--vs-primary);
1054
- }
1055
- .vs-bg-success {
1056
- background-color: var(--vs-success);
1057
- }
1058
- .vs-bg-danger {
1059
- background-color: var(--vs-danger);
1060
- }
1061
- .vs-bg-warning {
1062
- background-color: var(--vs-warning);
1063
- }
1064
- .vs-bg-light {
1065
- background-color: var(--vs-light);
1197
+ .vs-sticky-right {
1198
+ position: sticky;
1199
+ background: white;
1200
+ z-index: 3;
1201
+ box-shadow: -2px 0 4px rgba(0, 0, 0, 0.05);
1066
1202
  }
1067
1203
 
1068
- .vs-border {
1069
- border: 1px solid var(--vs-table-border);
1070
- }
1071
- .vs-border-top {
1072
- border-top: 1px solid var(--vs-table-border);
1073
- }
1074
- .vs-border-bottom {
1075
- border-bottom: 1px solid var(--vs-table-border);
1076
- }
1077
- .vs-border-left {
1078
- border-left: 1px solid var(--vs-table-border);
1079
- }
1080
- .vs-border-right {
1081
- border-right: 1px solid var(--vs-table-border);
1204
+ /* Dynamic shadow indicator */
1205
+ .vs-table-container.has-left-shadow::before,
1206
+ .vs-table-container.has-right-shadow::after {
1207
+ content: '';
1208
+ position: absolute;
1209
+ top: 0;
1210
+ bottom: 0;
1211
+ width: 12px;
1212
+ pointer-events: none;
1213
+ z-index: 3;
1082
1214
  }
1083
1215
 
1084
- .vs-rounded {
1085
- border-radius: var(--vs-border-radius);
1086
- }
1087
- .vs-rounded-sm {
1088
- border-radius: var(--vs-border-radius-sm);
1089
- }
1090
- .vs-rounded-lg {
1091
- border-radius: var(--vs-border-radius-lg);
1216
+ .vs-table-container.has-left-shadow::before {
1217
+ left: 0;
1218
+ box-shadow: inset 6px 0 6px -6px rgba(0, 0, 0, 0.1);
1092
1219
  }
1093
1220
 
1094
- .vs-shadow {
1095
- box-shadow: var(--vs-shadow);
1096
- }
1097
- .vs-shadow-sm {
1098
- box-shadow: var(--vs-shadow-sm);
1099
- }
1100
- .vs-shadow-lg {
1101
- box-shadow: var(--vs-shadow-lg);
1221
+ .vs-table-container.has-right-shadow::after {
1222
+ right: 0;
1223
+ box-shadow: inset -6px 0 6px -6px rgba(0, 0, 0, 0.1);
1102
1224
  }
1103
1225
 
1104
- /* Animation Classes */
1105
- .vs-fade-in {
1106
- animation: vs-fade-in 0.3s ease-in-out;
1226
+ /* Sticky Header */
1227
+ .vs-sticky-header-wrapper {
1228
+ overflow-y: auto;
1229
+ overflow-x: auto;
1230
+ position: relative;
1231
+ max-height: calc(100vh - 250px);
1232
+ scrollbar-gutter: stable;
1233
+ background-color: white;
1107
1234
  }
1108
1235
 
1109
- @keyframes vs-fade-in {
1110
- from {
1111
- opacity: 0;
1112
- transform: translateY(-10px);
1113
- }
1114
- to {
1115
- opacity: 1;
1116
- transform: translateY(0);
1117
- }
1236
+ .vs-sticky-header-wrapper thead {
1237
+ background: var(--vs-table-bg, #fff);
1238
+ position: sticky;
1239
+ top: 0;
1240
+ z-index: 5;
1241
+ transition: transform 0.1s ease-out;
1118
1242
  }
1119
1243
 
1120
- .vs-slide-in {
1121
- animation: vs-slide-in 0.3s ease-in-out;
1244
+ .vs-sticky-header-wrapper thead th {
1245
+ background: var(--vs-table-bg, #fff);
1246
+ position: sticky;
1247
+ top: 0;
1248
+ // z-index: 2;
1122
1249
  }
1123
1250
 
1124
- @keyframes vs-slide-in {
1125
- from {
1126
- transform: translateX(-20px);
1127
- opacity: 0;
1128
- }
1129
- to {
1130
- transform: translateX(0);
1131
- opacity: 1;
1132
- }
1133
- }
1251
+ .vs-group-header.vs-sticky-left,
1252
+ .vs-group-header.vs-sticky-right {
1253
+ background: var(--vs-table-bg, #fff);
1254
+ // z-index: 5 !important;
1255
+ }
@@ -1 +0,0 @@
1
- declare module 'jspdf-autotable';
@@ -1 +0,0 @@
1
- declare module 'jspdf';