1
1
<template >
2
- <div class =" flex flex-1 flex-col" >
3
- <div v-if =" pagination" class =" mb-3 flex items-center justify-end" >
4
- <HoppButtonSecondary
5
- outline
6
- filled
7
- :icon =" IconLeft"
8
- :disabled =" page === 1"
9
- @click =" changePage(PageDirection.Previous)"
10
- />
11
-
12
- <span class =" flex h-full w-10 items-center justify-center" >{{
13
- page
14
- }}</span >
15
-
16
- <HoppButtonSecondary
17
- outline
18
- filled
19
- :icon =" IconRight"
20
- :disabled =" page === pagination.totalPages"
21
- @click =" changePage(PageDirection.Next)"
22
- />
23
- </div >
24
-
25
- <div class =" overflow-auto rounded-md border border-dividerDark shadow-md" >
26
- <!-- An Extension Slot to extend the table functionality such as search -->
27
- <slot name =" extension" ></slot >
28
-
29
- <table class =" w-full table-fixed" >
30
- <thead >
2
+ <div class =" overflow-auto rounded-md border border-dividerDark shadow-md" >
3
+ <!-- An Extension Slot to extend the table functionality such as search -->
4
+ <slot name =" extension" ></slot >
5
+
6
+ <table class =" w-full table-fixed" >
7
+ <thead >
8
+ <tr
9
+ class =" border-b border-dividerDark bg-primaryLight text-left text-sm text-secondary"
10
+ >
11
+ <th v-if =" selectedRows" class =" w-24" >
12
+ <input
13
+ ref =" selectAllCheckbox"
14
+ type =" checkbox"
15
+ :checked =" areAllRowsSelected"
16
+ :disabled =" loading"
17
+ class =" flex h-full w-full items-center justify-center"
18
+ @click.stop =" toggleAllRows"
19
+ />
20
+ </th >
21
+ <slot name =" head" >
22
+ <th
23
+ v-for =" th in headings"
24
+ :key =" th.key"
25
+ scope =" col"
26
+ class =" px-6 py-3"
27
+ >
28
+ {{ th.label ?? th.key }}
29
+ </th >
30
+ </slot >
31
+ </tr >
32
+ </thead >
33
+
34
+ <tbody class =" divide-y divide-divider" >
35
+ <tr v-if =" loading" >
36
+ <slot name =" loading-state" >
37
+ <td :colspan =" columnSpan" >
38
+ <div class =" mx-auto my-3 h-5 w-5 text-center" >
39
+ <HoppSmartSpinner />
40
+ </div >
41
+ </td >
42
+ </slot >
43
+ </tr >
44
+
45
+ <tr v-else-if =" !list.length" >
46
+ <slot name =" empty-state" >
47
+ <td :colspan =" columnSpan" class =" py-3 text-center" >
48
+ <p >No data available</p >
49
+ </td >
50
+ </slot >
51
+ </tr >
52
+
53
+ <template v-else >
31
54
<tr
32
- class =" border-b border-dividerDark bg-primaryLight text-left text-sm text-secondary"
55
+ v-for =" (rowData, rowIndex) in workingList"
56
+ :key =" rowIndex"
57
+ class =" rounded-xl text-secondaryDark hover:cursor-pointer hover:bg-divider"
58
+ :class =" { 'divide-x divide-divider': showYBorder }"
59
+ @click =" onRowClicked(rowData)"
33
60
>
34
- <th v-if =" selectedRows" class = " w-24 " >
61
+ <td v-if =" selectedRows" >
35
62
<input
36
- ref =" selectAllCheckbox"
37
63
type =" checkbox"
38
- :checked =" areAllRowsSelected"
39
- :disabled =" loading"
64
+ :checked =" isRowSelected(rowData)"
40
65
class =" flex h-full w-full items-center justify-center"
41
- @click.stop =" toggleAllRows "
66
+ @click.stop =" toggleRow(rowData) "
42
67
/>
43
- </th >
44
- <slot name =" head " >
45
- <th
46
- v-for =" th in headings"
47
- :key =" th .key"
48
- scope = " col "
49
- class = " px-6 py-3 "
68
+ </td >
69
+ <slot name =" body " :row = " rowData " >
70
+ <td
71
+ v-for =" cellHeading in headings"
72
+ :key =" cellHeading .key"
73
+ class = " px-4 py-2 "
74
+ @click = " !cellHeading.preventClick && onRowClicked(rowData) "
50
75
>
51
- {{ th.label ?? th.key }}
52
- </th >
53
- </slot >
54
- </tr >
55
- </thead >
56
-
57
- <tbody class =" divide-y divide-divider" >
58
- <tr v-if =" loading" >
59
- <slot name =" loading-state" >
60
- <td :colspan =" columnSpan" >
61
- <div class =" mx-auto my-3 h-5 w-5 text-center" >
62
- <HoppSmartSpinner />
63
- </div >
64
- </td >
65
- </slot >
66
- </tr >
67
-
68
- <tr v-else-if =" !list.length" >
69
- <slot name =" empty-state" >
70
- <td :colspan =" columnSpan" class =" py-3 text-center" >
71
- <p >No data available</p >
76
+ <!-- Dynamic column slot -->
77
+ <slot :name =" cellHeading.key" :item =" rowData" >
78
+ <!-- Generic implementation of the column -->
79
+ <div class =" flex flex-col truncate" >
80
+ <span class =" truncate" >
81
+ {{ rowData[cellHeading.key] ?? "-" }}
82
+ </span >
83
+ </div >
84
+ </slot >
72
85
</td >
73
86
</slot >
74
87
</tr >
75
-
76
- <template v-else >
77
- <tr
78
- v-for =" (rowData, rowIndex) in workingList"
79
- :key =" rowIndex"
80
- class =" rounded-xl text-secondaryDark hover:cursor-pointer hover:bg-divider"
81
- :class =" { 'divide-x divide-divider': showYBorder }"
82
- @click =" onRowClicked(rowData)"
83
- >
84
- <td v-if =" selectedRows" >
85
- <input
86
- type =" checkbox"
87
- :checked =" isRowSelected(rowData)"
88
- class =" flex h-full w-full items-center justify-center"
89
- @click.stop =" toggleRow(rowData)"
90
- />
91
- </td >
92
- <slot name =" body" :row =" rowData" >
93
- <td
94
- v-for =" cellHeading in headings"
95
- :key =" cellHeading.key"
96
- class =" px-4 py-2"
97
- @click =" !cellHeading.preventClick && onRowClicked(rowData)"
98
- >
99
- <!-- Dynamic column slot -->
100
- <slot :name =" cellHeading.key" :item =" rowData" >
101
- <!-- Generic implementation of the column -->
102
- <div class =" flex flex-col truncate" >
103
- <span class =" truncate" >
104
- {{ rowData[cellHeading.key] ?? "-" }}
105
- </span >
106
- </div >
107
- </slot >
108
- </td >
109
- </slot >
110
- </tr >
111
- </template >
112
- </tbody >
113
- </table >
114
- </div >
88
+ </template >
89
+ </tbody >
90
+ </table >
115
91
</div >
116
92
</template >
117
93
118
94
<script lang="ts" setup>
119
95
import { useVModel } from " @vueuse/core"
120
96
import { isEqual } from " lodash-es"
121
- import { computed , ref , watch } from " vue"
122
-
123
- import IconLeft from " ~icons/lucide/arrow-left"
124
- import IconRight from " ~icons/lucide/arrow-right"
125
-
97
+ import { computed , ref , watch , watchEffect } from " vue"
126
98
import { HoppSmartSpinner } from " .."
127
- import { HoppButtonSecondary } from " ../button"
128
99
129
100
export type CellHeading = {
130
101
key: string
@@ -143,19 +114,16 @@ const props = withDefaults(
143
114
/** The headings of the table */
144
115
headings? : CellHeading []
145
116
117
+ /** Contains the rows selected */
146
118
selectedRows? : Item []
119
+
147
120
/** Whether to enable sorting */
148
121
sort? : {
149
122
/** The key to sort the list by */
150
123
key: string
151
124
direction: Direction
152
125
}
153
126
154
- /** Whether to enable pagination */
155
- pagination? : {
156
- totalPages: number
157
- }
158
-
159
127
/** Whether to show a loading spinner */
160
128
loading? : boolean
161
129
}>(),
@@ -171,31 +139,8 @@ const emit = defineEmits<{
171
139
(event : " onRowClicked" , item : Item ): void
172
140
(event : " update:list" , list : Item []): void
173
141
(event : " update:selectedRows" , selectedRows : Item []): void
174
- (event : " pageNumber" , page : number ): void
175
142
}>()
176
143
177
- // Pagination functionality
178
- const page = ref (1 )
179
-
180
- enum PageDirection {
181
- Previous ,
182
- Next ,
183
- }
184
-
185
- const changePage = (direction : PageDirection ) => {
186
- const isPrevious = direction === PageDirection .Previous
187
-
188
- const isValidPreviousAction = isPrevious && page .value > 1
189
- const isValidNextAction =
190
- ! isPrevious && page .value < props .pagination ! .totalPages
191
-
192
- if (isValidNextAction || isValidPreviousAction ) {
193
- page .value += isPrevious ? - 1 : 1
194
-
195
- emit (" pageNumber" , page .value )
196
- }
197
- }
198
-
199
144
// The working version of the list that is used to perform operations upon
200
145
const workingList = useVModel (props , " list" , emit )
201
146
@@ -259,6 +204,14 @@ const areAllRowsSelected = computed(() => {
259
204
})
260
205
})
261
206
207
+ watchEffect (() => {
208
+ if (selectedRows .value ?.length === 0 ) {
209
+ workingList .value .forEach ((item ) => {
210
+ item .selected = false
211
+ })
212
+ }
213
+ })
214
+
262
215
// Sort List by key and direction which can set to ascending or descending
263
216
export type Direction = " ascending" | " descending"
264
217
0 commit comments