@dolanske/vui 1.0.4 → 1.1.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/LICENSE +673 -673
- package/README.md +42 -42
- package/dist/components/Accordion/Accordion.vue.d.ts +3 -2
- package/dist/components/Accordion/AccordionGroup.vue.d.ts +5 -2
- package/dist/components/Alert/Alert.vue.d.ts +3 -2
- package/dist/components/Avatar/Avatar.vue.d.ts +3 -2
- package/dist/components/Badge/Badge.vue.d.ts +3 -2
- package/dist/components/Breadcrumbs/BreadcrumbItem.vue.d.ts +3 -2
- package/dist/components/Breadcrumbs/Breadcrumbs.vue.d.ts +3 -2
- package/dist/components/Button/Button.vue.d.ts +3 -2
- package/dist/components/ButtonGroup/ButtonGroup.vue.d.ts +3 -2
- package/dist/components/Calendar/Calendar.vue.d.ts +6 -6
- package/dist/components/Card/Card.vue.d.ts +4 -3
- package/dist/components/Checkbox/Checkbox.vue.d.ts +7 -6
- package/dist/components/CopyClipboard/CopyClipboard.vue.d.ts +2 -1
- package/dist/components/Divider/Divider.vue.d.ts +3 -2
- package/dist/components/Drawer/Drawer.vue.d.ts +10 -9
- package/dist/components/Dropdown/Dropdown.vue.d.ts +66 -3
- package/dist/components/Dropdown/DropdownItem.vue.d.ts +3 -2
- package/dist/components/Dropdown/DropdownTitle.vue.d.ts +6 -6
- package/dist/components/Flex/Flex.vue.d.ts +13 -12
- package/dist/components/Grid/Grid.vue.d.ts +7 -6
- package/dist/components/Input/Color.vue.d.ts +5 -5
- package/dist/components/Input/Counter.vue.d.ts +5 -5
- package/dist/components/Input/Dropzone.vue.d.ts +95 -10
- package/dist/components/Input/File.vue.d.ts +4 -3
- package/dist/components/Input/Input.vue.d.ts +7 -6
- package/dist/components/Input/Password.vue.d.ts +1 -1
- package/dist/components/Input/Textarea.vue.d.ts +7 -6
- package/dist/components/Kbd/Kbd.vue.d.ts +1 -1
- package/dist/components/Kbd/KbdGroup.vue.d.ts +2 -1
- package/dist/components/Modal/Confirm.vue.d.ts +7 -9
- package/dist/components/Modal/Modal.vue.d.ts +16 -13
- package/dist/components/OTP/OTP.vue.d.ts +7 -6
- package/dist/components/OTP/OTPItem.vue.d.ts +1 -1
- package/dist/components/Pagination/Pagination.vue.d.ts +3 -2
- package/dist/components/Popout/Popout.vue.d.ts +3 -2
- package/dist/components/Progress/Progress.vue.d.ts +5 -5
- package/dist/components/Radio/Radio.vue.d.ts +7 -6
- package/dist/components/Radio/RadioGroup.vue.d.ts +7 -6
- package/dist/components/Select/Select.vue.d.ts +4 -8
- package/dist/components/Sheet/Sheet.vue.d.ts +22 -13
- package/dist/components/Sidebar/Sidebar.vue.d.ts +7 -6
- package/dist/components/Skeleton/Skeleton.vue.d.ts +1 -1
- package/dist/components/Spinner/Spinner.vue.d.ts +1 -1
- package/dist/components/Switch/Switch.vue.d.ts +7 -6
- package/dist/components/Table/Cell.vue.d.ts +5 -2
- package/dist/components/Table/Head.vue.d.ts +3 -2
- package/dist/components/Table/Root.vue.d.ts +3 -2
- package/dist/components/Table/table.d.ts +2 -2
- package/dist/components/Tabs/Tab.vue.d.ts +3 -2
- package/dist/components/Tabs/Tabs.vue.d.ts +7 -6
- package/dist/components/Toast/toast.d.ts +6 -6
- package/dist/components/Tooltip/Tooltip.vue.d.ts +2 -1
- package/dist/internal/Backdrop/Backdrop.vue.d.ts +3 -2
- package/dist/vui.css +1 -0
- package/dist/vui.js +8090 -7884
- package/package.json +73 -72
- package/src/App.vue +95 -95
- package/src/components/Accordion/Accordion.vue +91 -91
- package/src/components/Accordion/AccordionGroup.vue +43 -43
- package/src/components/Accordion/accordion.scss +82 -82
- package/src/components/Alert/Alert.vue +59 -59
- package/src/components/Alert/alert.scss +161 -161
- package/src/components/Avatar/Avatar.vue +53 -53
- package/src/components/Avatar/avatar.scss +52 -52
- package/src/components/Badge/Badge.vue +21 -21
- package/src/components/Badge/badge.scss +210 -206
- package/src/components/Breadcrumbs/BreadcrumbItem.vue +26 -26
- package/src/components/Breadcrumbs/Breadcrumbs.vue +30 -30
- package/src/components/Breadcrumbs/breadcrumbs.scss +31 -31
- package/src/components/Button/Button.vue +85 -85
- package/src/components/Button/button.scss +279 -279
- package/src/components/ButtonGroup/ButtonGroup.vue +28 -28
- package/src/components/ButtonGroup/button-group.scss +51 -51
- package/src/components/Calendar/Calendar.vue +66 -66
- package/src/components/Calendar/calendar.scss +83 -83
- package/src/components/Card/Card.vue +48 -48
- package/src/components/Card/card.scss +53 -53
- package/src/components/Checkbox/Checkbox.vue +54 -54
- package/src/components/Checkbox/checkbox.scss +80 -80
- package/src/components/CopyClipboard/CopyClipboard.vue +91 -91
- package/src/components/CopyClipboard/copy-clipboard.scss +25 -25
- package/src/components/Divider/Divider.vue +44 -44
- package/src/components/Divider/divider.scss +35 -35
- package/src/components/Drawer/Drawer.vue +102 -97
- package/src/components/Drawer/drawer.scss +37 -37
- package/src/components/Dropdown/Dropdown.vue +135 -135
- package/src/components/Dropdown/DropdownItem.vue +33 -33
- package/src/components/Dropdown/DropdownTitle.vue +14 -14
- package/src/components/Dropdown/dropdown-item.scss +84 -84
- package/src/components/Dropdown/dropdown.scss +53 -53
- package/src/components/Flex/Flex.vue +113 -113
- package/src/components/Grid/Grid.vue +79 -80
- package/src/components/Input/Color.vue +26 -26
- package/src/components/Input/Counter.vue +66 -66
- package/src/components/Input/Dropzone.vue +65 -65
- package/src/components/Input/File.vue +15 -15
- package/src/components/Input/Input.vue +123 -123
- package/src/components/Input/Password.vue +35 -35
- package/src/components/Input/Textarea.vue +78 -78
- package/src/components/Input/input.scss +302 -302
- package/src/components/Kbd/Kbd.vue +48 -48
- package/src/components/Kbd/KbdGroup.vue +27 -27
- package/src/components/Kbd/kbd.scss +19 -19
- package/src/components/Modal/Confirm.vue +56 -56
- package/src/components/Modal/Modal.vue +103 -99
- package/src/components/Modal/modal.scss +54 -54
- package/src/components/OTP/OTP.vue +133 -133
- package/src/components/OTP/OTPItem.vue +37 -37
- package/src/components/OTP/otp.scss +84 -84
- package/src/components/Pagination/Pagination.vue +77 -77
- package/src/components/Pagination/pagination.ts +78 -78
- package/src/components/Popout/Popout.vue +52 -52
- package/src/components/Popout/popout.scss +15 -15
- package/src/components/Progress/Progress.vue +103 -103
- package/src/components/Progress/progress.scss +47 -47
- package/src/components/Radio/Radio.vue +38 -38
- package/src/components/Radio/RadioGroup.vue +40 -40
- package/src/components/Radio/radio.scss +78 -78
- package/src/components/Select/Select.vue +211 -211
- package/src/components/Select/select.scss +77 -77
- package/src/components/Sheet/Sheet.vue +108 -98
- package/src/components/Sheet/sheet.scss +69 -69
- package/src/components/Sidebar/Sidebar.vue +115 -115
- package/src/components/Sidebar/sidebar.scss +124 -124
- package/src/components/Skeleton/Skeleton.vue +43 -43
- package/src/components/Skeleton/skeleton.scss +14 -14
- package/src/components/Spinner/Spinner.vue +42 -42
- package/src/components/Spinner/spinner.scss +47 -47
- package/src/components/Switch/Switch.vue +31 -31
- package/src/components/Switch/switch.scss +93 -93
- package/src/components/Table/Cell.vue +23 -23
- package/src/components/Table/Head.vue +59 -59
- package/src/components/Table/Root.vue +66 -66
- package/src/components/Table/SelectAll.vue +23 -23
- package/src/components/Table/SelectRow.vue +30 -30
- package/src/components/Table/index.ts +7 -7
- package/src/components/Table/table.scss +154 -154
- package/src/components/Table/table.ts +248 -248
- package/src/components/Tabs/Tab.vue +25 -25
- package/src/components/Tabs/Tabs.vue +90 -90
- package/src/components/Tabs/tabs.scss +87 -87
- package/src/components/Toast/Toasts.vue +52 -52
- package/src/components/Toast/toast.scss +45 -45
- package/src/components/Toast/toast.ts +75 -75
- package/src/components/Tooltip/Tooltip.vue +86 -86
- package/src/components/Tooltip/tooltip.scss +8 -8
- package/src/examples/ExampleAccordions.vue +58 -58
- package/src/examples/ExampleAlerts.vue +78 -78
- package/src/examples/ExampleAvatars.vue +44 -44
- package/src/examples/ExampleBadges.vue +48 -48
- package/src/examples/ExampleBreadcrumbs.vue +46 -46
- package/src/examples/ExampleButtons.vue +140 -140
- package/src/examples/ExampleCalendars.vue +40 -40
- package/src/examples/ExampleCards.vue +94 -94
- package/src/examples/ExampleCheckboxes.vue +123 -123
- package/src/examples/ExampleCopyClipboard.vue +47 -47
- package/src/examples/ExampleDividers.vue +39 -39
- package/src/examples/ExampleDrawers.vue +67 -67
- package/src/examples/ExampleDropdowns.vue +114 -114
- package/src/examples/ExampleFlexGrid.vue +124 -122
- package/src/examples/ExampleInputs.vue +234 -234
- package/src/examples/ExampleKBD.vue +65 -65
- package/src/examples/ExampleModals.vue +143 -143
- package/src/examples/ExamplePalette.vue +159 -159
- package/src/examples/ExamplePopouts.vue +41 -41
- package/src/examples/ExampleSheets.vue +77 -77
- package/src/examples/ExampleSidebars.vue +270 -270
- package/src/examples/ExampleSkeletons.vue +26 -26
- package/src/examples/ExampleSpinners.vue +80 -78
- package/src/examples/ExampleTables.vue +195 -195
- package/src/examples/ExampleTabs.vue +119 -119
- package/src/examples/ExampleToasts.vue +96 -96
- package/src/examples/ExampleTooltips.vue +70 -70
- package/src/examples/shared/ExampleColor.vue +28 -28
- package/src/index.ts +116 -116
- package/src/internal/Backdrop/Backdrop.vue +22 -22
- package/src/internal/Backdrop/backdrop.scss +34 -34
- package/src/main.ts +5 -5
- package/src/shared/helpers.ts +117 -117
- package/src/shared/theme.ts +22 -22
- package/src/shared/types.ts +29 -29
- package/src/style/animation.scss +22 -22
- package/src/style/core.scss +119 -125
- package/src/style/layout.scss +207 -233
- package/src/style/media-query.scss +29 -29
- package/src/style/reset.scss +135 -135
- package/src/style/text.scss +137 -124
- package/src/style/theme.scss +195 -195
- package/src/style/tooltip.scss +146 -146
- package/src/style/typography.scss +415 -415
- package/src/style/utils.scss +36 -36
- package/src/style.scss +1 -1
- package/dist/style.css +0 -1
|
@@ -1,248 +1,248 @@
|
|
|
1
|
-
import type { ComputedRef, InjectionKey, MaybeRefOrGetter, Ref } from 'vue'
|
|
2
|
-
import type { DeepRequired } from '../../shared/types'
|
|
3
|
-
import { computed, provide, readonly, ref, toValue } from 'vue'
|
|
4
|
-
import { searchString } from '../../shared/helpers'
|
|
5
|
-
import { paginate } from '../Pagination/pagination'
|
|
6
|
-
|
|
7
|
-
export type BaseRow = Record<string, string | number>
|
|
8
|
-
|
|
9
|
-
export interface TableSelectionProvide {
|
|
10
|
-
selectedRows: Ref<Set<BaseRow>>
|
|
11
|
-
selectRow: (row: BaseRow) => void
|
|
12
|
-
selectAllRows: () => void
|
|
13
|
-
enabled: ComputedRef<boolean>
|
|
14
|
-
isSelectedAll: ComputedRef<boolean>
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const TableSelectionProvideSymbol = Symbol('select-row-provide') as InjectionKey<TableSelectionProvide>
|
|
18
|
-
|
|
19
|
-
interface Sorting<K> {
|
|
20
|
-
key?: K
|
|
21
|
-
type: 'asc' | 'desc'
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface Header {
|
|
25
|
-
label: string
|
|
26
|
-
sortToggle: () => void
|
|
27
|
-
sortKey?: 'asc' | 'desc'
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
interface TableOptionsInput {
|
|
32
|
-
pagination?: {
|
|
33
|
-
enabled?: boolean
|
|
34
|
-
perPage?: number
|
|
35
|
-
maxPages?: number
|
|
36
|
-
}
|
|
37
|
-
select?: boolean
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// eslint-disable-next-line ts/explicit-function-return-type
|
|
41
|
-
export function defineTable<const Dataset extends any[]>(
|
|
42
|
-
computedDataset: MaybeRefOrGetter<Dataset>,
|
|
43
|
-
tableOptions?: TableOptionsInput,
|
|
44
|
-
) {
|
|
45
|
-
const $data = computed(() => toValue(computedDataset))
|
|
46
|
-
|
|
47
|
-
//
|
|
48
|
-
// Reactive options + defaults
|
|
49
|
-
const options = ref(Object.assign({
|
|
50
|
-
pagination: {
|
|
51
|
-
enabled: false,
|
|
52
|
-
perPage: 10,
|
|
53
|
-
maxPages: 3,
|
|
54
|
-
},
|
|
55
|
-
select: false,
|
|
56
|
-
}, tableOptions) as DeepRequired<TableOptionsInput>)
|
|
57
|
-
|
|
58
|
-
//
|
|
59
|
-
// Pagination
|
|
60
|
-
const currentPage = ref(1)
|
|
61
|
-
|
|
62
|
-
const pagination = computed(() => paginate(
|
|
63
|
-
$data.value.length,
|
|
64
|
-
currentPage.value,
|
|
65
|
-
options.value.pagination?.perPage,
|
|
66
|
-
options.value.pagination?.maxPages,
|
|
67
|
-
))
|
|
68
|
-
|
|
69
|
-
const canNextPage = computed(() => pagination.value.currentPage < pagination.value.endPage)
|
|
70
|
-
const canPrevPage = computed(() => pagination.value.currentPage > pagination.value.startPage)
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Sets the currently active data page. Please note that you provide the page
|
|
74
|
-
* number, no its index. So this is 1-indexed input
|
|
75
|
-
*
|
|
76
|
-
* @param page Page number
|
|
77
|
-
*/
|
|
78
|
-
const setPage = (page: number): void => {
|
|
79
|
-
if ((page > currentPage.value && canNextPage.value)
|
|
80
|
-
|| (page < currentPage.value && canPrevPage.value)) {
|
|
81
|
-
currentPage.value = page
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
//
|
|
86
|
-
// Sorting
|
|
87
|
-
|
|
88
|
-
const sorting = ref<Sorting<Ref<keyof Dataset[number]>>>({
|
|
89
|
-
key: undefined,
|
|
90
|
-
type: 'asc',
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
const setSort = (key: keyof Dataset[number], type: 'asc' | 'desc' | 'toggle' = 'asc'): void => {
|
|
94
|
-
sorting.value.key = key
|
|
95
|
-
sorting.value.type = type === 'toggle'
|
|
96
|
-
// Toggle between descending & ascending whenever the set sort fn is called
|
|
97
|
-
? sorting.value.type === 'asc' ? 'desc' : 'asc'
|
|
98
|
-
: type
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const clearSort = (): void => {
|
|
102
|
-
sorting.value.key = undefined
|
|
103
|
-
sorting.value.type = 'asc'
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
//
|
|
107
|
-
// Searching
|
|
108
|
-
const search = ref<string>()
|
|
109
|
-
|
|
110
|
-
//
|
|
111
|
-
// Dataset formatting
|
|
112
|
-
const filteredRows = computed(() => {
|
|
113
|
-
const searchValue = search.value
|
|
114
|
-
let final = $data.value
|
|
115
|
-
|
|
116
|
-
if (searchValue) {
|
|
117
|
-
final = final.filter((row: Dataset[number]) => {
|
|
118
|
-
const matches = Object
|
|
119
|
-
.values(row)
|
|
120
|
-
.map(row => `${row}`)
|
|
121
|
-
return searchString(matches, searchValue)
|
|
122
|
-
}) as Dataset
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const key = sorting.value.key
|
|
126
|
-
|
|
127
|
-
if (key) {
|
|
128
|
-
// FIXME: change to `toSorted` when typescript is ok
|
|
129
|
-
final = [...final].sort((a: Dataset[number], b: Dataset[number]) => {
|
|
130
|
-
const aValue = a[key]
|
|
131
|
-
const bValue = b[key]
|
|
132
|
-
return sorting.value.type === 'asc'
|
|
133
|
-
? aValue > bValue ? 1 : -1
|
|
134
|
-
: aValue > bValue ? -1 : 1
|
|
135
|
-
}) as Dataset
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return final
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
const headers = computed(() => Object
|
|
142
|
-
.keys($data.value[0] || {})
|
|
143
|
-
.map((key) => {
|
|
144
|
-
return {
|
|
145
|
-
label: key,
|
|
146
|
-
sortKey: sorting.value.key === key && sorting.value.type,
|
|
147
|
-
sortToggle: () => {
|
|
148
|
-
// 3-way toggle asc -> desc -> turn off (reset to undefined)
|
|
149
|
-
if (sorting.value.key === key) {
|
|
150
|
-
switch (sorting.value.type) {
|
|
151
|
-
case 'asc': {
|
|
152
|
-
sorting.value.type = 'desc'
|
|
153
|
-
break
|
|
154
|
-
}
|
|
155
|
-
case 'desc': {
|
|
156
|
-
sorting.value.key = undefined
|
|
157
|
-
sorting.value.key = 'asc'
|
|
158
|
-
break
|
|
159
|
-
}
|
|
160
|
-
default: {
|
|
161
|
-
sorting.value.key = key
|
|
162
|
-
sorting.value.type = 'asc'
|
|
163
|
-
break
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
setSort(key)
|
|
169
|
-
}
|
|
170
|
-
},
|
|
171
|
-
} as Header
|
|
172
|
-
}),
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
const rows = computed(() => {
|
|
176
|
-
if (options.value.pagination?.enabled === true) {
|
|
177
|
-
return filteredRows.value.slice(
|
|
178
|
-
pagination.value.startIndex,
|
|
179
|
-
pagination.value.endIndex + 1,
|
|
180
|
-
) as Dataset
|
|
181
|
-
}
|
|
182
|
-
return filteredRows.value as Dataset
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
//
|
|
186
|
-
// Row selecting
|
|
187
|
-
const selectedRows = ref<Set<BaseRow>>(new Set() as Set<BaseRow>)
|
|
188
|
-
const selectingEnabled = computed(() => options.value.select)
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Accepts either an existing index of a row within the dataset or the dataset
|
|
192
|
-
* row itself. If the item is already selected, it will be deselected.
|
|
193
|
-
*
|
|
194
|
-
* @param row {Number | RowObject}
|
|
195
|
-
*/
|
|
196
|
-
function selectRow(row: Dataset[number]): void {
|
|
197
|
-
if (selectedRows.value.has(row)) {
|
|
198
|
-
selectedRows.value.delete(row)
|
|
199
|
-
}
|
|
200
|
-
else {
|
|
201
|
-
selectedRows.value.add(row)
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const isSelectedAll = computed(() => $data.value.length === selectedRows.value.size)
|
|
206
|
-
|
|
207
|
-
function selectAllRows(): void {
|
|
208
|
-
if (isSelectedAll.value) {
|
|
209
|
-
// If the selected indexes have the same length as the data array, we can
|
|
210
|
-
// assume all of them are selected. Therefore we toggle it by deselecting
|
|
211
|
-
// all of them
|
|
212
|
-
selectedRows.value = new Set()
|
|
213
|
-
}
|
|
214
|
-
else {
|
|
215
|
-
const data = new Set<BaseRow>()
|
|
216
|
-
for (const item of $data.value) {
|
|
217
|
-
data.add(item)
|
|
218
|
-
}
|
|
219
|
-
selectedRows.value = new Set(data)
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
provide(TableSelectionProvideSymbol, {
|
|
224
|
-
selectedRows,
|
|
225
|
-
selectRow,
|
|
226
|
-
selectAllRows,
|
|
227
|
-
enabled: selectingEnabled,
|
|
228
|
-
isSelectedAll,
|
|
229
|
-
})
|
|
230
|
-
|
|
231
|
-
return {
|
|
232
|
-
setSort,
|
|
233
|
-
clearSort,
|
|
234
|
-
search,
|
|
235
|
-
rows: readonly(rows),
|
|
236
|
-
allRows: readonly(filteredRows),
|
|
237
|
-
selectedRows: readonly(selectedRows),
|
|
238
|
-
headers: readonly(headers),
|
|
239
|
-
pagination,
|
|
240
|
-
canPrevPage,
|
|
241
|
-
canNextPage,
|
|
242
|
-
setPage,
|
|
243
|
-
options,
|
|
244
|
-
selectRow,
|
|
245
|
-
selectAllRows,
|
|
246
|
-
isSelectedAll,
|
|
247
|
-
}
|
|
248
|
-
}
|
|
1
|
+
import type { ComputedRef, InjectionKey, MaybeRefOrGetter, Ref } from 'vue'
|
|
2
|
+
import type { DeepRequired } from '../../shared/types'
|
|
3
|
+
import { computed, provide, readonly, ref, toValue } from 'vue'
|
|
4
|
+
import { searchString } from '../../shared/helpers'
|
|
5
|
+
import { paginate } from '../Pagination/pagination'
|
|
6
|
+
|
|
7
|
+
export type BaseRow = Record<string, string | number>
|
|
8
|
+
|
|
9
|
+
export interface TableSelectionProvide {
|
|
10
|
+
selectedRows: Ref<Set<BaseRow>>
|
|
11
|
+
selectRow: (row: BaseRow) => void
|
|
12
|
+
selectAllRows: () => void
|
|
13
|
+
enabled: ComputedRef<boolean>
|
|
14
|
+
isSelectedAll: ComputedRef<boolean>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const TableSelectionProvideSymbol = Symbol('select-row-provide') as InjectionKey<TableSelectionProvide>
|
|
18
|
+
|
|
19
|
+
interface Sorting<K> {
|
|
20
|
+
key?: K
|
|
21
|
+
type: 'asc' | 'desc'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface Header {
|
|
25
|
+
label: string
|
|
26
|
+
sortToggle: () => void
|
|
27
|
+
sortKey?: 'asc' | 'desc'
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface TableOptionsInput {
|
|
32
|
+
pagination?: {
|
|
33
|
+
enabled?: boolean
|
|
34
|
+
perPage?: number
|
|
35
|
+
maxPages?: number
|
|
36
|
+
}
|
|
37
|
+
select?: boolean
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// eslint-disable-next-line ts/explicit-function-return-type
|
|
41
|
+
export function defineTable<const Dataset extends any[]>(
|
|
42
|
+
computedDataset: MaybeRefOrGetter<Dataset>,
|
|
43
|
+
tableOptions?: TableOptionsInput,
|
|
44
|
+
) {
|
|
45
|
+
const $data = computed(() => toValue(computedDataset))
|
|
46
|
+
|
|
47
|
+
//
|
|
48
|
+
// Reactive options + defaults
|
|
49
|
+
const options = ref(Object.assign({
|
|
50
|
+
pagination: {
|
|
51
|
+
enabled: false,
|
|
52
|
+
perPage: 10,
|
|
53
|
+
maxPages: 3,
|
|
54
|
+
},
|
|
55
|
+
select: false,
|
|
56
|
+
}, tableOptions) as DeepRequired<TableOptionsInput>)
|
|
57
|
+
|
|
58
|
+
//
|
|
59
|
+
// Pagination
|
|
60
|
+
const currentPage = ref(1)
|
|
61
|
+
|
|
62
|
+
const pagination = computed(() => paginate(
|
|
63
|
+
$data.value.length,
|
|
64
|
+
currentPage.value,
|
|
65
|
+
options.value.pagination?.perPage,
|
|
66
|
+
options.value.pagination?.maxPages,
|
|
67
|
+
))
|
|
68
|
+
|
|
69
|
+
const canNextPage = computed(() => pagination.value.currentPage < pagination.value.endPage)
|
|
70
|
+
const canPrevPage = computed(() => pagination.value.currentPage > pagination.value.startPage)
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Sets the currently active data page. Please note that you provide the page
|
|
74
|
+
* number, no its index. So this is 1-indexed input
|
|
75
|
+
*
|
|
76
|
+
* @param page Page number
|
|
77
|
+
*/
|
|
78
|
+
const setPage = (page: number): void => {
|
|
79
|
+
if ((page > currentPage.value && canNextPage.value)
|
|
80
|
+
|| (page < currentPage.value && canPrevPage.value)) {
|
|
81
|
+
currentPage.value = page
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
//
|
|
86
|
+
// Sorting
|
|
87
|
+
|
|
88
|
+
const sorting = ref<Sorting<Ref<keyof Dataset[number]>>>({
|
|
89
|
+
key: undefined,
|
|
90
|
+
type: 'asc',
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
const setSort = (key: keyof Dataset[number], type: 'asc' | 'desc' | 'toggle' = 'asc'): void => {
|
|
94
|
+
sorting.value.key = key
|
|
95
|
+
sorting.value.type = type === 'toggle'
|
|
96
|
+
// Toggle between descending & ascending whenever the set sort fn is called
|
|
97
|
+
? sorting.value.type === 'asc' ? 'desc' : 'asc'
|
|
98
|
+
: type
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const clearSort = (): void => {
|
|
102
|
+
sorting.value.key = undefined
|
|
103
|
+
sorting.value.type = 'asc'
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
//
|
|
107
|
+
// Searching
|
|
108
|
+
const search = ref<string>()
|
|
109
|
+
|
|
110
|
+
//
|
|
111
|
+
// Dataset formatting
|
|
112
|
+
const filteredRows = computed(() => {
|
|
113
|
+
const searchValue = search.value
|
|
114
|
+
let final = $data.value
|
|
115
|
+
|
|
116
|
+
if (searchValue) {
|
|
117
|
+
final = final.filter((row: Dataset[number]) => {
|
|
118
|
+
const matches = Object
|
|
119
|
+
.values(row)
|
|
120
|
+
.map(row => `${row}`)
|
|
121
|
+
return searchString(matches, searchValue)
|
|
122
|
+
}) as Dataset
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const key = sorting.value.key
|
|
126
|
+
|
|
127
|
+
if (key) {
|
|
128
|
+
// FIXME: change to `toSorted` when typescript is ok
|
|
129
|
+
final = [...final].sort((a: Dataset[number], b: Dataset[number]) => {
|
|
130
|
+
const aValue = a[key]
|
|
131
|
+
const bValue = b[key]
|
|
132
|
+
return sorting.value.type === 'asc'
|
|
133
|
+
? aValue > bValue ? 1 : -1
|
|
134
|
+
: aValue > bValue ? -1 : 1
|
|
135
|
+
}) as Dataset
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return final
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
const headers = computed(() => Object
|
|
142
|
+
.keys($data.value[0] || {})
|
|
143
|
+
.map((key) => {
|
|
144
|
+
return {
|
|
145
|
+
label: key,
|
|
146
|
+
sortKey: sorting.value.key === key && sorting.value.type,
|
|
147
|
+
sortToggle: () => {
|
|
148
|
+
// 3-way toggle asc -> desc -> turn off (reset to undefined)
|
|
149
|
+
if (sorting.value.key === key) {
|
|
150
|
+
switch (sorting.value.type) {
|
|
151
|
+
case 'asc': {
|
|
152
|
+
sorting.value.type = 'desc'
|
|
153
|
+
break
|
|
154
|
+
}
|
|
155
|
+
case 'desc': {
|
|
156
|
+
sorting.value.key = undefined
|
|
157
|
+
sorting.value.key = 'asc'
|
|
158
|
+
break
|
|
159
|
+
}
|
|
160
|
+
default: {
|
|
161
|
+
sorting.value.key = key
|
|
162
|
+
sorting.value.type = 'asc'
|
|
163
|
+
break
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
setSort(key)
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
} as Header
|
|
172
|
+
}),
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
const rows = computed(() => {
|
|
176
|
+
if (options.value.pagination?.enabled === true) {
|
|
177
|
+
return filteredRows.value.slice(
|
|
178
|
+
pagination.value.startIndex,
|
|
179
|
+
pagination.value.endIndex + 1,
|
|
180
|
+
) as Dataset
|
|
181
|
+
}
|
|
182
|
+
return filteredRows.value as Dataset
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
//
|
|
186
|
+
// Row selecting
|
|
187
|
+
const selectedRows = ref<Set<BaseRow>>(new Set() as Set<BaseRow>)
|
|
188
|
+
const selectingEnabled = computed(() => options.value.select)
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Accepts either an existing index of a row within the dataset or the dataset
|
|
192
|
+
* row itself. If the item is already selected, it will be deselected.
|
|
193
|
+
*
|
|
194
|
+
* @param row {Number | RowObject}
|
|
195
|
+
*/
|
|
196
|
+
function selectRow(row: Dataset[number]): void {
|
|
197
|
+
if (selectedRows.value.has(row)) {
|
|
198
|
+
selectedRows.value.delete(row)
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
selectedRows.value.add(row)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const isSelectedAll = computed(() => $data.value.length === selectedRows.value.size)
|
|
206
|
+
|
|
207
|
+
function selectAllRows(): void {
|
|
208
|
+
if (isSelectedAll.value) {
|
|
209
|
+
// If the selected indexes have the same length as the data array, we can
|
|
210
|
+
// assume all of them are selected. Therefore we toggle it by deselecting
|
|
211
|
+
// all of them
|
|
212
|
+
selectedRows.value = new Set()
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
const data = new Set<BaseRow>()
|
|
216
|
+
for (const item of $data.value) {
|
|
217
|
+
data.add(item)
|
|
218
|
+
}
|
|
219
|
+
selectedRows.value = new Set(data)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
provide(TableSelectionProvideSymbol, {
|
|
224
|
+
selectedRows,
|
|
225
|
+
selectRow,
|
|
226
|
+
selectAllRows,
|
|
227
|
+
enabled: selectingEnabled,
|
|
228
|
+
isSelectedAll,
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
setSort,
|
|
233
|
+
clearSort,
|
|
234
|
+
search,
|
|
235
|
+
rows: readonly(rows),
|
|
236
|
+
allRows: readonly(filteredRows),
|
|
237
|
+
selectedRows: readonly(selectedRows),
|
|
238
|
+
headers: readonly(headers),
|
|
239
|
+
pagination,
|
|
240
|
+
canPrevPage,
|
|
241
|
+
canNextPage,
|
|
242
|
+
setPage,
|
|
243
|
+
options,
|
|
244
|
+
selectRow,
|
|
245
|
+
selectAllRows,
|
|
246
|
+
isSelectedAll,
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { Icon } from '@iconify/vue'
|
|
3
|
-
|
|
4
|
-
export interface TabProps {
|
|
5
|
-
disabled?: boolean
|
|
6
|
-
label?: string
|
|
7
|
-
icon?: string
|
|
8
|
-
}
|
|
9
|
-
const props = defineProps<TabProps>()
|
|
10
|
-
</script>
|
|
11
|
-
|
|
12
|
-
<template>
|
|
13
|
-
<button
|
|
14
|
-
class="vui-tab"
|
|
15
|
-
:data-tab-id="label"
|
|
16
|
-
:class="{ disabled: props.disabled }"
|
|
17
|
-
role="tab"
|
|
18
|
-
:name="label"
|
|
19
|
-
>
|
|
20
|
-
<slot>
|
|
21
|
-
<Icon v-if="props.icon" :icon="props.icon" />
|
|
22
|
-
{{ props.label }}
|
|
23
|
-
</slot>
|
|
24
|
-
</button>
|
|
25
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { Icon } from '@iconify/vue'
|
|
3
|
+
|
|
4
|
+
export interface TabProps {
|
|
5
|
+
disabled?: boolean
|
|
6
|
+
label?: string
|
|
7
|
+
icon?: string
|
|
8
|
+
}
|
|
9
|
+
const props = defineProps<TabProps>()
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<button
|
|
14
|
+
class="vui-tab"
|
|
15
|
+
:data-tab-id="label"
|
|
16
|
+
:class="{ disabled: props.disabled }"
|
|
17
|
+
role="tab"
|
|
18
|
+
:name="label"
|
|
19
|
+
>
|
|
20
|
+
<slot>
|
|
21
|
+
<Icon v-if="props.icon" :icon="props.icon" />
|
|
22
|
+
{{ props.label }}
|
|
23
|
+
</slot>
|
|
24
|
+
</button>
|
|
25
|
+
</template>
|