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.
@@ -1,489 +1,525 @@
1
1
  <template>
2
- <div class="max-w-screen-xl mx-auto h-[calc(100dvh-20rem)] overflow-hidden">
3
- <div :class="tableClasses">
4
- <table class="table">
5
- <thead>
6
- <tr>
7
- <th
8
- v-if="checkable"
9
- style="width: 45px; flex: 45 0 auto; max-width: 45px; background-color: rgb(243 244 246);"
10
- >
11
- <UCheckbox
12
- :checked="isAllChecked"
13
- :indeterminate="isIndeterminate"
14
- class="flex justify-center mt-0"
15
- @change="checkAll"
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">&vert;</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
- <span v-if="headerOptional">
36
- <slot name="headerOptionalItem"></slot>
37
- </span>
38
- </th>
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 { ref, computed, watch } from 'vue';
93
- import { getValueByPath, indexOf, defaultFilter } from '../../utils/helpers';
94
- import UCheckbox from './UCheckbox.vue';
95
- import { PropType } from 'vue';
96
- import { capitalizeText } from '../../utils/lodash';
97
- import EUIPagination from './EUIPagination.vue';
98
-
99
- export default {
100
- name: 'UITable',
101
- components: { UCheckbox, EUIPagination },
102
-
103
- props: {
104
- items: { type: Array, default: () => [] },
105
- dataCy: { type: String, default: 'ui-pagination' },
106
- headers: { type: Array, default: () => [] },
107
- paginated: Boolean,
108
- tableExpanded: Boolean,
109
- backendPagination: Boolean,
110
- search: { type: String, default: '' },
111
- perPage: { type: [Number, String], default: 5 },
112
- total: { type: [Number, String], default: 0 },
113
- currentPage: { type: Number, default: 1 },
114
- defaultSort: { type: String, default: '' },
115
- defaultSortDirection: { type: String, default: 'asc' },
116
- headerOptional: { type: Boolean, default: false },
117
- checkedRows: { type: Array, default: () => [] },
118
- checkable: { type: Boolean, default: false },
119
- stickyHeader: { type: Boolean, default: true },
120
- stickyColumn: { type: Boolean, default: false },
121
- stickyColumnTwo: { type: Boolean, default: false },
122
- isRowCheckable: { type: Function, default: () => true },
123
- hidePageNumber: { type: Boolean, default: false },
124
- footerClass: { type: String, default: '' }
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
- setup(props, { emit }) {
128
- const newCurrentPage = ref(props.currentPage);
129
- const newCheckedRows = ref([...props.checkedRows]);
130
- const currentSort = ref(props.defaultSort);
131
- const currentSortDir = ref(props.defaultSortDirection);
132
-
133
- const tableClasses = computed(() => ({
134
- 'ui-table scrollbar--thin': true,
135
- 'ui-table--sticky-header': props.stickyHeader,
136
- 'ui-table--sticky-column': props.stickyColumn,
137
- 'ui-table--sticky-column-two': props.stickyColumnTwo
138
- }));
139
-
140
- const itemsLength = computed(() => (props.backendPagination ? props.total : filteredItems.value.length));
141
-
142
- const filteredItems = computed(() => {
143
- if (!props.backendPagination && props.search) {
144
- const propsKeys = props.headers.map(h => h.value);
145
- return props.items.filter(item =>
146
- propsKeys.some(key => defaultFilter(getValueByPath(item, key), props.search.toLowerCase()))
147
- );
148
- }
149
- return props.items;
150
- });
151
-
152
- const computedItems = computed(() => {
153
- let items = [...filteredItems.value];
154
- if (currentSort.value && !props.backendPagination) items = sortItems(items);
155
- if (props.paginated && !props.backendPagination) items = paginateItems(items);
156
- return items;
157
- });
158
-
159
- const headerStyle = header => ({
160
- width: `${header.width || 300}px`,
161
- flex: `${header.width || 300} 0 auto`,
162
- maxWidth: `${header.width || 300}px`,
163
- backgroundColor: header.color || 'rgb(243 244 246)'
164
- });
165
-
166
- const bodyStyle = header => ({
167
- width: `${header.width || 300}px`,
168
- flex: `${header.width || 300} 0 auto`,
169
- maxWidth: `${header.width || 300}px`,
170
- });
171
-
172
-
173
- const pageChanged = page => {
174
- newCurrentPage.value = page;
175
- emit('update:currentPage', page);
176
- emit('page-change', page);
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
- <style lang="scss" scoped>
284
- .ui-table {
285
- @apply overflow-auto bg-white bg-no-repeat;
286
- background-image: linear-gradient(to right, white, white),
287
- linear-gradient(to right, white, white),
288
- linear-gradient(to right, rgba(223, 231, 242, 1), rgba(255, 255, 255, 0)),
289
- linear-gradient(to left, rgba(223, 231, 242, 1), rgba(255, 255, 255, 0)),
290
- linear-gradient(to top, white, rgba(255, 255, 255, 0)),
291
- linear-gradient(to bottom, rgba(255, 255, 255, 0), rgba(223, 231, 242, 1));
292
- background-position: left center, right center, left center, right center,
293
- left bottom, left bottom;
294
- background-size: 30px 100%, 30px 100%, 20px 100%, 20px 100%, 100% 100px,
295
- 100% 20px;
296
- background-attachment: local, local, scroll, scroll, local, scroll;
297
-
298
- .table {
299
- @apply border-separate w-full border-0 border-spacing-0 border-gray-50 box-border;
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
- &--sticky-header {
447
- .table {
448
- thead {
449
- tr {
450
- @apply sticky top-0 z-10;
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
- &__no-result {
456
- @apply w-full border-none;
457
- position: unset;
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
- &__hovercontent {
460
- @apply flex items-center absolute inset-y-0;
461
- right: 10px;
462
- transition: all 0.15s ease-out;
463
- &--show {
464
- opacity: 1;
465
- }
466
- &--hide {
467
- opacity: 0;
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
- &__footer {
471
- @apply flex justify-between items-center p-3;
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
- .search-icon {
476
- span {
477
- &::after {
478
- @apply absolute m-auto;
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
- </style>
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>