edvoyui-component-library-test-flight 0.0.60 → 0.0.62

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,528 @@
1
1
  <template>
2
- <div class="max-w-screen-xl mx-auto border border-gray-100 border-solid 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"
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
+ tableClasses,
24
+ 'scrollbar--thin overscroll-none',
25
+ computedItems.length === 0 ? 'overflow-hidden pointer-events-none' : 'overflow-auto',
26
+ tableHeight
27
+ ? tableHeight
28
+ : 'h-[calc(100svh-12rem)] max-h-[calc(100svh-12rem)]',
29
+ ]"
30
+ ref="tableContainer"
31
+ @scroll="handleScroll"
32
+ >
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/75': 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="[isScrolled && headerIndex === 0 ? stickyClass.body : '',
161
+ ]"
162
+ >
163
+ <slot
164
+ :name="`item.${header?.value}`"
165
+ :row="row"
166
+ :rowIndex="rowIndex"
167
+ :headerIndex="headerIndex"
168
+ >
169
+ {{ getValueByPath(row, header?.value) }}
170
+ </slot>
171
+ </td>
172
+ </tr>
173
+ <template v-if="tableExpanded">
174
+ <slot
175
+ name="expanded"
176
+ :row="row"
177
+ :rowIndex="rowIndex"
178
+ ></slot>
179
+ </template>
180
+ </template>
181
+ <template v-else-if="computedItems.length === 0">
182
+ <tr class="norecords">
183
+ <td
184
+ :colspan="
185
+ checkable === true ? headers.length + 1 : headers.length
186
+ "
187
+ >
188
+ <div
189
+ class="flex items-center justify-center text-xl font-medium text-gray-500 h-[calc(100svh-18rem)] max-w-screen-xl border"
190
+ >
191
+ No matching records found
192
+ </div>
193
+ </td>
194
+ </tr>
195
+ </template>
196
+ </tbody>
197
+ </table>
198
+
199
+ </div>
200
+ <div
201
+ class="sticky bottom-0 left-0 z-50 flex items-center justify-between px-2 py-1 bg-white border-t border-gray-300"
202
+ >
203
+ <slot name="tableCount">
204
+ <div class="inline-flex items-center gap-x-2">
205
+ <div class="text-sm font-medium text-gray-900">
206
+ Total {{ total }}
207
+ </div>
208
+ <span class="text-gray-300">&vert;</span>
209
+ <div class="inline-flex items-center">
210
+ <span class="text-sm font-medium text-gray-900">Per page</span>
211
+ <EUIPageLimit
212
+ :page-limit="limit"
213
+ @update-limit="changeLimit($event)"
214
+ @refetch="searchData($event)"
215
+ :iconStyle="true"
216
+ />
217
+ </div>
218
+ </div>
219
+ </slot>
220
+ <template v-if="paginated && computedItems.length !== 0">
221
+ <slot name="tablepagination">
222
+ <EUIStudentPagination
223
+ :activePage="newCurrentPage"
224
+ :pageLimit="limit"
225
+ :totalCount="total"
226
+ @change-page="pageChanged($event)"
16
227
  />
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
228
  </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">
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="headerStyle(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>
229
+ </template>
230
+ </div>
231
+ </div>
232
+ </Transition>
88
233
  </div>
89
234
  </template>
