sprintify-ui 0.0.0
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/README.md +188 -0
- package/dist/types/src/components/BaseAlert.vue.d.ts +51 -0
- package/dist/types/src/components/BaseAutocomplete.vue.d.ts +268 -0
- package/dist/types/src/components/BaseAutocompleteFetch.vue.d.ts +273 -0
- package/dist/types/src/components/BaseAvatar.vue.d.ts +126 -0
- package/dist/types/src/components/BaseBadge.vue.d.ts +94 -0
- package/dist/types/src/components/BaseBelongsTo.vue.d.ts +268 -0
- package/dist/types/src/components/BaseBoolean.vue.d.ts +64 -0
- package/dist/types/src/components/BaseBreadcrumbs.vue.d.ts +66 -0
- package/dist/types/src/components/BaseButton.vue.d.ts +23 -0
- package/dist/types/src/components/BaseCard.vue.d.ts +74 -0
- package/dist/types/src/components/BaseCardRow.vue.d.ts +16 -0
- package/dist/types/src/components/BaseClipboard.vue.d.ts +74 -0
- package/dist/types/src/components/BaseContainer.vue.d.ts +34 -0
- package/dist/types/src/components/BaseCounter.vue.d.ts +125 -0
- package/dist/types/src/components/BaseDataIterator.vue.d.ts +345 -0
- package/dist/types/src/components/BaseDataTable.vue.d.ts +657 -0
- package/dist/types/src/components/BaseDataTableToggleColumns.vue.d.ts +1281 -0
- package/dist/types/src/components/BaseDatePicker.vue.d.ts +190 -0
- package/dist/types/src/components/BaseDateSelect.vue.d.ts +171 -0
- package/dist/types/src/components/BaseDescriptionList.vue.d.ts +48 -0
- package/dist/types/src/components/BaseDescriptionListItem.vue.d.ts +49 -0
- package/dist/types/src/components/BaseDialog.vue.d.ts +160 -0
- package/dist/types/src/components/BaseFilePicker.vue.d.ts +44 -0
- package/dist/types/src/components/BaseFileUploader.vue.d.ts +220 -0
- package/dist/types/src/components/BaseInput.vue.d.ts +209 -0
- package/dist/types/src/components/BaseInputLabel.vue.d.ts +31 -0
- package/dist/types/src/components/BaseLoadingCover.vue.d.ts +166 -0
- package/dist/types/src/components/BaseLoadingPage.vue.d.ts +2 -0
- package/dist/types/src/components/BaseMediaLibrary.vue.d.ts +269 -0
- package/dist/types/src/components/BaseMediaLibraryItem.vue.d.ts +75 -0
- package/dist/types/src/components/BaseMenu.vue.d.ts +117 -0
- package/dist/types/src/components/BaseMenuItem.vue.d.ts +147 -0
- package/dist/types/src/components/BaseModalCenter.vue.d.ts +141 -0
- package/dist/types/src/components/BaseModalSide.vue.d.ts +141 -0
- package/dist/types/src/components/BaseNavbar.vue.d.ts +79 -0
- package/dist/types/src/components/BaseNavbarItem.vue.d.ts +80 -0
- package/dist/types/src/components/BaseNavbarItemContent.vue.d.ts +127 -0
- package/dist/types/src/components/BasePagination.vue.d.ts +25 -0
- package/dist/types/src/components/BasePaginationSimple.vue.d.ts +25 -0
- package/dist/types/src/components/BasePanel.vue.d.ts +31 -0
- package/dist/types/src/components/BasePassword.vue.d.ts +66 -0
- package/dist/types/src/components/BaseProcessRing.vue.d.ts +36 -0
- package/dist/types/src/components/BaseReadMore.vue.d.ts +74 -0
- package/dist/types/src/components/BaseSelect.vue.d.ts +55 -0
- package/dist/types/src/components/BaseSideNavigation.vue.d.ts +48 -0
- package/dist/types/src/components/BaseSideNavigationItem.vue.d.ts +92 -0
- package/dist/types/src/components/BaseSkeleton.vue.d.ts +93 -0
- package/dist/types/src/components/BaseSpinner.vue.d.ts +2 -0
- package/dist/types/src/components/BaseSwitch.vue.d.ts +39 -0
- package/dist/types/src/components/BaseSystemAlert.vue.d.ts +141 -0
- package/dist/types/src/components/BaseTabItem.vue.d.ts +70 -0
- package/dist/types/src/components/BaseTable.vue.d.ts +467 -0
- package/dist/types/src/components/BaseTableColumn.vue.d.ts +164 -0
- package/dist/types/src/components/BaseTabs.vue.d.ts +48 -0
- package/dist/types/src/components/BaseTagAutocomplete.vue.d.ts +274 -0
- package/dist/types/src/components/BaseTagAutocompleteFetch.vue.d.ts +251 -0
- package/dist/types/src/components/BaseTextarea.vue.d.ts +228 -0
- package/dist/types/src/components/BaseTextareaAutoresize.vue.d.ts +44 -0
- package/dist/types/src/components/BaseTitle.vue.d.ts +45 -0
- package/dist/types/src/components/BaseWordCount.vue.d.ts +31 -0
- package/dist/types/src/components/SlotComponent.d.ts +43 -0
- package/dist/types/src/components/index.d.ts +2 -0
- package/dist/types/src/composables/breakpoints.d.ts +12 -0
- package/dist/types/src/composables/modal.d.ts +6 -0
- package/dist/types/src/constants/MyConstants.d.ts +1 -0
- package/dist/types/src/constants/index.d.ts +2 -0
- package/dist/types/src/index.d.ts +253 -0
- package/dist/types/src/types/Media.d.ts +8 -0
- package/dist/types/src/types/UploadedFile.d.ts +9 -0
- package/dist/types/src/types/User.d.ts +6 -0
- package/dist/types/src/types/types.d.ts +88 -0
- package/dist/types/src/utils/fileSizeFormat.d.ts +1 -0
- package/dist/types/src/utils/index.d.ts +4 -0
- package/dist/types/src/utils/scrollPreventer.d.ts +4 -0
- package/dist/types/src/utils/toHumanList.d.ts +1 -0
- package/package.json +99 -0
- package/src/assets/button.css +80 -0
- package/src/assets/form.css +15 -0
- package/src/assets/main.css +3 -0
- package/src/assets/pikaday.css +134 -0
- package/src/assets/tailwind.css +5 -0
- package/src/components/BaseAlert.stories.js +52 -0
- package/src/components/BaseAlert.vue +152 -0
- package/src/components/BaseAutocomplete.stories.js +127 -0
- package/src/components/BaseAutocomplete.vue +376 -0
- package/src/components/BaseAutocompleteFetch.stories.js +121 -0
- package/src/components/BaseAutocompleteFetch.vue +185 -0
- package/src/components/BaseAvatar.stories.js +39 -0
- package/src/components/BaseAvatar.vue +92 -0
- package/src/components/BaseBadge.stories.js +61 -0
- package/src/components/BaseBadge.vue +70 -0
- package/src/components/BaseBelongsTo.stories.js +130 -0
- package/src/components/BaseBelongsTo.vue +122 -0
- package/src/components/BaseBoolean.stories.js +35 -0
- package/src/components/BaseBoolean.vue +29 -0
- package/src/components/BaseBreadcrumbs.stories.js +45 -0
- package/src/components/BaseBreadcrumbs.vue +78 -0
- package/src/components/BaseButton.stories.js +80 -0
- package/src/components/BaseButton.vue +39 -0
- package/src/components/BaseCard.stories.js +61 -0
- package/src/components/BaseCard.vue +49 -0
- package/src/components/BaseCardRow.vue +34 -0
- package/src/components/BaseClipboard.stories.js +31 -0
- package/src/components/BaseClipboard.vue +96 -0
- package/src/components/BaseContainer.stories.js +34 -0
- package/src/components/BaseContainer.vue +50 -0
- package/src/components/BaseCounter.stories.js +32 -0
- package/src/components/BaseCounter.vue +72 -0
- package/src/components/BaseDataIterator.stories.js +90 -0
- package/src/components/BaseDataIterator.vue +658 -0
- package/src/components/BaseDataTable.stories.js +95 -0
- package/src/components/BaseDataTable.vue +489 -0
- package/src/components/BaseDataTableToggleColumns.vue +69 -0
- package/src/components/BaseDatePicker.stories.js +53 -0
- package/src/components/BaseDatePicker.vue +166 -0
- package/src/components/BaseDateSelect.vue +192 -0
- package/src/components/BaseDescriptionList.vue +11 -0
- package/src/components/BaseDescriptionListItem.vue +12 -0
- package/src/components/BaseDialog.vue +104 -0
- package/src/components/BaseFilePicker.vue +101 -0
- package/src/components/BaseFileUploader.vue +166 -0
- package/src/components/BaseInput.vue +82 -0
- package/src/components/BaseInputLabel.vue +26 -0
- package/src/components/BaseLoadingCover.vue +84 -0
- package/src/components/BaseLoadingPage.vue +19 -0
- package/src/components/BaseMediaLibrary.vue +281 -0
- package/src/components/BaseMediaLibraryItem.vue +92 -0
- package/src/components/BaseMenu.vue +114 -0
- package/src/components/BaseMenuItem.vue +93 -0
- package/src/components/BaseModalCenter.vue +107 -0
- package/src/components/BaseModalSide.vue +112 -0
- package/src/components/BaseNavbar.vue +72 -0
- package/src/components/BaseNavbarItem.vue +72 -0
- package/src/components/BaseNavbarItemContent.vue +57 -0
- package/src/components/BasePagination.vue +82 -0
- package/src/components/BasePaginationSimple.vue +60 -0
- package/src/components/BasePanel.vue +39 -0
- package/src/components/BasePassword.vue +73 -0
- package/src/components/BaseProcessRing.vue +56 -0
- package/src/components/BaseReadMore.vue +72 -0
- package/src/components/BaseSelect.vue +59 -0
- package/src/components/BaseSideNavigation.vue +7 -0
- package/src/components/BaseSideNavigationItem.vue +42 -0
- package/src/components/BaseSkeleton.vue +24 -0
- package/src/components/BaseSpinner.vue +47 -0
- package/src/components/BaseSwitch.vue +87 -0
- package/src/components/BaseSystemAlert.vue +86 -0
- package/src/components/BaseTabItem.vue +30 -0
- package/src/components/BaseTable.vue +781 -0
- package/src/components/BaseTableColumn.vue +109 -0
- package/src/components/BaseTabs.vue +12 -0
- package/src/components/BaseTagAutocomplete.vue +385 -0
- package/src/components/BaseTagAutocompleteFetch.vue +154 -0
- package/src/components/BaseTextarea.vue +73 -0
- package/src/components/BaseTextareaAutoresize.vue +117 -0
- package/src/components/BaseTitle.vue +80 -0
- package/src/components/BaseWordCount.vue +36 -0
- package/src/components/SlotComponent.ts +37 -0
- package/src/components/index.ts +5 -0
- package/src/composables/breakpoints.ts +6 -0
- package/src/composables/modal.ts +77 -0
- package/src/constants/MyConstants.ts +1 -0
- package/src/constants/index.ts +5 -0
- package/src/env.d.ts +15 -0
- package/src/index.ts +70 -0
- package/src/lang/en.json +56 -0
- package/src/lang/fr.json +56 -0
- package/src/types/Media.ts +9 -0
- package/src/types/UploadedFile.ts +10 -0
- package/src/types/User.ts +7 -0
- package/src/types/types.ts +112 -0
- package/src/utils/fileSizeFormat.ts +15 -0
- package/src/utils/index.ts +5 -0
- package/src/utils/scrollPreventer.ts +21 -0
- package/src/utils/toHumanList.ts +20 -0
|
@@ -0,0 +1,781 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="relative w-full overflow-hidden">
|
|
3
|
+
<div ref="slot" style="display: none">
|
|
4
|
+
<slot />
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<div class="flex flex-col">
|
|
8
|
+
<div
|
|
9
|
+
class="isolate overflow-x-auto overflow-y-auto"
|
|
10
|
+
:style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }"
|
|
11
|
+
>
|
|
12
|
+
<div class="inline-block min-w-full align-middle">
|
|
13
|
+
<div class="relative">
|
|
14
|
+
<table class="min-w-full border-separate border-spacing-0">
|
|
15
|
+
<thead v-if="newColumns.length" ref="thead">
|
|
16
|
+
<tr>
|
|
17
|
+
<th v-if="showDetailRowIcon" class="th" />
|
|
18
|
+
<th
|
|
19
|
+
v-if="checkable && checkboxPosition === 'left'"
|
|
20
|
+
class="th pl-3"
|
|
21
|
+
align="left"
|
|
22
|
+
>
|
|
23
|
+
<input
|
|
24
|
+
type="checkbox"
|
|
25
|
+
autocomplete="off"
|
|
26
|
+
:checked="isAllChecked"
|
|
27
|
+
:disabled="isAllUncheckable"
|
|
28
|
+
:class="checkboxStyle"
|
|
29
|
+
@change="checkAll"
|
|
30
|
+
/>
|
|
31
|
+
</th>
|
|
32
|
+
<th
|
|
33
|
+
v-for="(column, index) in visibleColumns"
|
|
34
|
+
:key="column.newKey + ':' + index + 'header'"
|
|
35
|
+
v-bind="column.thAttrs && column.thAttrs(column)"
|
|
36
|
+
:style="column.style"
|
|
37
|
+
class="th py-2 pl-3 pr-3 text-left"
|
|
38
|
+
@click.stop="sort(column, undefined, $event as any)"
|
|
39
|
+
>
|
|
40
|
+
<button
|
|
41
|
+
type="button"
|
|
42
|
+
class="flex w-full items-center bg-transparent text-left text-sm font-medium leading-tight text-slate-900"
|
|
43
|
+
:class="{
|
|
44
|
+
'cursor-default': !column.sortable,
|
|
45
|
+
'text-blue-600':
|
|
46
|
+
column.sortable && currentSortColumn === column,
|
|
47
|
+
}"
|
|
48
|
+
>
|
|
49
|
+
<span class="mr-1 whitespace-nowrap">{{
|
|
50
|
+
column.label
|
|
51
|
+
}}</span>
|
|
52
|
+
<span
|
|
53
|
+
v-show="column.sortable && currentSortColumn === column"
|
|
54
|
+
>
|
|
55
|
+
<Icon
|
|
56
|
+
icon="mdi:chevron-down"
|
|
57
|
+
class="h-5 w-5 duration-300"
|
|
58
|
+
:class="{
|
|
59
|
+
'rotate-180': isAsc,
|
|
60
|
+
}"
|
|
61
|
+
/>
|
|
62
|
+
</span>
|
|
63
|
+
</button>
|
|
64
|
+
</th>
|
|
65
|
+
<th
|
|
66
|
+
v-if="checkable && checkboxPosition === 'right'"
|
|
67
|
+
class="th pr-3"
|
|
68
|
+
align="right"
|
|
69
|
+
>
|
|
70
|
+
<input
|
|
71
|
+
autocomplete="off"
|
|
72
|
+
type="checkbox"
|
|
73
|
+
:checked="isAllChecked"
|
|
74
|
+
:disabled="isAllUncheckable"
|
|
75
|
+
:class="checkboxStyle"
|
|
76
|
+
@change="checkAll"
|
|
77
|
+
/>
|
|
78
|
+
</th>
|
|
79
|
+
</tr>
|
|
80
|
+
</thead>
|
|
81
|
+
|
|
82
|
+
<tbody class="bg-white">
|
|
83
|
+
<tr v-if="newCheckedRows.length">
|
|
84
|
+
<td
|
|
85
|
+
:colspan="columnCount"
|
|
86
|
+
class="sticky z-10 p-0"
|
|
87
|
+
:style="{
|
|
88
|
+
top: theadHeight + 'px',
|
|
89
|
+
}"
|
|
90
|
+
:class="[borderClasses]"
|
|
91
|
+
>
|
|
92
|
+
<slot
|
|
93
|
+
name="checkedHeader"
|
|
94
|
+
:uncheck-all="uncheckAll"
|
|
95
|
+
:count="newCheckedRows.length"
|
|
96
|
+
:check-rows="newCheckedRows"
|
|
97
|
+
>
|
|
98
|
+
<div
|
|
99
|
+
class="flex items-center justify-between bg-slate-100 py-2 pl-3 pr-2 text-sm"
|
|
100
|
+
>
|
|
101
|
+
<div>
|
|
102
|
+
<span class="mr-3 text-slate-500"
|
|
103
|
+
>{{
|
|
104
|
+
$t('sui.x_rows_selected', {
|
|
105
|
+
count: newCheckedRows.length,
|
|
106
|
+
})
|
|
107
|
+
}}.</span
|
|
108
|
+
>
|
|
109
|
+
<button
|
|
110
|
+
type="button"
|
|
111
|
+
class="mr-3 inline text-slate-700 underline"
|
|
112
|
+
@click="uncheckAll()"
|
|
113
|
+
>
|
|
114
|
+
{{ $t('sui.deselect_all') }}
|
|
115
|
+
</button>
|
|
116
|
+
</div>
|
|
117
|
+
<BaseMenu
|
|
118
|
+
v-if="checkableActions?.length"
|
|
119
|
+
menu-class="w-52"
|
|
120
|
+
:items="checkableActions"
|
|
121
|
+
>
|
|
122
|
+
<template #button="{ open }">
|
|
123
|
+
<div
|
|
124
|
+
class="flex h-10 w-10 items-center justify-center rounded-full border border-slate-300 bg-white duration-150 hover:bg-slate-50"
|
|
125
|
+
:class="[
|
|
126
|
+
open
|
|
127
|
+
? 'ring-2 ring-primary-500 ring-offset-2'
|
|
128
|
+
: false,
|
|
129
|
+
]"
|
|
130
|
+
>
|
|
131
|
+
<Icon icon="heroicons-solid:dots-vertical" />
|
|
132
|
+
</div>
|
|
133
|
+
</template>
|
|
134
|
+
</BaseMenu>
|
|
135
|
+
</div>
|
|
136
|
+
</slot>
|
|
137
|
+
</td>
|
|
138
|
+
</tr>
|
|
139
|
+
|
|
140
|
+
<template v-for="(row, index) in data" :key="index">
|
|
141
|
+
<tr>
|
|
142
|
+
<td
|
|
143
|
+
v-if="showDetailRowIcon"
|
|
144
|
+
class="pl-3"
|
|
145
|
+
:class="borderBottomClasses(index, row)"
|
|
146
|
+
style="width: 36px"
|
|
147
|
+
>
|
|
148
|
+
<button
|
|
149
|
+
type="button"
|
|
150
|
+
class="mr-0 flex h-8 w-8 appearance-none items-center justify-center rounded-full border-0 bg-white text-slate-400 duration-300 hover:bg-slate-100 hover:text-slate-700"
|
|
151
|
+
:class="{
|
|
152
|
+
'rotate-180': isVisibleDetailRow(row),
|
|
153
|
+
}"
|
|
154
|
+
@click.stop="toggleDetails(row)"
|
|
155
|
+
>
|
|
156
|
+
<Icon
|
|
157
|
+
v-if="hasDetailedVisible(row)"
|
|
158
|
+
icon="mdi:chevron-down"
|
|
159
|
+
class="h-5 w-5"
|
|
160
|
+
/>
|
|
161
|
+
</button>
|
|
162
|
+
</td>
|
|
163
|
+
|
|
164
|
+
<td
|
|
165
|
+
v-if="checkable && checkboxPosition === 'left'"
|
|
166
|
+
class="pl-3"
|
|
167
|
+
:class="borderBottomClasses(index, row)"
|
|
168
|
+
>
|
|
169
|
+
<input
|
|
170
|
+
type="checkbox"
|
|
171
|
+
autocomplete="off"
|
|
172
|
+
:disabled="!isRowCheckable(row)"
|
|
173
|
+
:checked="isRowChecked(row)"
|
|
174
|
+
:class="checkboxStyle"
|
|
175
|
+
@click="checkRow(row, index, $event as MouseEvent)"
|
|
176
|
+
/>
|
|
177
|
+
</td>
|
|
178
|
+
|
|
179
|
+
<SlotComponent
|
|
180
|
+
v-for="(column, colindex) in visibleColumns"
|
|
181
|
+
:key="column.newKey + index + ':' + colindex"
|
|
182
|
+
v-bind="column.tdAttrs && column.tdAttrs(row, column)"
|
|
183
|
+
:component="column"
|
|
184
|
+
scoped
|
|
185
|
+
name="default"
|
|
186
|
+
tag="td"
|
|
187
|
+
class="py-3 pl-3 pr-3 text-sm"
|
|
188
|
+
:class="borderBottomClasses(index, row)"
|
|
189
|
+
:data-label="column.label"
|
|
190
|
+
:props="{ row, column, index, colindex, toggleDetails }"
|
|
191
|
+
@click="
|
|
192
|
+
$emit(
|
|
193
|
+
'cell-click',
|
|
194
|
+
row,
|
|
195
|
+
column,
|
|
196
|
+
index,
|
|
197
|
+
colindex,
|
|
198
|
+
$event
|
|
199
|
+
)
|
|
200
|
+
"
|
|
201
|
+
/>
|
|
202
|
+
|
|
203
|
+
<td
|
|
204
|
+
v-if="checkable && checkboxPosition === 'right'"
|
|
205
|
+
class="pr-3"
|
|
206
|
+
:class="borderBottomClasses(index, row)"
|
|
207
|
+
align="right"
|
|
208
|
+
>
|
|
209
|
+
<input
|
|
210
|
+
type="checkbox"
|
|
211
|
+
autocomplete="off"
|
|
212
|
+
:disabled="!isRowCheckable(row)"
|
|
213
|
+
:checked="isRowChecked(row)"
|
|
214
|
+
:class="checkboxStyle"
|
|
215
|
+
@click="checkRow(row, index, $event as MouseEvent)"
|
|
216
|
+
/>
|
|
217
|
+
</td>
|
|
218
|
+
</tr>
|
|
219
|
+
|
|
220
|
+
<transition :name="detailTransition">
|
|
221
|
+
<tr v-if="isActiveDetailRow(row)" :key="index + 'detail'">
|
|
222
|
+
<td
|
|
223
|
+
:colspan="columnCount"
|
|
224
|
+
class="td"
|
|
225
|
+
:class="borderBottomDetailClasses(index)"
|
|
226
|
+
>
|
|
227
|
+
<slot name="detail" :row="row" :index="index" />
|
|
228
|
+
</td>
|
|
229
|
+
</tr>
|
|
230
|
+
</transition>
|
|
231
|
+
</template>
|
|
232
|
+
|
|
233
|
+
<tr v-if="data.length == 0">
|
|
234
|
+
<td :colspan="columnCount">
|
|
235
|
+
<slot name="empty" />
|
|
236
|
+
</td>
|
|
237
|
+
</tr>
|
|
238
|
+
</tbody>
|
|
239
|
+
</table>
|
|
240
|
+
|
|
241
|
+
<slot name="loading">
|
|
242
|
+
<BaseLoadingCover
|
|
243
|
+
:delay="0"
|
|
244
|
+
:model-value="loading"
|
|
245
|
+
backdrop-class="bg-white bg-opacity-50"
|
|
246
|
+
/>
|
|
247
|
+
<div v-if="loading" class="h-[300px]" />
|
|
248
|
+
</slot>
|
|
249
|
+
</div>
|
|
250
|
+
</div>
|
|
251
|
+
</div>
|
|
252
|
+
</div>
|
|
253
|
+
</div>
|
|
254
|
+
</template>
|
|
255
|
+
|
|
256
|
+
<script lang="ts">
|
|
257
|
+
export default {
|
|
258
|
+
name: 'BaseTable',
|
|
259
|
+
inheritAttrs: false,
|
|
260
|
+
};
|
|
261
|
+
</script>
|
|
262
|
+
|
|
263
|
+
<script lang="ts" setup>
|
|
264
|
+
import { PropType, ref, Ref } from 'vue';
|
|
265
|
+
import { BaseTableColumn, MenuItemInterface, Row } from '@/types/types';
|
|
266
|
+
import SlotComponent from './SlotComponent';
|
|
267
|
+
import { useResizeObserver } from '@vueuse/core';
|
|
268
|
+
import { debounce, isArray } from 'lodash';
|
|
269
|
+
|
|
270
|
+
const checkboxStyle =
|
|
271
|
+
'disabled:bg-slate-100 disabled:border-slate-300 disabled:cursor-not-allowed border-slate-400 rounded';
|
|
272
|
+
|
|
273
|
+
provide('table', getCurrentInstance());
|
|
274
|
+
|
|
275
|
+
const props = defineProps({
|
|
276
|
+
/** Table data */
|
|
277
|
+
data: {
|
|
278
|
+
type: Array as PropType<Row[]>,
|
|
279
|
+
default: () => [],
|
|
280
|
+
},
|
|
281
|
+
/** Loading state */
|
|
282
|
+
loading: {
|
|
283
|
+
default: false,
|
|
284
|
+
type: Boolean,
|
|
285
|
+
},
|
|
286
|
+
visibleColumns: {
|
|
287
|
+
default: undefined,
|
|
288
|
+
type: Array as PropType<number[]>,
|
|
289
|
+
},
|
|
290
|
+
/** Allow row details */
|
|
291
|
+
detailed: {
|
|
292
|
+
default: false,
|
|
293
|
+
type: Boolean,
|
|
294
|
+
},
|
|
295
|
+
/** Rows can be checked (multiple), checked rows will have a .is-checked class if you want to style */
|
|
296
|
+
checkable: {
|
|
297
|
+
default: false,
|
|
298
|
+
type: Boolean,
|
|
299
|
+
},
|
|
300
|
+
/** Define checkable actions */
|
|
301
|
+
checkableActions: {
|
|
302
|
+
default: undefined,
|
|
303
|
+
type: Array as PropType<MenuItemInterface[]>,
|
|
304
|
+
},
|
|
305
|
+
/**
|
|
306
|
+
* Position of the checkbox (if checkable is true)
|
|
307
|
+
* @values left, right
|
|
308
|
+
*/
|
|
309
|
+
checkboxPosition: {
|
|
310
|
+
type: String as PropType<'left' | 'right'>,
|
|
311
|
+
default: 'left',
|
|
312
|
+
},
|
|
313
|
+
/** Custom method to verify if a row is checkable, works when is checkable */
|
|
314
|
+
isRowCheckable: {
|
|
315
|
+
type: Function,
|
|
316
|
+
default: () => true,
|
|
317
|
+
},
|
|
318
|
+
/** Set which rows are checked, use v-model:checkedRows to make it two-way binding */
|
|
319
|
+
checkedRows: {
|
|
320
|
+
default: () => [],
|
|
321
|
+
type: Array as PropType<Row[]>,
|
|
322
|
+
},
|
|
323
|
+
/** Sets the default sort column field */
|
|
324
|
+
sortField: {
|
|
325
|
+
type: String,
|
|
326
|
+
default: '',
|
|
327
|
+
},
|
|
328
|
+
/**
|
|
329
|
+
* Sets the default sort column direction
|
|
330
|
+
* @values asc, desc
|
|
331
|
+
*/
|
|
332
|
+
sortDirection: {
|
|
333
|
+
type: String,
|
|
334
|
+
default: 'asc',
|
|
335
|
+
},
|
|
336
|
+
/** Controls the visibility of the trigger that toggles the detailed rows. */
|
|
337
|
+
hasDetailedVisible: {
|
|
338
|
+
type: Function,
|
|
339
|
+
default: () => true,
|
|
340
|
+
},
|
|
341
|
+
/** Use a unique key of your data Object when use detailed or opened detailed. (id recommended) */
|
|
342
|
+
rowKey: {
|
|
343
|
+
type: String,
|
|
344
|
+
default: 'id',
|
|
345
|
+
},
|
|
346
|
+
/* Transition name to use when toggling row details. */
|
|
347
|
+
detailTransition: {
|
|
348
|
+
type: String,
|
|
349
|
+
default: '',
|
|
350
|
+
},
|
|
351
|
+
/* Max height (in px) */
|
|
352
|
+
maxHeight: {
|
|
353
|
+
default: undefined,
|
|
354
|
+
type: Number,
|
|
355
|
+
},
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
const emit = defineEmits([
|
|
359
|
+
'check',
|
|
360
|
+
'check-all',
|
|
361
|
+
'update:checkedRows',
|
|
362
|
+
'details-open',
|
|
363
|
+
'details-close',
|
|
364
|
+
'update:openedDetailed',
|
|
365
|
+
'sort',
|
|
366
|
+
'cell-click',
|
|
367
|
+
]);
|
|
368
|
+
|
|
369
|
+
const visibleDetailRows = ref([]) as Ref<Row[]>;
|
|
370
|
+
const newCheckedRows = ref([...props.checkedRows]) as Ref<Row[]>;
|
|
371
|
+
const lastCheckedRowIndex = ref(null) as Ref<number | null>;
|
|
372
|
+
const currentSortColumn = ref(null) as Ref<BaseTableColumn | null>;
|
|
373
|
+
const isAsc = ref(true);
|
|
374
|
+
const defaultSlots = ref([]) as Ref<BaseTableColumn[]>;
|
|
375
|
+
const sequence = ref(1);
|
|
376
|
+
|
|
377
|
+
const slot = ref(null) as Ref<HTMLElement | null>;
|
|
378
|
+
const thead = ref(null) as Ref<HTMLElement | null>;
|
|
379
|
+
const theadHeight = ref(0);
|
|
380
|
+
|
|
381
|
+
useResizeObserver(thead, () => setTheadHeightDebounce());
|
|
382
|
+
|
|
383
|
+
const setTheadHeightDebounce = debounce(() => {
|
|
384
|
+
setTheadHeight();
|
|
385
|
+
}, 100);
|
|
386
|
+
|
|
387
|
+
function setTheadHeight() {
|
|
388
|
+
if (thead.value) {
|
|
389
|
+
theadHeight.value = thead.value.clientHeight;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const newColumns = computed(() => {
|
|
394
|
+
return defaultSlots.value;
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
const visibleColumns = computed(() => {
|
|
398
|
+
if (!newColumns.value) {
|
|
399
|
+
return newColumns.value;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return newColumns.value.filter((column: BaseTableColumn) => {
|
|
403
|
+
if (column.visible === false) {
|
|
404
|
+
return false;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (column.alwaysVisible) {
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (!isArray(props.visibleColumns)) {
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (props.visibleColumns.includes(column.newKey)) {
|
|
416
|
+
return true;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return false;
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Return total column count based if it's checkable or expanded
|
|
425
|
+
*/
|
|
426
|
+
const columnCount = computed(() => {
|
|
427
|
+
let count = visibleColumns.value.length;
|
|
428
|
+
count += props.checkable ? 1 : 0;
|
|
429
|
+
count += props.detailed ? 1 : 0;
|
|
430
|
+
|
|
431
|
+
return count;
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Return if detailed row tabled
|
|
436
|
+
* will be with chevron column & icon or not
|
|
437
|
+
*/
|
|
438
|
+
const showDetailRowIcon = computed(() => {
|
|
439
|
+
return props.detailed;
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* When checkedRows prop change, update internal value without
|
|
444
|
+
* mutating original data.
|
|
445
|
+
*/
|
|
446
|
+
watch(
|
|
447
|
+
() => props.checkedRows,
|
|
448
|
+
(rows) => {
|
|
449
|
+
newCheckedRows.value = [...rows];
|
|
450
|
+
},
|
|
451
|
+
{ deep: true }
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
watch(
|
|
455
|
+
() => props.sortField,
|
|
456
|
+
() => {
|
|
457
|
+
updateSortState();
|
|
458
|
+
}
|
|
459
|
+
);
|
|
460
|
+
|
|
461
|
+
watch(
|
|
462
|
+
() => props.sortDirection,
|
|
463
|
+
() => {
|
|
464
|
+
updateSortState();
|
|
465
|
+
}
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
onMounted(() => {
|
|
469
|
+
nextTick(() => {
|
|
470
|
+
updateSortState();
|
|
471
|
+
});
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Sort the column.
|
|
476
|
+
* Toggle current direction on column if it's sortable
|
|
477
|
+
* and not just updating the prop.
|
|
478
|
+
*/
|
|
479
|
+
function sort(column: BaseTableColumn, updatingData = false, event = null) {
|
|
480
|
+
if (!column || !column.sortable) {
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if (!updatingData) {
|
|
485
|
+
isAsc.value =
|
|
486
|
+
column === currentSortColumn.value
|
|
487
|
+
? !isAsc.value
|
|
488
|
+
: props.sortDirection.toLowerCase() !== 'desc';
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* @property {string} field column field
|
|
493
|
+
* @property {boolean} direction 'asc' or 'desc'
|
|
494
|
+
* @property {Event} event native event
|
|
495
|
+
*/
|
|
496
|
+
emit('sort', column.field, isAsc.value ? 'asc' : 'desc', event);
|
|
497
|
+
|
|
498
|
+
currentSortColumn.value = column;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Check if the row is checked (is added to the array).
|
|
503
|
+
*/
|
|
504
|
+
function isRowChecked(row: Row): boolean {
|
|
505
|
+
return (
|
|
506
|
+
newCheckedRows.value.find((r) => r[props.rowKey] == row[props.rowKey]) !==
|
|
507
|
+
undefined
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Check if all rows in the page are checkable.
|
|
513
|
+
*/
|
|
514
|
+
const isAllUncheckable = computed(() => {
|
|
515
|
+
const validData = props.data.filter((row) => props.isRowCheckable(row));
|
|
516
|
+
return validData.length === 0;
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Check if all rows in the page are checked.
|
|
521
|
+
*/
|
|
522
|
+
const isAllChecked = computed(() => {
|
|
523
|
+
const validData = props.data.filter((row) => {
|
|
524
|
+
return props.isRowCheckable(row);
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
if (validData.length === 0) {
|
|
528
|
+
return false;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
const missingChecked = validData.some((currentRow) => {
|
|
532
|
+
return !isRowChecked(currentRow);
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
return !missingChecked;
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Remove a checked row from the array.
|
|
540
|
+
*/
|
|
541
|
+
function removeCheckedRow(row: Row) {
|
|
542
|
+
const index = newCheckedRows.value.findIndex(
|
|
543
|
+
(r) => r[props.rowKey] == row[props.rowKey]
|
|
544
|
+
);
|
|
545
|
+
if (index >= 0) {
|
|
546
|
+
newCheckedRows.value.splice(index, 1);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Header checkbox click listener.
|
|
552
|
+
* Add or remove all rows in current page.
|
|
553
|
+
*/
|
|
554
|
+
function checkAll() {
|
|
555
|
+
if (isAllChecked.value) {
|
|
556
|
+
newCheckedRows.value = [];
|
|
557
|
+
} else {
|
|
558
|
+
props.data.forEach((currentRow) => {
|
|
559
|
+
if (props.isRowCheckable(currentRow)) {
|
|
560
|
+
newCheckedRows.value.push(currentRow);
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
sendCheckUpdate();
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Remove all rows in current page.
|
|
570
|
+
*/
|
|
571
|
+
function uncheckAll() {
|
|
572
|
+
newCheckedRows.value = [];
|
|
573
|
+
|
|
574
|
+
sendCheckUpdate();
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function sendCheckUpdate() {
|
|
578
|
+
emit('check', newCheckedRows.value);
|
|
579
|
+
emit('check-all', newCheckedRows.value);
|
|
580
|
+
emit('update:checkedRows', newCheckedRows.value);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Row checkbox click listener.
|
|
585
|
+
*/
|
|
586
|
+
function checkRow(row: Row, index: number, event: MouseEvent) {
|
|
587
|
+
if (!props.isRowCheckable(row)) {
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const lastIndex = lastCheckedRowIndex.value;
|
|
592
|
+
lastCheckedRowIndex.value = index;
|
|
593
|
+
|
|
594
|
+
if (event.shiftKey && lastIndex !== null && index !== lastIndex) {
|
|
595
|
+
shiftCheckRow(row, index, lastIndex);
|
|
596
|
+
} else if (!isRowChecked(row)) {
|
|
597
|
+
newCheckedRows.value.push(row);
|
|
598
|
+
} else {
|
|
599
|
+
removeCheckedRow(row);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
emit('check', newCheckedRows.value, row);
|
|
603
|
+
|
|
604
|
+
// Emit checked rows to update user variable
|
|
605
|
+
emit('update:checkedRows', newCheckedRows.value);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* Check row when shift is pressed.
|
|
610
|
+
*/
|
|
611
|
+
function shiftCheckRow(row: Row, index: number, lastCheckedRowIndex: number) {
|
|
612
|
+
// Get the subset of the list between the two indices
|
|
613
|
+
const subset = props.data.slice(
|
|
614
|
+
Math.min(index, lastCheckedRowIndex),
|
|
615
|
+
Math.max(index, lastCheckedRowIndex) + 1
|
|
616
|
+
);
|
|
617
|
+
|
|
618
|
+
// Determine the operation based on the state of the clicked checkbox
|
|
619
|
+
const shouldCheck = !isRowChecked(row);
|
|
620
|
+
|
|
621
|
+
subset.forEach((item) => {
|
|
622
|
+
removeCheckedRow(item);
|
|
623
|
+
if (shouldCheck && props.isRowCheckable(item)) {
|
|
624
|
+
newCheckedRows.value.push(item);
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Toggle to show/hide details slot
|
|
631
|
+
*/
|
|
632
|
+
function toggleDetails(row: Row) {
|
|
633
|
+
const found = isVisibleDetailRow(row);
|
|
634
|
+
|
|
635
|
+
if (found) {
|
|
636
|
+
closeDetailRow(row);
|
|
637
|
+
emit('details-close', row);
|
|
638
|
+
} else {
|
|
639
|
+
openDetailRow(row);
|
|
640
|
+
emit('details-open', row);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// Syncs the detailed rows with the parent component
|
|
644
|
+
emit('update:openedDetailed', visibleDetailRows.value);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
function openDetailRow(row: Row) {
|
|
648
|
+
const index = getDetailedIndex(row);
|
|
649
|
+
visibleDetailRows.value.push(index);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
function closeDetailRow(row: Row) {
|
|
653
|
+
const index = getDetailedIndex(row);
|
|
654
|
+
const i = visibleDetailRows.value.indexOf(index);
|
|
655
|
+
if (i >= 0) {
|
|
656
|
+
visibleDetailRows.value.splice(i, 1);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function isVisibleDetailRow(row: Row) {
|
|
661
|
+
const index = getDetailedIndex(row);
|
|
662
|
+
return visibleDetailRows.value.indexOf(index) >= 0;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function isActiveDetailRow(row: Row) {
|
|
666
|
+
return props.detailed && isVisibleDetailRow(row);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* When the rowKey is defined we use the object[rowKey] as index.
|
|
671
|
+
* If not, use the object reference by default.
|
|
672
|
+
*/
|
|
673
|
+
function getDetailedIndex(row: Row) {
|
|
674
|
+
const key = props.rowKey;
|
|
675
|
+
return !key.length || !row ? row : row[key];
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Update sort state
|
|
680
|
+
*/
|
|
681
|
+
function updateSortState() {
|
|
682
|
+
const sortField = props.sortField;
|
|
683
|
+
|
|
684
|
+
const sortDirection = props.sortDirection;
|
|
685
|
+
|
|
686
|
+
const sortColumn = newColumns.value.filter(
|
|
687
|
+
(column) => column.field === sortField
|
|
688
|
+
)[0];
|
|
689
|
+
|
|
690
|
+
// Set sort state
|
|
691
|
+
|
|
692
|
+
if (sortColumn) {
|
|
693
|
+
currentSortColumn.value = sortColumn;
|
|
694
|
+
isAsc.value = sortDirection.toLowerCase() !== 'desc';
|
|
695
|
+
} else {
|
|
696
|
+
currentSortColumn.value = null;
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/*
|
|
702
|
+
|--------------------------------------------------------------------------
|
|
703
|
+
| BaseTableColumns functions
|
|
704
|
+
|--------------------------------------------------------------------------
|
|
705
|
+
*/
|
|
706
|
+
|
|
707
|
+
function addColumn(column: BaseTableColumn) {
|
|
708
|
+
defaultSlots.value.push(column);
|
|
709
|
+
|
|
710
|
+
const slotHTMLElement = slot.value as HTMLElement;
|
|
711
|
+
|
|
712
|
+
if (slotHTMLElement && slotHTMLElement.children) {
|
|
713
|
+
nextTick(() => {
|
|
714
|
+
const ids = defaultSlots.value
|
|
715
|
+
.map((it) => `[data-id="${it.newKey}"]`)
|
|
716
|
+
.join(',');
|
|
717
|
+
|
|
718
|
+
const sortedIds = Array.from(slotHTMLElement.querySelectorAll(ids)).map(
|
|
719
|
+
(el: Element) => el.getAttribute('data-id')
|
|
720
|
+
);
|
|
721
|
+
|
|
722
|
+
defaultSlots.value = defaultSlots.value.sort((a, b) => {
|
|
723
|
+
return (
|
|
724
|
+
sortedIds.indexOf(`${a.newKey}`) - sortedIds.indexOf(`${b.newKey}`)
|
|
725
|
+
);
|
|
726
|
+
});
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
function removeColumn(column: BaseTableColumn) {
|
|
732
|
+
defaultSlots.value = defaultSlots.value.filter(
|
|
733
|
+
(d) => d.newKey !== column.newKey
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
const borderClasses = 'border-b border-slate-300';
|
|
738
|
+
|
|
739
|
+
function borderBottomClasses(index: number, row: Record<string, any>): string {
|
|
740
|
+
if (index < props.data.length - 1) {
|
|
741
|
+
return borderClasses;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
if (isActiveDetailRow(row)) {
|
|
745
|
+
return borderClasses;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
return '';
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
function borderBottomDetailClasses(index: number): string {
|
|
752
|
+
if (index < props.data.length - 1) {
|
|
753
|
+
return borderClasses;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
return '';
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
function nextSequence() {
|
|
760
|
+
return sequence.value++;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
provide('addColumn', addColumn);
|
|
764
|
+
provide('removeColumn', removeColumn);
|
|
765
|
+
provide('nextSequence', nextSequence);
|
|
766
|
+
|
|
767
|
+
defineExpose({
|
|
768
|
+
newColumns,
|
|
769
|
+
});
|
|
770
|
+
</script>
|
|
771
|
+
|
|
772
|
+
<style scoped>
|
|
773
|
+
.th {
|
|
774
|
+
@apply bg-slate-50;
|
|
775
|
+
@apply sticky;
|
|
776
|
+
@apply top-0;
|
|
777
|
+
@apply z-10;
|
|
778
|
+
@apply border-b border-slate-300;
|
|
779
|
+
@apply bg-opacity-75 backdrop-blur backdrop-filter;
|
|
780
|
+
}
|
|
781
|
+
</style>
|