edvoyui-component-library-test-flight 0.0.61 → 0.0.63
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/dist/library-vue-ts.cjs.js +18 -18
- package/dist/library-vue-ts.css +1 -1
- package/dist/library-vue-ts.es.js +7496 -7068
- package/dist/library-vue-ts.umd.js +19 -19
- package/dist/table/UCheckbox.vue.d.ts +5 -0
- package/dist/table/UCheckbox.vue.d.ts.map +1 -0
- package/dist/table/UTable.vue.d.ts +4 -0
- package/dist/table/UTable.vue.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/components/index.ts +2 -0
- package/src/components/table/UTable.scss +73 -0
- package/src/components/table/UTable.vue +506 -470
- package/src/components/table/UTableview.vue +1 -0
|
@@ -1,489 +1,525 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
<
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
2
|
+
<div>
|
|
3
|
+
<Transition name="fade" mode="out-in">
|
|
4
|
+
<div
|
|
5
|
+
v-if="loading"
|
|
6
|
+
:class="[
|
|
7
|
+
'overflow-hidden relative z-0 isolate bg-white backdrop-blur transition-colors duration-150 ease-in-out rounded-xl border border-gray-50',
|
|
8
|
+
tableHeight
|
|
9
|
+
? tableHeight
|
|
10
|
+
: 'h-[calc(100svh-9rem)] max-h-[calc(100svh-9rem)]',
|
|
11
|
+
]"
|
|
12
|
+
>
|
|
13
|
+
<div
|
|
14
|
+
class="absolute flex items-center z-[calc(infinity)] w-full h-auto pointer-events-none inset-0"
|
|
15
|
+
>
|
|
16
|
+
<EUICircleLoader />
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
<div v-else class="relative max-w-screen-xl mx-auto overflow-hidden">
|
|
20
|
+
<div
|
|
21
|
+
id="euitable-comp"
|
|
22
|
+
:class="[
|
|
23
|
+
'ui-table scrollbar--thin overscroll-none',
|
|
24
|
+
computedItems.length === 0
|
|
25
|
+
? 'overflow-hidden pointer-events-none'
|
|
26
|
+
: 'overflow-auto',
|
|
27
|
+
tableHeight
|
|
28
|
+
? tableHeight
|
|
29
|
+
: 'h-[calc(100svh-12rem)] max-h-[calc(100svh-12rem)]',
|
|
30
|
+
]"
|
|
31
|
+
ref="tableContainer"
|
|
32
|
+
@scroll="handleScroll"
|
|
33
|
+
>
|
|
34
|
+
<table class="table">
|
|
35
|
+
<thead>
|
|
36
|
+
<tr>
|
|
37
|
+
<th
|
|
38
|
+
v-if="checkable"
|
|
39
|
+
class="checkable"
|
|
40
|
+
style="
|
|
41
|
+
width: 45px;
|
|
42
|
+
flex: 45 0 auto;
|
|
43
|
+
max-width: 45px;
|
|
44
|
+
background-color: rgb(243 244 246);
|
|
45
|
+
"
|
|
46
|
+
scope="col"
|
|
47
|
+
>
|
|
48
|
+
<UCheckbox
|
|
49
|
+
:checked="isAllChecked"
|
|
50
|
+
:indeterminate="isIndeterminate"
|
|
51
|
+
:disabled="isAllUncheckable"
|
|
52
|
+
class="flex justify-center mt-0"
|
|
53
|
+
@change="checkAll"
|
|
54
|
+
/>
|
|
55
|
+
</th>
|
|
56
|
+
<th
|
|
57
|
+
v-for="(header, headerIndex) in headers"
|
|
58
|
+
:key="headerIndex"
|
|
59
|
+
scope="col"
|
|
60
|
+
:class="[
|
|
61
|
+
'snap-start snap-always',
|
|
62
|
+
isScrolled && headerIndex === 0 ? stickyClass.head : '',
|
|
63
|
+
{
|
|
64
|
+
'cursor-pointer hover:!text-gray-900': header?.sortable,
|
|
65
|
+
'!bg-gray-200': currentSort === header.value,
|
|
66
|
+
},
|
|
67
|
+
]"
|
|
68
|
+
:style="headerStyle(header)"
|
|
69
|
+
@click="sortBy(header, $event)"
|
|
70
|
+
>
|
|
71
|
+
<div
|
|
72
|
+
class="flex-1 w-full text-sm font-medium text-current truncate font-inter"
|
|
73
|
+
>
|
|
74
|
+
<slot name="header" :header="header">
|
|
75
|
+
{{ capitalizeText(header?.text ?? header?.name ?? "") }}
|
|
76
|
+
</slot>
|
|
77
|
+
<slot
|
|
78
|
+
v-if="headerOptional"
|
|
79
|
+
name="headerOptionalItem"
|
|
80
|
+
></slot>
|
|
81
|
+
</div>
|
|
82
|
+
<div v-if="header?.sortable" class="flex-none size-6">
|
|
83
|
+
<svg
|
|
84
|
+
width="24"
|
|
85
|
+
height="24"
|
|
86
|
+
viewBox="0 0 24 24"
|
|
87
|
+
fill="none"
|
|
88
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
89
|
+
class="size-6"
|
|
90
|
+
>
|
|
91
|
+
<rect width="24" height="24" fill="none" />
|
|
92
|
+
<path
|
|
93
|
+
d="M8 10C9.06206 8.5381 10.3071 7.2287 11.7021 6.1058C11.8774 5.9647 12.1226 5.9647 12.2979 6.1058C13.6929 7.2287 14.9379 8.5381 16 10"
|
|
94
|
+
:stroke="
|
|
95
|
+
currentSortDir === 'asc' &&
|
|
96
|
+
currentSort === header?.value
|
|
97
|
+
? '#111827'
|
|
98
|
+
: '#9ca3af'
|
|
99
|
+
"
|
|
100
|
+
stroke-opacity="0.8"
|
|
101
|
+
stroke-width="2"
|
|
102
|
+
stroke-linecap="round"
|
|
103
|
+
stroke-linejoin="round"
|
|
104
|
+
/>
|
|
105
|
+
<path
|
|
106
|
+
d="M8 14C9.06206 15.4619 10.3071 16.7713 11.7021 17.8942C11.8774 18.0353 12.1226 18.0353 12.2979 17.8942C13.6929 16.7713 14.9379 15.4619 16 14"
|
|
107
|
+
:stroke="
|
|
108
|
+
currentSortDir === 'desc' &&
|
|
109
|
+
currentSort === header?.value
|
|
110
|
+
? '#111827'
|
|
111
|
+
: '#9ca3af'
|
|
112
|
+
"
|
|
113
|
+
stroke-opacity="0.8"
|
|
114
|
+
stroke-width="2"
|
|
115
|
+
stroke-linecap="round"
|
|
116
|
+
stroke-linejoin="round"
|
|
117
|
+
/>
|
|
118
|
+
</svg>
|
|
119
|
+
</div>
|
|
120
|
+
</th>
|
|
121
|
+
</tr>
|
|
122
|
+
</thead>
|
|
123
|
+
<tbody>
|
|
124
|
+
<template
|
|
125
|
+
v-if="computedItems.length > 0"
|
|
126
|
+
v-for="(row, rowIndex) in computedItems"
|
|
127
|
+
:key="`table-row-${rowIndex}`"
|
|
128
|
+
>
|
|
129
|
+
<tr
|
|
130
|
+
@mouseenter="
|
|
131
|
+
$attrs.mouseenter
|
|
132
|
+
? $emit('mouseenter', row, rowIndex)
|
|
133
|
+
: null
|
|
134
|
+
"
|
|
135
|
+
@mouseleave="
|
|
136
|
+
$attrs.mouseleave
|
|
137
|
+
? $emit('mouseleave', row, rowIndex)
|
|
138
|
+
: null
|
|
139
|
+
"
|
|
140
|
+
class="group"
|
|
141
|
+
>
|
|
142
|
+
<td
|
|
143
|
+
v-if="checkable"
|
|
144
|
+
class="checkable"
|
|
145
|
+
style="width: 45px; flex: 45 0 auto; max-width: 45px"
|
|
146
|
+
>
|
|
147
|
+
<UCheckbox
|
|
148
|
+
:disabled="!isRowCheckable(row)"
|
|
149
|
+
:checked="isRowChecked(row)"
|
|
150
|
+
class="flex justify-center mt-0"
|
|
151
|
+
@change.prevent.stop="
|
|
152
|
+
($event) => checkRow(row, rowIndex, $event)
|
|
153
|
+
"
|
|
154
|
+
/>
|
|
155
|
+
</td>
|
|
156
|
+
<td
|
|
157
|
+
v-for="(header, headerIndex) in headers"
|
|
158
|
+
:key="headerIndex"
|
|
159
|
+
:style="bodyStyle(header)"
|
|
160
|
+
:class="[
|
|
161
|
+
isScrolled && headerIndex === 0 ? stickyClass.body : '',
|
|
162
|
+
]"
|
|
163
|
+
>
|
|
164
|
+
<slot
|
|
165
|
+
:name="`item.${header?.value}`"
|
|
166
|
+
:row="row"
|
|
167
|
+
:rowIndex="rowIndex"
|
|
168
|
+
:headerIndex="headerIndex"
|
|
169
|
+
>
|
|
170
|
+
{{ getValueByPath(row, header?.value) }}
|
|
171
|
+
</slot>
|
|
172
|
+
</td>
|
|
173
|
+
</tr>
|
|
174
|
+
<template v-if="tableExpanded">
|
|
175
|
+
<slot name="expanded" :row="row" :rowIndex="rowIndex"></slot>
|
|
176
|
+
</template>
|
|
177
|
+
</template>
|
|
178
|
+
<template v-else-if="computedItems.length === 0">
|
|
179
|
+
<tr class="norecords">
|
|
180
|
+
<td
|
|
181
|
+
:colspan="
|
|
182
|
+
checkable === true ? headers.length + 1 : headers.length
|
|
183
|
+
"
|
|
184
|
+
>
|
|
185
|
+
<div
|
|
186
|
+
class="flex items-center justify-center text-xl font-medium text-gray-500 h-[calc(100svh-18rem)] max-w-screen-xl border"
|
|
187
|
+
>
|
|
188
|
+
No matching records found
|
|
189
|
+
</div>
|
|
190
|
+
</td>
|
|
191
|
+
</tr>
|
|
192
|
+
</template>
|
|
193
|
+
</tbody>
|
|
194
|
+
</table>
|
|
195
|
+
</div>
|
|
196
|
+
<div
|
|
197
|
+
class="sticky bottom-0 left-0 z-50 flex items-center justify-between px-2 py-1 bg-white border-t border-gray-300"
|
|
198
|
+
>
|
|
199
|
+
<slot name="tableCount">
|
|
200
|
+
<div class="inline-flex items-center gap-x-2">
|
|
201
|
+
<div class="text-sm font-medium text-gray-900">
|
|
202
|
+
Total {{ total }}
|
|
203
|
+
</div>
|
|
204
|
+
<span class="text-gray-300">|</span>
|
|
205
|
+
<div class="inline-flex items-center">
|
|
206
|
+
<span class="text-sm font-medium text-gray-900">Per page</span>
|
|
207
|
+
<EUIPageLimit
|
|
208
|
+
:page-limit="limit"
|
|
209
|
+
@update-limit="changeLimit($event)"
|
|
210
|
+
@refetch="searchData($event)"
|
|
211
|
+
:iconStyle="true"
|
|
212
|
+
/>
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
215
|
+
</slot>
|
|
216
|
+
<template v-if="paginated && computedItems.length !== 0">
|
|
217
|
+
<slot name="tablepagination">
|
|
218
|
+
<EUIStudentPagination
|
|
219
|
+
:activePage="newCurrentPage"
|
|
220
|
+
:pageLimit="limit"
|
|
221
|
+
:totalCount="total"
|
|
222
|
+
@change-page="pageChanged($event)"
|
|
16
223
|
/>
|
|
17
|
-
</th>
|
|
18
|
-
<th
|
|
19
|
-
v-for="(header, headerIndex) in headers"
|
|
20
|
-
:key="headerIndex"
|
|
21
|
-
:class="[
|
|
22
|
-
{
|
|
23
|
-
'ui-table__sortable': header.sortable,
|
|
24
|
-
'ui-table__sortable--active': currentSort === header.value
|
|
25
|
-
},
|
|
26
|
-
sortClass(header)
|
|
27
|
-
]"
|
|
28
|
-
:style="headerStyle(header)"
|
|
29
|
-
@click="sortBy(header)"
|
|
30
|
-
>
|
|
31
|
-
|
|
32
|
-
<slot name="header" :header="header">
|
|
33
|
-
{{ capitalizeText(header?.text ?? header?.name ?? '') }}
|
|
34
224
|
</slot>
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
</tr>
|
|
40
|
-
</thead>
|
|
41
|
-
<tbody>
|
|
42
|
-
<tr v-for="(row, rowIndex) in computedItems" :key="rowIndex" class="group">
|
|
43
|
-
<td v-if="checkable" style="width: 45px; flex: 45 0 auto; max-width: 45px">
|
|
44
|
-
<UCheckbox
|
|
45
|
-
:disabled="!isRowCheckable(row)"
|
|
46
|
-
:checked="isRowChecked(row)"
|
|
47
|
-
class="flex justify-center mt-0"
|
|
48
|
-
@change.prevent.stop="($event) => checkRow(row, rowIndex, $event)"
|
|
49
|
-
/>
|
|
50
|
-
</td>
|
|
51
|
-
<td
|
|
52
|
-
v-for="(header, headerIndex) in headers"
|
|
53
|
-
:key="headerIndex"
|
|
54
|
-
:style="bodyStyle(header)"
|
|
55
|
-
>
|
|
56
|
-
<span class="ui-table__truncate">
|
|
57
|
-
<slot :name="`item.${header.value}`" :row="row" :rowIndex="rowIndex">
|
|
58
|
-
{{ getValueByPath(row, header.value) }}
|
|
59
|
-
</slot>
|
|
60
|
-
</span>
|
|
61
|
-
</td>
|
|
62
|
-
</tr>
|
|
63
|
-
<slot v-if="tableExpanded" name="expanded" :row="row" :rowIndex="rowIndex"></slot>
|
|
64
|
-
</tbody>
|
|
65
|
-
<tbody v-if="computedItems.length === 0">
|
|
66
|
-
<tr>
|
|
67
|
-
<td :colspan="headers.length" class="ui-table__no-result">
|
|
68
|
-
No matching records found
|
|
69
|
-
</td>
|
|
70
|
-
</tr>
|
|
71
|
-
</tbody>
|
|
72
|
-
</table>
|
|
73
|
-
</div>
|
|
74
|
-
<div :class="['ui-table__footer', footerClass]">
|
|
75
|
-
<slot name="table-bottom-left">
|
|
76
|
-
<div></div>
|
|
77
|
-
</slot>
|
|
78
|
-
<!-- v-if="paginated && computedItems.length !== 0" -->
|
|
79
|
-
<EUIPagination
|
|
80
|
-
:per-page="perPage"
|
|
81
|
-
:total="itemsLength"
|
|
82
|
-
:data-cy="dataCy"
|
|
83
|
-
:current="newCurrentPage"
|
|
84
|
-
:hide-number="hidePageNumber"
|
|
85
|
-
@change="pageChanged"
|
|
86
|
-
/>
|
|
87
|
-
</div>
|
|
225
|
+
</template>
|
|
226
|
+
</div>
|
|
227
|
+
</div>
|
|
228
|
+
</Transition>
|
|
88
229
|
</div>
|
|
89
230
|
</template>
|
|
90
231
|
|
|
91
|
-
<script>
|
|
92
|
-
import {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
232
|
+
<script lang="ts" setup>
|
|
233
|
+
import {
|
|
234
|
+
ref,
|
|
235
|
+
computed,
|
|
236
|
+
watch,
|
|
237
|
+
toRefs,
|
|
238
|
+
PropType,
|
|
239
|
+
onMounted,
|
|
240
|
+
onUnmounted,
|
|
241
|
+
} from "vue";
|
|
242
|
+
import { getValueByPath, indexOf, defaultFilter } from "../../utils/helpers";
|
|
243
|
+
import UCheckbox from "./UCheckbox.vue";
|
|
244
|
+
import { capitalizeText } from "../../utils/lodash";
|
|
245
|
+
import EUIStudentPagination from "./EUIStudentPagination.vue";
|
|
246
|
+
import EUICircleLoader from "../loader/EUICircleLoader.vue";
|
|
247
|
+
import EUIPageLimit from "./EUIPageLimit.vue";
|
|
248
|
+
import "./UTable.scss";
|
|
249
|
+
|
|
250
|
+
interface Header {
|
|
251
|
+
value: string;
|
|
252
|
+
text?: string;
|
|
253
|
+
name?: string;
|
|
254
|
+
sortable?: boolean;
|
|
255
|
+
width?: number;
|
|
256
|
+
color?: string;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
interface Item {
|
|
260
|
+
[key: string]: any;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const props = defineProps({
|
|
264
|
+
items: { type: Array as PropType<Item[]>, default: () => [], required: true },
|
|
265
|
+
headers: {
|
|
266
|
+
type: Array as PropType<Header[]>,
|
|
267
|
+
default: () => [],
|
|
268
|
+
required: true,
|
|
269
|
+
},
|
|
270
|
+
paginated: { type: Boolean, default: false },
|
|
271
|
+
tableExpanded: { type: Boolean, default: false },
|
|
272
|
+
search: { type: String, default: "" },
|
|
273
|
+
perPage: { type: Number, default: 5 },
|
|
274
|
+
total: { type: Number, default: 0 },
|
|
275
|
+
currentPage: { type: Number, default: 1 },
|
|
276
|
+
defaultSort: { type: String, default: "" },
|
|
277
|
+
defaultSortDirection: { type: String, default: "asc" },
|
|
278
|
+
headerOptional: { type: Boolean, default: false },
|
|
279
|
+
checkedRows: { type: Array as PropType<Item[]>, default: () => [] },
|
|
280
|
+
checkable: { type: Boolean, default: false },
|
|
281
|
+
isRowCheckable: {
|
|
282
|
+
type: Function as PropType<(row: Item) => boolean>,
|
|
283
|
+
default: () => true,
|
|
284
|
+
},
|
|
285
|
+
stickyColumn: { type: Boolean, default: true },
|
|
286
|
+
tableHeight: {
|
|
287
|
+
type: String,
|
|
288
|
+
required: false,
|
|
289
|
+
default: "",
|
|
125
290
|
},
|
|
291
|
+
tableLoading: { type: Boolean, default: false },
|
|
292
|
+
backendPagination: Boolean,
|
|
293
|
+
});
|
|
126
294
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
const sortItems = items => {
|
|
180
|
-
return items.sort((a, b) => {
|
|
181
|
-
const aValue = getValueByPath(a, currentSort.value);
|
|
182
|
-
const bValue = getValueByPath(b, currentSort.value);
|
|
183
|
-
return currentSortDir.value === 'asc' ? aValue - bValue : bValue - aValue;
|
|
184
|
-
});
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
const paginateItems = items => {
|
|
188
|
-
const start = (newCurrentPage.value - 1) * props.perPage;
|
|
189
|
-
const end = start + props.perPage;
|
|
190
|
-
return items.slice(start, end);
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
const isRowChecked = row => {
|
|
194
|
-
return newCheckedRows.value.includes(row);
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
const sortClass = () => {
|
|
198
|
-
return header => {
|
|
199
|
-
return this.currentSortDir === "asc" &&
|
|
200
|
-
this.currentSort === header.value
|
|
201
|
-
? "ui-table__sortable--asc"
|
|
202
|
-
: this.currentSortDir === "desc" && this.currentSort === header.value
|
|
203
|
-
? "ui-table__sortable--dsc"
|
|
204
|
-
: ""
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const isAllChecked = computed(() => {
|
|
209
|
-
return (
|
|
210
|
-
computedItems.value.length > 0 &&
|
|
211
|
-
computedItems.value.every(row => newCheckedRows.value.includes(row))
|
|
212
|
-
);
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
const isIndeterminate = computed(() => {
|
|
216
|
-
const checkedCount = computedItems.value.filter(row =>
|
|
217
|
-
newCheckedRows.value.includes(row)
|
|
218
|
-
).length;
|
|
219
|
-
return checkedCount > 0 && checkedCount < computedItems.value.length;
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
const checkAll = () => {
|
|
223
|
-
if (isAllChecked.value) {
|
|
224
|
-
// Uncheck all rows
|
|
225
|
-
newCheckedRows.value = []
|
|
226
|
-
} else {
|
|
227
|
-
// Check all rows
|
|
228
|
-
const rowsToCheck = computedItems.value.filter(
|
|
229
|
-
row => !newCheckedRows.value.includes(row)
|
|
230
|
-
);
|
|
231
|
-
newCheckedRows.value.push(...rowsToCheck);
|
|
232
|
-
}
|
|
233
|
-
emit('check', newCheckedRows.value);
|
|
234
|
-
emit("check-all", newCheckedRows.value);
|
|
235
|
-
emit("update:checkedRows", newCheckedRows.value);
|
|
236
|
-
emit("update:selectedRows", newCheckedRows.value);
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
const removeCheckedRow = (row) => {
|
|
240
|
-
const index = indexOf(newCheckedRows.value, row);
|
|
241
|
-
if (index >= 0) {
|
|
242
|
-
newCheckedRows.value.splice(index, 1);
|
|
243
|
-
}
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
const checkRow = (row, index, event) => {
|
|
247
|
-
const isChecked = newCheckedRows.value.includes(row);
|
|
248
|
-
if (event.target && isChecked) {
|
|
249
|
-
// Remove row from checkedRows
|
|
250
|
-
removeCheckedRow(row);
|
|
251
|
-
}
|
|
252
|
-
else {
|
|
253
|
-
newCheckedRows.value.push(row);
|
|
254
|
-
};
|
|
255
|
-
emit('check', newCheckedRows.value);
|
|
256
|
-
emit("update:checkedRows", newCheckedRows.value);
|
|
257
|
-
emit("update:selectedRows", newCheckedRows.value);
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
watch(() => props.currentPage, newPage => (newCurrentPage.value = newPage));
|
|
261
|
-
watch(() => props.checkedRows, rows => (newCheckedRows.value = [...rows]));
|
|
262
|
-
|
|
263
|
-
return {
|
|
264
|
-
tableClasses,
|
|
265
|
-
computedItems,
|
|
266
|
-
headerStyle,
|
|
267
|
-
bodyStyle,
|
|
268
|
-
pageChanged,
|
|
269
|
-
checkRow,
|
|
270
|
-
checkAll,
|
|
271
|
-
itemsLength,
|
|
272
|
-
sortClass,
|
|
273
|
-
isRowChecked,
|
|
274
|
-
isAllChecked,
|
|
275
|
-
isIndeterminate,
|
|
276
|
-
getValueByPath,
|
|
277
|
-
capitalizeText
|
|
278
|
-
};
|
|
295
|
+
const {
|
|
296
|
+
defaultSort,
|
|
297
|
+
defaultSortDirection,
|
|
298
|
+
checkedRows,
|
|
299
|
+
currentPage,
|
|
300
|
+
items,
|
|
301
|
+
backendPagination,
|
|
302
|
+
total,
|
|
303
|
+
search,
|
|
304
|
+
headers,
|
|
305
|
+
paginated,
|
|
306
|
+
isRowCheckable,
|
|
307
|
+
} = toRefs(props);
|
|
308
|
+
|
|
309
|
+
// Emits used
|
|
310
|
+
const emit = defineEmits([
|
|
311
|
+
"update:currentPage",
|
|
312
|
+
"changePage",
|
|
313
|
+
"sort",
|
|
314
|
+
"check",
|
|
315
|
+
"check-all",
|
|
316
|
+
"update:checkedRows",
|
|
317
|
+
"update:selectedRows",
|
|
318
|
+
"changeLimit",
|
|
319
|
+
"mouseenter",
|
|
320
|
+
"mouseleave",
|
|
321
|
+
]);
|
|
322
|
+
|
|
323
|
+
const currentSort = ref(defaultSort.value);
|
|
324
|
+
const currentSortDir = ref(defaultSortDirection.value);
|
|
325
|
+
const newCheckedRows = ref([...checkedRows.value]);
|
|
326
|
+
const newCurrentPage = ref(currentPage.value);
|
|
327
|
+
const limit = ref(props.perPage);
|
|
328
|
+
const loading = computed(() => props.tableLoading);
|
|
329
|
+
|
|
330
|
+
// Computed Property
|
|
331
|
+
|
|
332
|
+
const filteredItems = computed(() => {
|
|
333
|
+
let filteredItems = items.value.slice();
|
|
334
|
+
if (!backendPagination.value && search.value !== "") {
|
|
335
|
+
if (search.value.trim() === "") return filteredItems;
|
|
336
|
+
const props = headers.value.map((h: any) => h.value);
|
|
337
|
+
filteredItems = items.value.filter((row) =>
|
|
338
|
+
props.some((prop) =>
|
|
339
|
+
defaultFilter(
|
|
340
|
+
getValueByPath(row, prop),
|
|
341
|
+
search.value.toString().toLowerCase()
|
|
342
|
+
)
|
|
343
|
+
)
|
|
344
|
+
);
|
|
279
345
|
}
|
|
346
|
+
return filteredItems;
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
const computedItems = computed(() => {
|
|
350
|
+
let items = filteredItems.value;
|
|
351
|
+
// Sort items before slicing for pagination
|
|
352
|
+
items.sort((a: any, b: any) => {
|
|
353
|
+
const modifier = currentSortDir.value === "desc" ? -1 : 1;
|
|
354
|
+
if (a[currentSort.value] < b[currentSort.value]) return -1 * modifier;
|
|
355
|
+
if (a[currentSort.value] > b[currentSort.value]) return 1 * modifier;
|
|
356
|
+
return 0;
|
|
357
|
+
});
|
|
358
|
+
return items;
|
|
359
|
+
|
|
360
|
+
// Apply pagination
|
|
361
|
+
// const start = (newCurrentPage.value - 0) * limit.value;
|
|
362
|
+
// const end = start + limit.value;
|
|
363
|
+
// return items.slice(start, end);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
const headerStyle = (header: Header) => ({
|
|
367
|
+
width: `${header.width || 300}px`,
|
|
368
|
+
flex: `${header.width || 300} 0 auto`,
|
|
369
|
+
maxWidth: `${header.width || 300}px`,
|
|
370
|
+
backgroundColor: header.color || "rgb(243 244 246)",
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
const bodyStyle = (header: Header) => ({
|
|
374
|
+
width: `${header.width || 300}px`,
|
|
375
|
+
flex: `${header.width || 300} 0 auto`,
|
|
376
|
+
maxWidth: `${header.width || 300}px`,
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
const searchData = (_data: any) => {
|
|
380
|
+
// console.log(_data);
|
|
280
381
|
};
|
|
281
|
-
</script>
|
|
282
382
|
|
|
283
|
-
|
|
284
|
-
.
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
.
|
|
299
|
-
|
|
300
|
-
border-spacing: 0;
|
|
301
|
-
thead {
|
|
302
|
-
tr {
|
|
303
|
-
@apply flex flex-grow;
|
|
304
|
-
th {
|
|
305
|
-
@apply bg-gray-50 px-3 py-4 text-sm text-gray-600 text-left font-semibold uppercase border border-l-0 border-solid border-neutral-200 cursor-auto whitespace-nowrap flex items-center last-of-type:rounded-tr-lg first-of-type:rounded-tl-lg first:border-l last:border-r;
|
|
306
|
-
height: 45px;
|
|
307
|
-
transition: all 0.15s ease-out;
|
|
308
|
-
&:last-of-type {
|
|
309
|
-
max-width: unset !important;
|
|
310
|
-
}
|
|
311
|
-
&__sortable {
|
|
312
|
-
@apply cursor-pointer pr-12;
|
|
313
|
-
&--active {
|
|
314
|
-
@apply bg-purple-50;
|
|
315
|
-
}
|
|
316
|
-
&::before,
|
|
317
|
-
&::after {
|
|
318
|
-
@apply absolute w-0 h-0;
|
|
319
|
-
content: "";
|
|
320
|
-
right: 20px;
|
|
321
|
-
border-left: 4px solid transparent;
|
|
322
|
-
border-right: 4px solid transparent;
|
|
323
|
-
}
|
|
324
|
-
&::before {
|
|
325
|
-
top: 13px;
|
|
326
|
-
border-bottom: 6px solid theme('colors.gray.600');
|
|
327
|
-
}
|
|
328
|
-
&::after {
|
|
329
|
-
top: 22px;
|
|
330
|
-
border-top: 6px solid theme("colors.gray.500");
|
|
331
|
-
}
|
|
332
|
-
&--asc {
|
|
333
|
-
&::before {
|
|
334
|
-
border-bottom: 6px solid theme("colors.purple.300");
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
&--dsc {
|
|
338
|
-
&::after {
|
|
339
|
-
border-top: 6px solid theme("colors.purple.300");
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
tbody {
|
|
347
|
-
tr {
|
|
348
|
-
@apply flex flex-grow transition-all duration-100 ease-in-out;
|
|
349
|
-
&:hover {
|
|
350
|
-
transition: all 0.15s ease-out;
|
|
351
|
-
@apply bg-gray-50;
|
|
352
|
-
td {
|
|
353
|
-
@apply border-purple-500 border-t first:rounded-l last:rounded-r border-r-gray-200 last:border-r-purple-500;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
td {
|
|
357
|
-
@apply relative text-sm text-gray-900 border-b border-solid break-words flex items-center border-0 first:border-l border-r border-gray-200 py-1.5 px-3 h-12;
|
|
358
|
-
&:last-of-type {
|
|
359
|
-
max-width: unset !important;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
&:last-child {
|
|
363
|
-
td {
|
|
364
|
-
@apply border-b-0;
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
&__truncate {
|
|
371
|
-
@apply truncate w-full;
|
|
372
|
-
}
|
|
373
|
-
&--sticky-column {
|
|
374
|
-
.table {
|
|
375
|
-
thead {
|
|
376
|
-
tr {
|
|
377
|
-
th {
|
|
378
|
-
&:first-child {
|
|
379
|
-
@apply sticky left-0 z-20;
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
tbody {
|
|
385
|
-
tr {
|
|
386
|
-
td {
|
|
387
|
-
&:first-child {
|
|
388
|
-
@apply sticky left-0 border-r bg-white;
|
|
389
|
-
z-index: 2;
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
&-with-checkbox {
|
|
396
|
-
.table {
|
|
397
|
-
thead {
|
|
398
|
-
tr {
|
|
399
|
-
th {
|
|
400
|
-
&:nth-child(2) {
|
|
401
|
-
@apply sticky z-20;
|
|
402
|
-
left: 45px;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
tbody {
|
|
408
|
-
tr {
|
|
409
|
-
td {
|
|
410
|
-
&:nth-child(2) {
|
|
411
|
-
@apply sticky border-r bg-white;
|
|
412
|
-
left: 45px;
|
|
413
|
-
z-index: 2;
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
&-two {
|
|
421
|
-
.table {
|
|
422
|
-
thead {
|
|
423
|
-
tr {
|
|
424
|
-
th {
|
|
425
|
-
&:nth-child(2) {
|
|
426
|
-
@apply sticky z-20;
|
|
427
|
-
left: 300px;
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
tbody {
|
|
433
|
-
tr {
|
|
434
|
-
td {
|
|
435
|
-
&:nth-child(2) {
|
|
436
|
-
@apply sticky border-r bg-white;
|
|
437
|
-
left: 300px;
|
|
438
|
-
z-index: 2;
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
}
|
|
383
|
+
const changeLimit = (limitData: number) => {
|
|
384
|
+
limit.value = limitData;
|
|
385
|
+
newCurrentPage.value = 1;
|
|
386
|
+
emit("update:currentPage", newCurrentPage.value);
|
|
387
|
+
emit("changeLimit", limitData);
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
const pageChanged = (page: number) => {
|
|
391
|
+
newCurrentPage.value = page > 0 ? page : 0;
|
|
392
|
+
emit("update:currentPage", newCurrentPage.value);
|
|
393
|
+
emit("changePage", page);
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
const sortBy = (header: any, event: any) => {
|
|
397
|
+
if (!header || !header.sortable) return;
|
|
398
|
+
if (header.value === currentSort.value) {
|
|
399
|
+
currentSortDir.value = currentSortDir.value === "asc" ? "desc" : "asc";
|
|
445
400
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
401
|
+
currentSort.value = header.value;
|
|
402
|
+
emit("sort", currentSort.value, currentSortDir.value, event);
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
const isAllChecked = computed(() => {
|
|
406
|
+
return (
|
|
407
|
+
computedItems.value.length > 0 &&
|
|
408
|
+
computedItems.value.every((row) => newCheckedRows.value.includes(row))
|
|
409
|
+
);
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
const isAllUncheckable = computed(() => {
|
|
413
|
+
const validVisibleData = computedItems.value?.filter((row) =>
|
|
414
|
+
isRowCheckable.value!(row)
|
|
415
|
+
);
|
|
416
|
+
return validVisibleData.length === 0;
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
const isIndeterminate = computed(() => {
|
|
420
|
+
const validVisibleData = computedItems.value.filter((row) =>
|
|
421
|
+
isRowCheckable.value(row)
|
|
422
|
+
);
|
|
423
|
+
return (
|
|
424
|
+
newCheckedRows.value.length > 0 &&
|
|
425
|
+
newCheckedRows.value.length < validVisibleData.length
|
|
426
|
+
);
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
const isRowChecked = (row: any) => {
|
|
430
|
+
return indexOf(newCheckedRows.value, row) >= 0;
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
const removeCheckedRow = (row: any) => {
|
|
434
|
+
const index = indexOf(newCheckedRows.value, row);
|
|
435
|
+
if (index >= 0) {
|
|
436
|
+
newCheckedRows.value.splice(index, 1);
|
|
454
437
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
const checkAll = () => {
|
|
441
|
+
if (isAllChecked.value) {
|
|
442
|
+
// Uncheck all rows
|
|
443
|
+
newCheckedRows.value = [];
|
|
444
|
+
} else {
|
|
445
|
+
// Check all rows
|
|
446
|
+
const rowsToCheck = computedItems.value.filter(
|
|
447
|
+
(row) => !newCheckedRows.value.includes(row)
|
|
448
|
+
);
|
|
449
|
+
newCheckedRows.value.push(...rowsToCheck);
|
|
458
450
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
451
|
+
emit("check", newCheckedRows.value);
|
|
452
|
+
emit("check-all", newCheckedRows.value);
|
|
453
|
+
emit("update:checkedRows", newCheckedRows.value);
|
|
454
|
+
emit("update:selectedRows", newCheckedRows.value);
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
const checkRow = (row: any, _rowIndex: number, _event: any) => {
|
|
458
|
+
const isChecked = newCheckedRows.value.includes(row);
|
|
459
|
+
if (_event && isChecked) {
|
|
460
|
+
removeCheckedRow(row);
|
|
461
|
+
} else {
|
|
462
|
+
newCheckedRows.value.push(row);
|
|
469
463
|
}
|
|
470
|
-
|
|
471
|
-
|
|
464
|
+
emit("check", newCheckedRows.value, row);
|
|
465
|
+
emit("update:checkedRows", newCheckedRows.value);
|
|
466
|
+
emit("update:selectedRows", newCheckedRows.value);
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
// watch
|
|
470
|
+
watch(
|
|
471
|
+
() => currentPage.value,
|
|
472
|
+
(newVal) => {
|
|
473
|
+
newCurrentPage.value = newVal;
|
|
474
|
+
},
|
|
475
|
+
{ immediate: true }
|
|
476
|
+
);
|
|
477
|
+
watch(
|
|
478
|
+
() => checkedRows.value,
|
|
479
|
+
(rows) => {
|
|
480
|
+
newCheckedRows.value = [...rows];
|
|
481
|
+
},
|
|
482
|
+
{ immediate: true }
|
|
483
|
+
);
|
|
484
|
+
|
|
485
|
+
const stickyClass = computed(() => {
|
|
486
|
+
return props.checkable && props.stickyColumn
|
|
487
|
+
? {
|
|
488
|
+
head: "bg-gray-100 sticky left-[45px] top-0 z-20",
|
|
489
|
+
body: "bg-white !sticky left-[45px] top-0 z-10 after:absolute after:content-[''] after:bg-inherit after:w-2 after:h-[103%] after:inset-y-0 after:-right-2 after:bg-gradient-to-r after:from-gray-200 after:from-0% after:via-gray-50 after:via-60% after:z-0 after:bg-white/10",
|
|
490
|
+
}
|
|
491
|
+
: {
|
|
492
|
+
head: "bg-gray-100 sticky left-0 top-0 z-20",
|
|
493
|
+
body: "bg-white !sticky left-0 top-0 z-10 after:absolute after:content-[''] after:bg-inherit after:w-2 after:h-[103%] after:inset-y-0 after:-right-2 after:bg-gradient-to-r after:from-gray-200 after:from-0% after:via-gray-50 after:via-60% after:z-0 after:bg-white/10",
|
|
494
|
+
};
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
const isOverflowing = ref(false);
|
|
498
|
+
const isScrolled = ref(false);
|
|
499
|
+
const tableContainer = ref<HTMLElement | null>(null);
|
|
500
|
+
|
|
501
|
+
const handleScroll = () => {
|
|
502
|
+
const container = tableContainer.value;
|
|
503
|
+
if (container) {
|
|
504
|
+
isScrolled.value = container.scrollLeft > 0;
|
|
472
505
|
}
|
|
473
|
-
}
|
|
506
|
+
};
|
|
474
507
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
content: "";
|
|
480
|
-
width: 18px;
|
|
481
|
-
height: 18px;
|
|
482
|
-
top: 10px;
|
|
483
|
-
right: 14px;
|
|
484
|
-
background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='iso-8859-1'%3F%3E%3C!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E%3C!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'%3E%3Csvg version='1.1' id='Capa_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='18px' height='18px' fill='%2393a3b8' viewBox='0 0 28.931 28.932' style='enable-background:new 0 0 28.931 28.932;' xml:space='preserve'%3E%3Cg%3E%3Cpath d='M28.344,25.518l-6.114-6.115c1.486-2.067,2.303-4.537,2.303-7.137c0-3.275-1.275-6.355-3.594-8.672 C18.625,1.278,15.543,0,12.266,0C8.99,0,5.909,1.275,3.593,3.594C1.277,5.909,0.001,8.99,0.001,12.266 c0,3.276,1.275,6.356,3.592,8.674c2.316,2.316,5.396,3.594,8.673,3.594c2.599,0,5.067-0.813,7.136-2.303l6.114,6.115 c0.392,0.391,0.902,0.586,1.414,0.586c0.513,0,1.024-0.195,1.414-0.586C29.125,27.564,29.125,26.299,28.344,25.518z M6.422,18.111 c-1.562-1.562-2.421-3.639-2.421-5.846S4.86,7.983,6.422,6.421c1.561-1.562,3.636-2.422,5.844-2.422s4.284,0.86,5.845,2.422 c1.562,1.562,2.422,3.638,2.422,5.845s-0.859,4.283-2.422,5.846c-1.562,1.562-3.636,2.42-5.845,2.42S7.981,19.672,6.422,18.111z'/%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3C/svg%3E%0A");
|
|
485
|
-
}
|
|
508
|
+
const checkOverflow = () => {
|
|
509
|
+
const container = tableContainer.value;
|
|
510
|
+
if (container) {
|
|
511
|
+
isOverflowing.value = container.scrollWidth > container.clientWidth;
|
|
486
512
|
}
|
|
487
|
-
}
|
|
513
|
+
};
|
|
488
514
|
|
|
489
|
-
|
|
515
|
+
onMounted(() => {
|
|
516
|
+
window.addEventListener("resize", checkOverflow);
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
onUnmounted(() => {
|
|
520
|
+
window.removeEventListener("resize", checkOverflow);
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
// Watch for changes in the container's width to check for overflow
|
|
524
|
+
watch(() => tableContainer.value?.clientWidth, checkOverflow);
|
|
525
|
+
</script>
|