90
235
 
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: '' }
236
+ <script lang="ts" setup>
237
+ import { ref, computed, watch, toRefs, PropType, onMounted, onUnmounted } from "vue";
238
+ import { getValueByPath, indexOf, defaultFilter } from "../../utils/helpers";
239
+ import UCheckbox from "./UCheckbox.vue";
240
+ import { capitalizeText } from "../../utils/lodash";
241
+ import EUIStudentPagination from "./EUIStudentPagination.vue";
242
+ import EUICircleLoader from "../loader/EUICircleLoader.vue";
243
+ import EUIPageLimit from "./EUIPageLimit.vue";
244
+ import "./UTable.scss";
245
+
246
+ interface Header {
247
+ value: string;
248
+ text?: string;
249
+ name?: string;
250
+ sortable?: boolean;
251
+ width?: number;
252
+ color?: string;
253
+ }
254
+
255
+ interface Item {
256
+ [key: string]: any;
257
+ }
258
+
259
+ const props = defineProps({
260
+ items: { type: Array as PropType<Item[]>, default: () => [] },
261
+ dataCy: { type: String, default: "ui-pagination" },
262
+ headers: { type: Array as PropType<Header[]>, default: () => [] },
263
+ paginated: Boolean,
264
+ tableExpanded: Boolean,
265
+ backendPagination: Boolean,
266
+ search: { type: String, default: "" },
267
+ perPage: { type: Number, default: 5 },
268
+ total: { type: Number, default: 0 },
269
+ currentPage: { type: Number, default: 1 },
270
+ defaultSort: { type: String, default: "" },
271
+ defaultSortDirection: { type: String, default: "asc" },
272
+ headerOptional: { type: Boolean, default: false },
273
+ checkedRows: { type: Array as PropType<Item[]>, default: () => [] },
274
+ checkable: { type: Boolean, default: false },
275
+ stickyHeader: { type: Boolean, default: true },
276
+ stickyColumn: { type: Boolean, default: true },
277
+ stickyColumnTwo: { type: Boolean, default: false },
278
+ isRowCheckable: {
279
+ type: Function as PropType<(row: Item) => boolean>,
280
+ default: () => true,
125
281
  },
282
+ hidePageNumber: { type: Boolean, default: false },
283
+ footerClass: { type: String, default: "" },
284
+ tableHeight: {
285
+ type: String,
286
+ required: false,
287
+ default: "",
288
+ },
289
+ tableLoading: { type: Boolean, default: false },
290
+ });
126
291
 
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(235 236 240)'
164
- });
165
-
166
- const pageChanged = page => {
167
- newCurrentPage.value = page;
168
- emit('update:currentPage', page);
169
- emit('page-change', page);
170
- };
171
-
172
- const sortItems = items => {
173
- return items.sort((a, b) => {
174
- const aValue = getValueByPath(a, currentSort.value);
175
- const bValue = getValueByPath(b, currentSort.value);
176
- return currentSortDir.value === 'asc' ? aValue - bValue : bValue - aValue;
177
- });
178
- };
179
-
180
- const paginateItems = items => {
181
- const start = (newCurrentPage.value - 1) * props.perPage;
182
- const end = start + props.perPage;
183
- return items.slice(start, end);
184
- };
185
-
186
- const isRowChecked = row => {
187
- return newCheckedRows.value.includes(row);
188
- };
189
-
190
- const sortClass = () => {
191
- return header => {
192
- return this.currentSortDir === "asc" &&
193
- this.currentSort === header.value
194
- ? "ui-table__sortable--asc"
195
- : this.currentSortDir === "desc" && this.currentSort === header.value
196
- ? "ui-table__sortable--dsc"
197
- : ""
198
- }
199
- }
200
-
201
- const isAllChecked = computed(() => {
202
- return (
203
- computedItems.value.length > 0 &&
204
- computedItems.value.every(row => newCheckedRows.value.includes(row))
205
- );
206
- });
207
-
208
- const isIndeterminate = computed(() => {
209
- const checkedCount = computedItems.value.filter(row =>
210
- newCheckedRows.value.includes(row)
211
- ).length;
212
- return checkedCount > 0 && checkedCount < computedItems.value.length;
213
- });
214
-
215
- const checkAll = () => {
216
- if (isAllChecked.value) {
217
- // Uncheck all rows
218
- newCheckedRows.value = []
219
- } else {
220
- // Check all rows
221
- const rowsToCheck = computedItems.value.filter(
222
- row => !newCheckedRows.value.includes(row)
223
- );
224
- newCheckedRows.value.push(...rowsToCheck);
225
- }
226
- emit('check', newCheckedRows.value);
227
- emit("check-all", newCheckedRows.value);
228
- emit("update:checkedRows", newCheckedRows.value);
229
- emit("update:selectedRows", newCheckedRows.value);
230
- };
231
-
232
- const removeCheckedRow = (row) => {
233
- const index = indexOf(newCheckedRows.value, row);
234
- if (index >= 0) {
235
- newCheckedRows.value.splice(index, 1);
236
- }
237
- };
238
-
239
- const checkRow = (row, index, event) => {
240
- const isChecked = newCheckedRows.value.includes(row);
241
- if (event.target && isChecked) {
242
- // Remove row from checkedRows
243
- removeCheckedRow(row);
244
- }
245
- else {
246
- newCheckedRows.value.push(row);
247
- };
248
- emit('check', newCheckedRows.value);
249
- emit("update:checkedRows", newCheckedRows.value);
250
- emit("update:selectedRows", newCheckedRows.value);
251
- };
252
-
253
- watch(() => props.currentPage, newPage => (newCurrentPage.value = newPage));
254
- watch(() => props.checkedRows, rows => (newCheckedRows.value = [...rows]));
255
-
256
- return {
257
- tableClasses,
258
- computedItems,
259
- headerStyle,
260
- pageChanged,
261
- checkRow,
262
- checkAll,
263
- itemsLength,
264
- sortClass,
265
- isRowChecked,
266
- isAllChecked,
267
- isIndeterminate,
268
- getValueByPath,
269
- capitalizeText
270
- };
292
+ const {
293
+ defaultSort,
294
+ defaultSortDirection,
295
+ checkedRows,
296
+ currentPage,
297
+ items,
298
+ backendPagination,
299
+ total,
300
+ search,
301
+ headers,
302
+ paginated,
303
+ isRowCheckable,
304
+ } = toRefs(props);
305
+
306
+ // Emits used
307
+ const emit = defineEmits([
308
+ "update:currentPage",
309
+ "changePage",
310
+ "sort",
311
+ "check",
312
+ "check-all",
313
+ "update:checkedRows",
314
+ "update:selectedRows",
315
+ "changeLimit",
316
+ "mouseenter",
317
+ "mouseleave",
318
+ ]);
319
+
320
+ const currentSort = ref(defaultSort.value);
321
+ const currentSortDir = ref(defaultSortDirection.value);
322
+ const newCheckedRows = ref([...checkedRows.value]);
323
+ const newCurrentPage = ref(currentPage.value);
324
+ const limit = ref(props.perPage);
325
+ const loading = computed(() => props.tableLoading);
326
+
327
+ // Computed Property
328
+ const tableClasses = computed(() => ({
329
+ "ui-table scrollbar--thin": true,
330
+ "ui-table--sticky-header": props.stickyHeader,
331
+ "ui-table--sticky-column": props.stickyColumn,
332
+ "ui-table--sticky-column-two": props.stickyColumnTwo,
333
+ }));
334
+
335
+ const filteredItems = computed(() => {
336
+ let filteredItems = items.value.slice();
337
+ if (!backendPagination.value && search.value !== "") {
338
+ if (search.value.trim() === "") return filteredItems;
339
+ const props = headers.value.map((h: any) => h.value);
340
+ filteredItems = items.value.filter((row) =>
341
+ props.some((prop) =>
342
+ defaultFilter(
343
+ getValueByPath(row, prop),
344
+ search.value.toString().toLowerCase()
345
+ )
346
+ )
347
+ );
271
348
  }
349
+ return filteredItems;
350
+ });
351
+
352
+ const computedItems = computed(() => {
353
+ let items = filteredItems.value;
354
+ // Sort items before slicing for pagination
355
+ items.sort((a: any, b: any) => {
356
+ const modifier = currentSortDir.value === "desc" ? -1 : 1;
357
+ if (a[currentSort.value] < b[currentSort.value]) return -1 * modifier;
358
+ if (a[currentSort.value] > b[currentSort.value]) return 1 * modifier;
359
+ return 0;
360
+ });
361
+ return items;
362
+
363
+ // Apply pagination
364
+ // const start = (newCurrentPage.value - 0) * limit.value;
365
+ // const end = start + limit.value;
366
+ // return items.slice(start, end);
367
+ });
368
+
369
+ const headerStyle = (header: Header) => ({
370
+ width: `${header.width || 300}px`,
371
+ flex: `${header.width || 300} 0 auto`,
372
+ maxWidth: `${header.width || 300}px`,
373
+ backgroundColor: header.color || "rgb(243 244 246)",
374
+ });
375
+
376
+ const bodyStyle = (header: Header) => ({
377
+ width: `${header.width || 300}px`,
378
+ flex: `${header.width || 300} 0 auto`,
379
+ maxWidth: `${header.width || 300}px`,
380
+ });
381
+
382
+ const searchData = (_data: any) => {
383
+ // console.log(_data);
272
384
  };
