edvoyui-component-library-test-flight 0.0.21 → 0.0.23
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/EUIButton.vue.d.ts.map +1 -0
- package/dist/library-vue-ts.cjs.js +4 -3
- package/dist/library-vue-ts.es.js +3 -8
- package/dist/library-vue-ts.umd.js +4 -3
- package/dist/select/EUISelect.vue.d.ts.map +1 -0
- package/package.json +4 -2
- package/src/App.vue +16 -0
- package/src/assets/fonts/gilroy/GilroyBold/font.woff +0 -0
- package/src/assets/fonts/gilroy/GilroyBold/font.woff2 +0 -0
- package/src/assets/fonts/gilroy/GilroyBoldItalic/font.woff +0 -0
- package/src/assets/fonts/gilroy/GilroyBoldItalic/font.woff2 +0 -0
- package/src/assets/fonts/gilroy/GilroyExtraBold/font.woff +0 -0
- package/src/assets/fonts/gilroy/GilroyExtraBold/font.woff2 +0 -0
- package/src/assets/fonts/gilroy/GilroyExtraBoldItalic/font.woff +0 -0
- package/src/assets/fonts/gilroy/GilroyExtraBoldItalic/font.woff2 +0 -0
- package/src/assets/fonts/gilroy/GilroyMedium/font.woff +0 -0
- package/src/assets/fonts/gilroy/GilroyMedium/font.woff2 +0 -0
- package/src/assets/fonts/gilroy/GilroyRegular/font.woff +0 -0
- package/src/assets/fonts/gilroy/GilroyRegular/font.woff2 +0 -0
- package/src/assets/fonts/gilroy/GilroySemiBold/font.woff +0 -0
- package/src/assets/fonts/gilroy/GilroySemiBold/font.woff2 +0 -0
- package/src/assets/fonts/inter/Inter-Bold.woff +0 -0
- package/src/assets/fonts/inter/Inter-Bold.woff2 +0 -0
- package/src/assets/fonts/inter/Inter-Italic.woff +0 -0
- package/src/assets/fonts/inter/Inter-Italic.woff2 +0 -0
- package/src/assets/fonts/inter/Inter-Medium.woff +0 -0
- package/src/assets/fonts/inter/Inter-Medium.woff2 +0 -0
- package/src/assets/fonts/inter/Inter-MediumItalic.woff +0 -0
- package/src/assets/fonts/inter/Inter-MediumItalic.woff2 +0 -0
- package/src/assets/fonts/inter/Inter-Regular.woff +0 -0
- package/src/assets/fonts/inter/Inter-Regular.woff2 +0 -0
- package/src/assets/fonts/inter/Inter-SemiBold.woff +0 -0
- package/src/assets/fonts/inter/Inter-SemiBold.woff2 +0 -0
- package/src/assets/fonts/inter/Inter-SemiBoldItalic.woff +0 -0
- package/src/assets/fonts/inter/Inter-SemiBoldItalic.woff2 +0 -0
- package/src/assets/scss/body.scss +15 -0
- package/src/assets/svg/ChevronDownSolid.vue +19 -0
- package/src/assets/svg/ChevronDownStroke.vue +22 -0
- package/src/assets/svg/SortArrow.vue +24 -0
- package/src/assets/svg/Student.vue +30 -0
- package/src/assets/svg/partner.vue +33 -0
- package/src/assets/svg/people.vue +25 -0
- package/src/assets/vue.svg +1 -0
- package/src/components/HelloWorld.vue +999 -0
- package/src/components/accordion/EUIAccordion.stories.ts +157 -0
- package/src/components/accordion/EUIAccordion.vue +90 -0
- package/src/components/avatar/EUIAvatar.stories.ts +157 -0
- package/src/components/avatar/EUIAvatar.vue +96 -0
- package/src/components/button/EUIButton.stories.ts +252 -0
- package/src/components/button/EUIButton.vue +151 -0
- package/src/components/checkbox/EUICheckbox.stories.ts +58 -0
- package/src/components/checkbox/EUICheckbox.vue +103 -0
- package/src/components/datepicker/EUIDatepicker.stories.ts +236 -0
- package/src/components/datepicker/EUIDatepicker.vue +185 -0
- package/src/components/delete.vue +108 -0
- package/src/components/dropdown/EUIMultiDropdown.stories.ts +187 -0
- package/src/components/dropdown/EUIMultiDropdown.vue +129 -0
- package/src/components/errorMessage/EUIErrorMessage.scss +0 -0
- package/src/components/errorMessage/EUIErrorMessage.stories.ts +41 -0
- package/src/components/errorMessage/EUIErrorMessage.vue +25 -0
- package/src/components/index.ts +46 -0
- package/src/components/input/EUIInput.stories.ts +174 -0
- package/src/components/input/EUIInput.vue +169 -0
- package/src/components/inputNormal/EUIInputNormal.stories.ts +164 -0
- package/src/components/inputNormal/EUIInputNormal.vue +161 -0
- package/src/components/loader/EUICircleLoader.vue +31 -0
- package/src/components/loader/EUICubeLoader.vue +237 -0
- package/src/components/loader/EUILoader.stories.ts +99 -0
- package/src/components/loader/EUILoader.vue +17 -0
- package/src/components/loader/EUISquareLoader.vue +47 -0
- package/src/components/modal/EUIModal.stories.ts +372 -0
- package/src/components/modal/EUIModal.vue +163 -0
- package/src/components/pillSelect/EUIPillSelect.stories.ts +74 -0
- package/src/components/pillSelect/EUIPillSelect.vue +149 -0
- package/src/components/popover/EUIPopover.stories.ts +247 -0
- package/src/components/popover/EUIPopover.vue +159 -0
- package/src/components/radio/EUIRadio.stories.ts +54 -0
- package/src/components/radio/EUIRadio.vue +78 -0
- package/src/components/searchInput/EUISearch.stories.ts +24 -0
- package/src/components/searchInput/EUISearch.vue +215 -0
- package/src/components/select/EUISelect.scss +0 -0
- package/src/components/select/EUISelect.stories.ts +49 -0
- package/src/components/select/EUISelect.vue +682 -0
- package/src/components/selectSearch/EUISelectSearch.vue +23 -0
- package/src/components/slideover/EUISlideover.stories.ts +318 -0
- package/src/components/slideover/EUISlideover.vue +207 -0
- package/src/components/stepperTimeline/EUIStepperHorizontal.vue +112 -0
- package/src/components/stepperTimeline/EUIStepperTimeline.stories.ts +54 -0
- package/src/components/stepperTimeline/EUIStepperTimeline.vue +16 -0
- package/src/components/stepperTimeline/EUIStepperVertical.vue +112 -0
- package/src/components/table/EUIDashboardTable.vue +482 -0
- package/src/components/table/EUIPageLimit.vue +66 -0
- package/src/components/table/EUIPagination.vue +175 -0
- package/src/components/table/EUIStudentPagination.vue +172 -0
- package/src/components/table/EUITable.stories.ts +190 -0
- package/src/components/table/EUITable.vue +508 -0
- package/src/components/table/EUITableCheckbox.vue +97 -0
- package/src/components/tabs/EUITabs.vue +128 -0
- package/src/components/tabs/EUItabs.stories.ts +123 -0
- package/src/components/tag/EUITag.stories.ts +46 -0
- package/src/components/tag/EUITag.vue +46 -0
- package/src/components/telephone/EUITelephone.stories.ts +202 -0
- package/src/components/telephone/EUITelephone.vue +280 -0
- package/src/components/textArea/EUITextArea.stories.ts +82 -0
- package/src/components/textArea/EUITextArea.vue +122 -0
- package/src/components/timeLine/EUITimeLine.stories.ts +247 -0
- package/src/components/timeLine/EUITimeLine.vue +43 -0
- package/src/components/timeLine/EUITimeLineItem.vue +124 -0
- package/src/components/toggle/EUIToggle.stories.ts +63 -0
- package/src/components/toggle/EUIToggle.vue +99 -0
- package/src/components/tooltip/EUITooltip.stories.ts +53 -0
- package/src/components/tooltip/EUITooltip.vue +108 -0
- package/src/data/books.ts +163 -0
- package/src/data/tab.ts +33 -0
- package/src/data/table.ts +5392 -0
- package/src/main.ts +5 -0
- package/src/utils/lodash.ts +9 -0
- package/src/utils/types.ts +9 -0
- package/src/vite-env.d.ts +5 -0
- package/dist/EUISelect.vue.d.ts.map +0 -1
- package/dist/button/EUIButton.vue.d.ts.map +0 -1
- package/dist/library-vue-ts.css +0 -1
- package/dist/style.scss +0 -118
- /package/dist/{button/EUIButton.vue.d.ts → EUIButton.vue.d.ts} +0 -0
- /package/dist/{EUISelect.vue.d.ts → select/EUISelect.vue.d.ts} +0 -0
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<div class="relative w-full mx-auto overflow-hidden">
|
|
4
|
+
<div
|
|
5
|
+
id="student-table"
|
|
6
|
+
:class="['overflow-auto scrollbar--thin overscroll-none', tableHeight ? tableHeight : 'h-[calc(100svh-12rem)] max-h-[calc(100svh-12rem)]']"
|
|
7
|
+
ref="tableContainer"
|
|
8
|
+
@scroll="handleScroll"
|
|
9
|
+
>
|
|
10
|
+
<table class="eui-table">
|
|
11
|
+
<thead
|
|
12
|
+
class="sticky top-0 left-0 z-20 bg-gray-100 before:-bottom-px before:left-0 before:absolute before:h-px before:w-full before:bg-gray-300"
|
|
13
|
+
>
|
|
14
|
+
<tr>
|
|
15
|
+
<template v-if="checkable">
|
|
16
|
+
<th class="checkable">
|
|
17
|
+
<EUITableCheckbox
|
|
18
|
+
:checked="isAllChecked"
|
|
19
|
+
:indeterminate="isIndeterminate"
|
|
20
|
+
:disabled="isAllUncheckable"
|
|
21
|
+
class="flex justify-center mt-0"
|
|
22
|
+
@change="checkAll()"
|
|
23
|
+
/>
|
|
24
|
+
</th>
|
|
25
|
+
</template>
|
|
26
|
+
<th
|
|
27
|
+
v-for="(header, headerIndex) in headers"
|
|
28
|
+
:key="`item-${headerIndex}`"
|
|
29
|
+
scope="col"
|
|
30
|
+
:class="[
|
|
31
|
+
'px-3 py-2 text-gray-600 snap-start snap-always',
|
|
32
|
+
isScrolled && headerIndex === 0 ? stickyClass.head : '',
|
|
33
|
+
{ 'cursor-pointer hover:text-gray-900': header?.sortable },
|
|
34
|
+
]"
|
|
35
|
+
:style="
|
|
36
|
+
header?.width
|
|
37
|
+
? `min-width:${header?.width}px;max-width:${header?.width}px;`
|
|
38
|
+
: ``
|
|
39
|
+
"
|
|
40
|
+
@click="sortBy(header, $event)"
|
|
41
|
+
>
|
|
42
|
+
<div
|
|
43
|
+
class="flex items-center justify-between gap-2 text-sm font-medium text-current font-inter"
|
|
44
|
+
>
|
|
45
|
+
<slot name="header" :header="header">
|
|
46
|
+
{{ capitalizeText(header?.text ?? header?.name ?? '') }}
|
|
47
|
+
</slot>
|
|
48
|
+
<slot name="headerOptionalItem"></slot>
|
|
49
|
+
<div v-if="header?.sortable" class="flex-none">
|
|
50
|
+
<svg
|
|
51
|
+
width="24"
|
|
52
|
+
height="24"
|
|
53
|
+
viewBox="0 0 24 24"
|
|
54
|
+
fill="none"
|
|
55
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
56
|
+
class="size-6"
|
|
57
|
+
>
|
|
58
|
+
<path
|
|
59
|
+
d="M8 15C9.06206 16.4619 10.3071 17.7713 11.7021 18.8942C11.8774 19.0353 12.1226 19.0353 12.2979 18.8942C13.6929 17.7713 14.9379 16.4619 16 15"
|
|
60
|
+
:stroke="
|
|
61
|
+
currentSortDir === 'asc' &&
|
|
62
|
+
currentSort === header?.value
|
|
63
|
+
? '#111827'
|
|
64
|
+
: '#9ca3af'
|
|
65
|
+
"
|
|
66
|
+
stroke-opacity="0.8"
|
|
67
|
+
stroke-width="2"
|
|
68
|
+
stroke-linecap="round"
|
|
69
|
+
stroke-linejoin="round"
|
|
70
|
+
/>
|
|
71
|
+
<path
|
|
72
|
+
d="M8 9C9.06206 7.5381 10.3071 6.2287 11.7021 5.1058C11.8774 4.9647 12.1226 4.9647 12.2979 5.1058C13.6929 6.2287 14.9379 7.5381 16 9"
|
|
73
|
+
:stroke="
|
|
74
|
+
currentSortDir === 'desc' &&
|
|
75
|
+
currentSort === header?.value
|
|
76
|
+
? '#111827'
|
|
77
|
+
: '#9ca3af'
|
|
78
|
+
"
|
|
79
|
+
stroke-opacity="0.8"
|
|
80
|
+
stroke-width="2"
|
|
81
|
+
stroke-linecap="round"
|
|
82
|
+
stroke-linejoin="round"
|
|
83
|
+
/>
|
|
84
|
+
</svg>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
</th>
|
|
88
|
+
</tr>
|
|
89
|
+
</thead>
|
|
90
|
+
<tbody>
|
|
91
|
+
<template
|
|
92
|
+
v-for="(row, rowIndex) in computedItems"
|
|
93
|
+
:key="`table-row-${rowIndex}`"
|
|
94
|
+
>
|
|
95
|
+
<tr
|
|
96
|
+
@mouseenter="$attrs.mouseenter ? $emit('mouseenter', row, rowIndex) : null"
|
|
97
|
+
@mouseleave="$attrs.mouseleave ? $emit('mouseleave', row, rowIndex) : null"
|
|
98
|
+
>
|
|
99
|
+
<template v-if="checkable">
|
|
100
|
+
<td class="checkable">
|
|
101
|
+
<EUITableCheckbox
|
|
102
|
+
:disabled="!isRowCheckable(row)"
|
|
103
|
+
:checked="isRowChecked(row)"
|
|
104
|
+
@change.prevent.stop="($event) => checkRow(row, rowIndex, $event)"
|
|
105
|
+
/>
|
|
106
|
+
</td>
|
|
107
|
+
</template>
|
|
108
|
+
<td
|
|
109
|
+
v-for="(header, headerIndex) in headers"
|
|
110
|
+
:key="headerIndex"
|
|
111
|
+
:class="[isScrolled && headerIndex === 0 ? stickyClass.body : '']"
|
|
112
|
+
>
|
|
113
|
+
<slot
|
|
114
|
+
:name="`item.${header?.value}`"
|
|
115
|
+
:row="row"
|
|
116
|
+
:rowIndex="rowIndex"
|
|
117
|
+
>
|
|
118
|
+
{{ getValueByPath(row, header?.value) }}
|
|
119
|
+
</slot>
|
|
120
|
+
</td>
|
|
121
|
+
</tr>
|
|
122
|
+
<template v-if="tableExpanded">
|
|
123
|
+
<slot name="expanded" :row="row" :rowIndex="rowIndex"></slot>
|
|
124
|
+
</template>
|
|
125
|
+
</template>
|
|
126
|
+
</tbody>
|
|
127
|
+
<tbody v-if="computedItems.length === 0">
|
|
128
|
+
<tr>
|
|
129
|
+
<td :colspan="checkable === true ? headers.length + 1 : headers.length">
|
|
130
|
+
<div
|
|
131
|
+
class="flex items-center justify-center text-xl font-medium text-gray-500 min-h-96"
|
|
132
|
+
>
|
|
133
|
+
No matching records found
|
|
134
|
+
</div>
|
|
135
|
+
</td>
|
|
136
|
+
</tr>
|
|
137
|
+
</tbody>
|
|
138
|
+
</table>
|
|
139
|
+
</div>
|
|
140
|
+
<div
|
|
141
|
+
class="sticky bottom-0 left-0 z-50 flex items-center justify-between px-2 py-1 bg-white border-t border-gray-300"
|
|
142
|
+
>
|
|
143
|
+
<slot name="tableCount">
|
|
144
|
+
<div class="inline-flex items-center gap-x-2">
|
|
145
|
+
<div class="text-sm font-medium text-gray-900">
|
|
146
|
+
Total {{ total }}
|
|
147
|
+
</div>
|
|
148
|
+
<span class="text-gray-300">|</span>
|
|
149
|
+
<div class="inline-flex items-center">
|
|
150
|
+
<span class="text-sm font-medium text-gray-900">Per page</span>
|
|
151
|
+
<EUIPageLimit
|
|
152
|
+
:page-limit="limit"
|
|
153
|
+
@update-limit="changeLimit($event)"
|
|
154
|
+
@refetch="searchData($event)"
|
|
155
|
+
:iconStyle="true"
|
|
156
|
+
/>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
</slot>
|
|
160
|
+
<template v-if="paginated && computedItems.length !== 0">
|
|
161
|
+
<slot name="tablepagination">
|
|
162
|
+
<EUIStudentPagination
|
|
163
|
+
:activePage="newCurrentPage"
|
|
164
|
+
:pageLimit="limit"
|
|
165
|
+
:totalCount="total"
|
|
166
|
+
@change-page="pageChanged($event)"
|
|
167
|
+
/>
|
|
168
|
+
</slot>
|
|
169
|
+
</template>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
</template>
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
<script setup lang="ts">
|
|
177
|
+
import { computed, onMounted, onUnmounted, PropType, ref, toRefs, watch } from "vue";
|
|
178
|
+
import EUITableCheckbox from "./EUITableCheckbox.vue";
|
|
179
|
+
import EUIPageLimit from "./EUIPageLimit.vue";
|
|
180
|
+
import EUIStudentPagination from "./EUIStudentPagination.vue";
|
|
181
|
+
import { capitalizeText } from "../../utils/lodash";
|
|
182
|
+
|
|
183
|
+
interface Header {
|
|
184
|
+
value: string;
|
|
185
|
+
text?: string;
|
|
186
|
+
name?: string;
|
|
187
|
+
width?: number;
|
|
188
|
+
sortable?: boolean;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const props = defineProps({
|
|
192
|
+
checkable: {
|
|
193
|
+
type: Boolean,
|
|
194
|
+
default: false,
|
|
195
|
+
},
|
|
196
|
+
stickyColumn: { type: Boolean, default: true },
|
|
197
|
+
paginated: { type: Boolean, default: false },
|
|
198
|
+
tableExpanded: { type: Boolean, default: false },
|
|
199
|
+
backendPagination: { type: Boolean, default: false },
|
|
200
|
+
checkedRows: { type: Array, default: () => ({}), required: true },
|
|
201
|
+
headers: { type: Array as PropType<Header[]>, default: () => ({}), required: true },
|
|
202
|
+
items: { type: Array, required: true, default: () => ({}) },
|
|
203
|
+
defaultSort: { type: String, default: "" },
|
|
204
|
+
defaultSortDirection: { type: String, default: "asc" },
|
|
205
|
+
search: { type: String, default: "" },
|
|
206
|
+
perPage: { type: Number, default: 5 },
|
|
207
|
+
currentPage: { type: Number, default: 0 },
|
|
208
|
+
total: { type: Number, default: 0 },
|
|
209
|
+
customIsChecked: {
|
|
210
|
+
type: Function as PropType<(item: any, obj: any) => boolean>,
|
|
211
|
+
default: () => () => false,
|
|
212
|
+
},
|
|
213
|
+
isRowCheckable: { type: Function, default: () => true },
|
|
214
|
+
tableHeight: {
|
|
215
|
+
type: String,
|
|
216
|
+
required: false,
|
|
217
|
+
default: ""
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
const {
|
|
222
|
+
defaultSort,
|
|
223
|
+
defaultSortDirection,
|
|
224
|
+
checkedRows,
|
|
225
|
+
currentPage,
|
|
226
|
+
items,
|
|
227
|
+
backendPagination,
|
|
228
|
+
total,
|
|
229
|
+
search,
|
|
230
|
+
headers,
|
|
231
|
+
paginated,
|
|
232
|
+
isRowCheckable,
|
|
233
|
+
customIsChecked,
|
|
234
|
+
} = toRefs(props);
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
const currentSort = ref(defaultSort.value);
|
|
239
|
+
const currentSortDir = ref(defaultSortDirection.value);
|
|
240
|
+
const newCheckedRows = ref([...checkedRows.value]);
|
|
241
|
+
const newCurrentPage = ref(currentPage.value);
|
|
242
|
+
const lastCheckedRowIndex = ref();
|
|
243
|
+
const limit = ref(props.perPage);
|
|
244
|
+
|
|
245
|
+
// Computed Property
|
|
246
|
+
const filteredItems = computed(() => {
|
|
247
|
+
let filteredItems = items.value.slice();
|
|
248
|
+
if (!backendPagination.value && search.value !== "") {
|
|
249
|
+
if (search.value.trim() === "") return filteredItems;
|
|
250
|
+
const props = headers.value.map((h:any) => h.value);
|
|
251
|
+
filteredItems = items.value.filter((row) =>
|
|
252
|
+
props.some((prop) =>
|
|
253
|
+
defaultFilter(
|
|
254
|
+
getValueByPath(row, prop),
|
|
255
|
+
search.value.toString().toLowerCase()
|
|
256
|
+
)
|
|
257
|
+
)
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
return filteredItems;
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
const computedItems = computed(() => {
|
|
264
|
+
let items = filteredItems.value;
|
|
265
|
+
// Sort items before slicing for pagination
|
|
266
|
+
items.sort((a:any, b:any) => {
|
|
267
|
+
const modifier = currentSortDir.value === "desc" ? -1 : 1;
|
|
268
|
+
if (a[currentSort.value] < b[currentSort.value]) return -1 * modifier;
|
|
269
|
+
if (a[currentSort.value] > b[currentSort.value]) return 1 * modifier;
|
|
270
|
+
return 0;
|
|
271
|
+
});
|
|
272
|
+
return items
|
|
273
|
+
|
|
274
|
+
// Apply pagination
|
|
275
|
+
// const start = (newCurrentPage.value - 0) * limit.value;
|
|
276
|
+
// const end = start + limit.value;
|
|
277
|
+
// return items.slice(start, end);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
const searchData = (data: number) => {
|
|
282
|
+
console.log(data);
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const isIndeterminate = computed(() => {
|
|
286
|
+
const validVisibleData = computedItems.value.filter((row) =>
|
|
287
|
+
isRowCheckable.value(row)
|
|
288
|
+
);
|
|
289
|
+
return (
|
|
290
|
+
newCheckedRows.value.length > 0 &&
|
|
291
|
+
newCheckedRows.value.length < validVisibleData.length
|
|
292
|
+
);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
const isAllChecked = computed(() => {
|
|
296
|
+
const validVisibleData = computedItems.value.filter((row) =>
|
|
297
|
+
isRowCheckable.value(row)
|
|
298
|
+
);
|
|
299
|
+
if (validVisibleData.length === 0) return false;
|
|
300
|
+
return validVisibleData.every(
|
|
301
|
+
(row) => indexOf(newCheckedRows.value, row, customIsChecked.value) >= 0
|
|
302
|
+
);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
const isAllUncheckable = computed(() => {
|
|
306
|
+
const validVisibleData = computedItems.value?.filter((row) =>
|
|
307
|
+
isRowCheckable.value!(row)
|
|
308
|
+
);
|
|
309
|
+
return validVisibleData.length === 0;
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// methods
|
|
313
|
+
const defaultFilter = (value:any, search:any) => {
|
|
314
|
+
return value.toString().toLowerCase().includes(search);
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const indexOf = (
|
|
318
|
+
array: any[],
|
|
319
|
+
obj: any,
|
|
320
|
+
fn: (item: any, obj: any) => boolean
|
|
321
|
+
): number => {
|
|
322
|
+
if (!array) return -1;
|
|
323
|
+
if (!fn || typeof fn !== "function") return array?.indexOf(obj);
|
|
324
|
+
for (let i = 0; i < array.length; i++) {
|
|
325
|
+
if (fn(array[i], obj)) {
|
|
326
|
+
return i;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return -1;
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const getValueByPath = (obj: any, path: string) => {
|
|
333
|
+
return path
|
|
334
|
+
.split(".")
|
|
335
|
+
.reduce(
|
|
336
|
+
(o: { [x: string]: any }, i: string | number) => (o ? o[i] : null),
|
|
337
|
+
obj
|
|
338
|
+
);
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
const emit = defineEmits([
|
|
342
|
+
"update:currentPage",
|
|
343
|
+
"changePage",
|
|
344
|
+
"sort",
|
|
345
|
+
"check",
|
|
346
|
+
"check-all",
|
|
347
|
+
"update:checkedRows",
|
|
348
|
+
"changeLimit",
|
|
349
|
+
"mouseenter",
|
|
350
|
+
"mouseleave"
|
|
351
|
+
]);
|
|
352
|
+
|
|
353
|
+
const changeLimit = (limitData: number) => {
|
|
354
|
+
limit.value = limitData;
|
|
355
|
+
newCurrentPage.value = 1;
|
|
356
|
+
emit("update:currentPage", newCurrentPage.value);
|
|
357
|
+
emit("changeLimit", limitData);
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
const pageChanged = (page: number) => {
|
|
361
|
+
newCurrentPage.value = page > 0 ? page : 0;
|
|
362
|
+
emit("update:currentPage", newCurrentPage.value);
|
|
363
|
+
emit("changePage", page);
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
const sortBy = (header: any, event: any) => {
|
|
367
|
+
if (!header || !header.sortable) return;
|
|
368
|
+
if (header.value === currentSort.value) {
|
|
369
|
+
currentSortDir.value = currentSortDir.value === "asc" ? "desc" : "asc";
|
|
370
|
+
}
|
|
371
|
+
currentSort.value = header.value;
|
|
372
|
+
emit("sort", currentSort.value, currentSortDir.value, event);
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
const checkAll = () => {
|
|
376
|
+
const allRowsCheckable = computedItems.value.filter((row) =>
|
|
377
|
+
isRowCheckable.value(row)
|
|
378
|
+
);
|
|
379
|
+
const allChecked = isAllChecked.value;
|
|
380
|
+
newCheckedRows.value = allChecked ? [] : allRowsCheckable.slice();
|
|
381
|
+
emit("check", newCheckedRows.value);
|
|
382
|
+
emit("check-all", newCheckedRows.value);
|
|
383
|
+
emit("update:checkedRows", newCheckedRows.value);
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
const removeCheckedRow = (row:any) => {
|
|
387
|
+
const index = indexOf(newCheckedRows.value, row, customIsChecked.value);
|
|
388
|
+
if (index >= 0) {
|
|
389
|
+
newCheckedRows.value.splice(index, 1);
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
const isRowChecked = (row:any) => {
|
|
394
|
+
return indexOf(newCheckedRows.value, row, customIsChecked.value) >= 0;
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
const checkRow = (row: any, rowIndex: number, event: Event) => {
|
|
398
|
+
if (!isRowCheckable.value(row)) return;
|
|
399
|
+
if(event) {
|
|
400
|
+
lastCheckedRowIndex.value = rowIndex;
|
|
401
|
+
if (!isRowChecked(row)) {
|
|
402
|
+
newCheckedRows.value.push(row);
|
|
403
|
+
} else {
|
|
404
|
+
removeCheckedRow(row);
|
|
405
|
+
}
|
|
406
|
+
emit("check", newCheckedRows.value, row);
|
|
407
|
+
emit("update:checkedRows", newCheckedRows.value);
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
// watch
|
|
412
|
+
watch(
|
|
413
|
+
() => currentPage.value,
|
|
414
|
+
(newVal) => {
|
|
415
|
+
newCurrentPage.value = newVal;
|
|
416
|
+
},
|
|
417
|
+
{ immediate: true }
|
|
418
|
+
);
|
|
419
|
+
watch(
|
|
420
|
+
() => checkedRows.value,
|
|
421
|
+
(rows) => {
|
|
422
|
+
newCheckedRows.value = [...rows];
|
|
423
|
+
},
|
|
424
|
+
{ immediate: true }
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
// table scroll to add class
|
|
428
|
+
const stickyClass = computed(() => {
|
|
429
|
+
return props.checkable && props.stickyColumn
|
|
430
|
+
? {
|
|
431
|
+
head: "bg-gray-100 sticky left-16 top-0 z-20 shadow-[10px_0px_16px_-5px_#e5e6e8]",
|
|
432
|
+
body: "bg-white sticky left-16 top-0 z-10 shadow-[-10px_24px_10px_10px_#e5e6e8]",
|
|
433
|
+
}
|
|
434
|
+
: {
|
|
435
|
+
head: "bg-gray-100 sticky left-0 top-0 z-20 shadow-[10px_0px_16px_-5px_#e5e6e8]",
|
|
436
|
+
body: "bg-white sticky left-0 top-0 z-10 shadow-[-10px_24px_10px_10px_#e5e6e8]",
|
|
437
|
+
};
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
const isOverflowing = ref(false);
|
|
441
|
+
const isScrolled = ref(false)
|
|
442
|
+
const tableContainer = ref<HTMLElement | null>(null);
|
|
443
|
+
|
|
444
|
+
const handleScroll = () => {
|
|
445
|
+
const container = tableContainer.value;
|
|
446
|
+
if (container) {
|
|
447
|
+
isScrolled.value = container.scrollLeft > 0;
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
const checkOverflow = () => {
|
|
452
|
+
const container = tableContainer.value;
|
|
453
|
+
if (container) {
|
|
454
|
+
isOverflowing.value = container.scrollWidth > container.clientWidth;
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
onMounted(() => {
|
|
459
|
+
window.addEventListener('resize', checkOverflow);
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
onUnmounted(() => {
|
|
463
|
+
window.removeEventListener('resize', checkOverflow);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
// Watch for changes in the container's width to check for overflow
|
|
467
|
+
watch(() => tableContainer.value?.clientWidth, checkOverflow);
|
|
468
|
+
</script>
|
|
469
|
+
|
|
470
|
+
<style lang="scss" scoped>
|
|
471
|
+
#student-table {
|
|
472
|
+
& .eui-table {
|
|
473
|
+
@apply min-w-full text-sm font-light text-left table table-auto w-full box-border border-separate relative border-spacing-0;
|
|
474
|
+
|
|
475
|
+
thead tr {
|
|
476
|
+
@apply snap-x snap-mandatory;
|
|
477
|
+
th {
|
|
478
|
+
@apply snap-always snap-start;
|
|
479
|
+
&:first-of-type.checkable {
|
|
480
|
+
@apply min-h-11 flex items-center justify-center px-2 max-w-16 w-16 sticky left-0 top-0 z-20 bg-gray-100;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
tbody {
|
|
486
|
+
@apply snap-y snap-mandatory snap-always;
|
|
487
|
+
tr {
|
|
488
|
+
@apply text-gray-600 transition-colors duration-100 bg-white snap-start ease-in-out snap-x snap-mandatory rounded-sm h-12;
|
|
489
|
+
&:hover {
|
|
490
|
+
@apply bg-purple-50 ring-1 ring-purple-100 ring-inset;
|
|
491
|
+
}
|
|
492
|
+
td {
|
|
493
|
+
@apply px-3 py-1 text-sm font-normal transition-transform duration-100 ease-in-out snap-start snap-always border-solid border border-l-0 border-r-0 border-white first:border-s last:border-e last-of-type:rounded-e-sm first-of-type:rounded-s-sm;
|
|
494
|
+
&:first-of-type.checkable {
|
|
495
|
+
@apply w-12 text-center sticky left-0 top-0 z-[11] bg-white;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
&:hover td {
|
|
499
|
+
@apply font-medium text-gray-900 bg-purple-50 border-purple-100;
|
|
500
|
+
&:first-of-type.checkable {
|
|
501
|
+
@apply bg-purple-50;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
</style>
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<label class="table-checkbox">
|
|
3
|
+
<input v-model="selected" type="checkbox" :value="value" v-bind="mergedAttrs" />
|
|
4
|
+
<span class="check-icon">
|
|
5
|
+
<svg
|
|
6
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
7
|
+
class="size-4"
|
|
8
|
+
viewBox="0 0 20 20"
|
|
9
|
+
fill="currentColor"
|
|
10
|
+
stroke="currentColor"
|
|
11
|
+
stroke-width="0.6"
|
|
12
|
+
>
|
|
13
|
+
<path
|
|
14
|
+
fill-rule="evenodd"
|
|
15
|
+
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
|
16
|
+
clip-rule="evenodd"
|
|
17
|
+
></path>
|
|
18
|
+
</svg>
|
|
19
|
+
</span>
|
|
20
|
+
</label>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<script setup lang="ts">
|
|
24
|
+
import { computed, ref, useAttrs, watch } from 'vue';
|
|
25
|
+
|
|
26
|
+
const props = defineProps({
|
|
27
|
+
indeterminate: Boolean,
|
|
28
|
+
checked: {
|
|
29
|
+
type: Boolean,
|
|
30
|
+
default: false
|
|
31
|
+
},
|
|
32
|
+
value: {
|
|
33
|
+
default: null
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const emit = defineEmits(['change'])
|
|
38
|
+
const isIndeterminate = ref(props.indeterminate)
|
|
39
|
+
|
|
40
|
+
watch(() => props.indeterminate, (val) => {
|
|
41
|
+
isIndeterminate.value = val
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const selected = computed({
|
|
45
|
+
get() {
|
|
46
|
+
return props.checked
|
|
47
|
+
},
|
|
48
|
+
set(val) {
|
|
49
|
+
emit('change', val)
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const isActive = computed(() => {
|
|
54
|
+
const modelIsArray = Array.isArray(props.checked) && props.checked.length !== 0
|
|
55
|
+
|
|
56
|
+
const sameValue = (element: any) => element === props.value
|
|
57
|
+
|
|
58
|
+
if (modelIsArray) {
|
|
59
|
+
return props.checked.some(sameValue)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return props.checked === true
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const attrs = useAttrs()
|
|
66
|
+
const computedTabIndex = computed(() => (attrs.disabled ? -1 : 0))
|
|
67
|
+
|
|
68
|
+
const mergedAttrs = computed(() => ({
|
|
69
|
+
...attrs,
|
|
70
|
+
tabindex: computedTabIndex.value,
|
|
71
|
+
role: 'checkbox',
|
|
72
|
+
// 'aria-checked': isIndeterminate.value ? 'mixed' : isActive.value.toString(),
|
|
73
|
+
// 'aria-disabled': attrs.disabled,
|
|
74
|
+
}))
|
|
75
|
+
|
|
76
|
+
watch(isActive, () => {
|
|
77
|
+
if (props.indeterminate) {
|
|
78
|
+
isIndeterminate.value = false
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
</script>
|
|
82
|
+
|
|
83
|
+
<style lang="scss">
|
|
84
|
+
.table-checkbox {
|
|
85
|
+
@apply size-10 relative flex items-center justify-center rounded-full cursor-pointer z-10 before:content-[''] before:transform before:translate-x-1/2 before:-translate-y-1/2 before:z-[-1] before:absolute before:top-1/2 before:right-1/2 before:block before:size-10 before:rounded-full before:bg-violet-100 before:opacity-0 before:transition-opacity hover:before:opacity-75;
|
|
86
|
+
|
|
87
|
+
& > input[type='checkbox'] {
|
|
88
|
+
@apply size-5 relative border border-solid border-gray-400 cursor-pointer appearance-none rounded-md z-0 accent-violet-600 checked:border-violet-600 checked:bg-violet-600 focus-within:outline-none focus-within:border-2 focus-within:border-violet-600;
|
|
89
|
+
}
|
|
90
|
+
& .check-icon {
|
|
91
|
+
@apply absolute top-1/2 right-1/2 transform translate-x-1/2 -translate-y-1/2 text-white transition-opacity opacity-0 pointer-events-none;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
.table-checkbox input[type='checkbox']:checked ~ .check-icon {
|
|
95
|
+
@apply opacity-100;
|
|
96
|
+
}
|
|
97
|
+
</style>
|