273
- </script>
274
385
 
275
- <style lang="scss" scoped>
276
- .ui-table {
277
- @apply overflow-auto bg-white bg-no-repeat border border-solid border-gray-50;
278
- background-image: linear-gradient(to right, white, white),
279
- linear-gradient(to right, white, white),
280
- linear-gradient(to right, rgba(223, 231, 242, 1), rgba(255, 255, 255, 0)),
281
- linear-gradient(to left, rgba(223, 231, 242, 1), rgba(255, 255, 255, 0)),
282
- linear-gradient(to top, white, rgba(255, 255, 255, 0)),
283
- linear-gradient(to bottom, rgba(255, 255, 255, 0), rgba(223, 231, 242, 1));
284
- background-position: left center, right center, left center, right center,
285
- left bottom, left bottom;
286
- background-size: 30px 100%, 30px 100%, 20px 100%, 20px 100%, 100% 100px,
287
- 100% 20px;
288
- background-attachment: local, local, scroll, scroll, local, scroll;
289
-
290
- .table {
291
- @apply border-separate w-full;
292
- border-spacing: 0;
293
- thead {
294
- tr {
295
- @apply flex flex-grow;
296
- th {
297
- @apply bg-gray-50 px-3 py-4 text-sm text-gray-600 text-left font-semibold uppercase border-r border-b border-solid border-gray-100 cursor-auto whitespace-nowrap flex items-center;
298
- height: 45px;
299
- transition: all 0.15s ease-out;
300
- &:hover {
301
- @apply bg-blue-50;
302
- }
303
- &:first-child {
304
- border-left-width: 0;
305
- }
306
- &:last-child {
307
- border-right-width: 0;
308
- }
309
- &:last-of-type {
310
- max-width: unset !important;
311
- }
312
- &__sortable {
313
- @apply cursor-pointer pr-12;
314
- &--active {
315
- @apply bg-blue-50;
316
- }
317
- &::before,
318
- &::after {
319
- @apply absolute w-0 h-0;
320
- content: "";
321
- right: 20px;
322
- border-left: 4px solid transparent;
323
- border-right: 4px solid transparent;
324
- }
325
- &::before {
326
- top: 13px;
327
- border-bottom: 6px solid theme('colors.gray.600');
328
- }
329
- &::after {
330
- top: 22px;
331
- border-top: 6px solid theme("colors.gray.500");
332
- }
333
- &--asc {
334
- &::before {
335
- border-bottom: 6px solid theme("colors.blue.300");
336
- }
337
- }
338
- &--dsc {
339
- &::after {
340
- border-top: 6px solid theme("colors.blue.300");
341
- }
342
- }
343
- }
344
- }
345
- }
346
- }
347
- tbody {
348
- tr {
349
- @apply flex flex-grow;
350
- &:hover {
351
- background-color: rgba(238, 243, 250, 0.8);
352
- transition: all 0.15s ease-out;
353
- }
354
- td {
355
- @apply relative text-sm text-gray-900 border-b border-solid border-gray-50 break-words flex items-center;
356
- padding: 7px 12px;
357
- height: 47px;
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
- }
386
+ const changeLimit = (limitData: number) => {
387
+ limit.value = limitData;
388
+ newCurrentPage.value = 1;
389
+ emit("update:currentPage", newCurrentPage.value);
390
+ emit("changeLimit", limitData);
391
+ };
392
+
393
+ const pageChanged = (page: number) => {
394
+ newCurrentPage.value = page > 0 ? page : 0;
395
+ emit("update:currentPage", newCurrentPage.value);
396
+ emit("changePage", page);
397
+ };
398
+
399
+ const sortBy = (header: any, event: any) => {
400
+ if (!header || !header.sortable) return;
401
+ if (header.value === currentSort.value) {
402
+ currentSortDir.value = currentSortDir.value === "asc" ? "desc" : "asc";
445
403
  }
446
- &--sticky-header {
447
- .table {
448
- thead {
449
- tr {
450
- @apply sticky top-0 z-10;
451
- }
452
- }
453
- }
404
+ currentSort.value = header.value;
405
+ emit("sort", currentSort.value, currentSortDir.value, event);
406
+ };
407
+
408
+ const isAllChecked = computed(() => {
409
+ return (
410
+ computedItems.value.length > 0 &&
411
+ computedItems.value.every((row) => newCheckedRows.value.includes(row))
412
+ );
413
+ });
414
+
415
+ const isAllUncheckable = computed(() => {
416
+ const validVisibleData = computedItems.value?.filter((row) =>
417
+ isRowCheckable.value!(row)
418
+ );
419
+ return validVisibleData.length === 0;
420
+ });
421
+
422
+ const isIndeterminate = computed(() => {
423
+ const validVisibleData = computedItems.value.filter((row) =>
424
+ isRowCheckable.value(row)
425
+ );
426
+ return (
427
+ newCheckedRows.value.length > 0 &&
428
+ newCheckedRows.value.length < validVisibleData.length
429
+ );
430
+ });
431
+
432
+ const isRowChecked = (row: any) => {
433
+ return indexOf(newCheckedRows.value, row) >= 0;
434
+ };
435
+
436
+ const removeCheckedRow = (row: any) => {
437
+ const index = indexOf(newCheckedRows.value, row);
438
+ if (index >= 0) {
439
+ newCheckedRows.value.splice(index, 1);
454
440
  }
455
- &__no-result {
456
- @apply w-full border-none;
457
- position: unset;
441
+ };
442
+
443
+ const checkAll = () => {
444
+ if (isAllChecked.value) {
445
+ // Uncheck all rows
446
+ newCheckedRows.value = [];
447
+ } else {
448
+ // Check all rows
449
+ const rowsToCheck = computedItems.value.filter(
450
+ (row) => !newCheckedRows.value.includes(row)
451
+ );
452
+ newCheckedRows.value.push(...rowsToCheck);
458
453
  }
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
- }
454
+ emit("check", newCheckedRows.value);
455
+ emit("check-all", newCheckedRows.value);
456
+ emit("update:checkedRows", newCheckedRows.value);
457
+ emit("update:selectedRows", newCheckedRows.value);
458
+ };
459
+
460
+ const checkRow = (row: any, _rowIndex: number, _event: any) => {
461
+ const isChecked = newCheckedRows.value.includes(row);
462
+ if (_event && isChecked) {
463
+ removeCheckedRow(row);
464
+ } else {
465
+ newCheckedRows.value.push(row);
469
466
  }
470
- &__footer {
471
- @apply flex justify-between items-center p-3;
467
+ emit("check", newCheckedRows.value, row);
468
+ emit("update:checkedRows", newCheckedRows.value);
469
+ emit("update:selectedRows", newCheckedRows.value);
470
+ };
471
+
472
+ // watch
473
+ watch(
474
+ () => currentPage.value,
475
+ (newVal) => {
476
+ newCurrentPage.value = newVal;
477
+ },
478
+ { immediate: true }
479
+ );
480
+ watch(
481
+ () => checkedRows.value,
482
+ (rows) => {
483
+ newCheckedRows.value = [...rows];
484
+ },
485
+ { immediate: true }
486
+ );
487
+
488
+ const stickyClass = computed(() => {
489
+ return props.checkable && props.stickyColumn
490
+ ? {
491
+ head: "bg-gray-100 sticky left-[45px] top-0 z-20",
492
+ 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",
493
+ }
494
+ : {
495
+ head: "bg-gray-100 sticky left-0 top-0 z-20",
496
+ 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",
497
+ };
498
+ });
499
+
500
+ const isOverflowing = ref(false);
501
+ const isScrolled = ref(false);
502
+ const tableContainer = ref<HTMLElement | null>(null);
503
+
504
+ const handleScroll = () => {
505
+ const container = tableContainer.value;
506
+ if (container) {
507
+ isScrolled.value = container.scrollLeft > 0;
472
508
  }
473
- }
509
+ };
474
510
 
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
- }
511
+ const checkOverflow = () => {
512
+ const container = tableContainer.value;
513
+ if (container) {
514
+ isOverflowing.value = container.scrollWidth > container.clientWidth;
486
515
  }
487
- }
516
+ };
488
517
 
489
- </style>
518
+ onMounted(() => {
519
+ window.addEventListener("resize", checkOverflow);
520
+ });
521
+
522
+ onUnmounted(() => {
523
+ window.removeEventListener("resize", checkOverflow);
524
+ });
525
+
526
+ // Watch for changes in the container's width to check for overflow
527
+ watch(() => tableContainer.value?.clientWidth, checkOverflow);
528
+ </script>