@soft-stech/bootsman-ui-shadcn 2.0.21 → 2.0.22
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/BuiPaginationCommon.vue_vue_type_script_setup_true_lang-BOuWIF4c.js +179 -0
- package/dist/{BuiScrollArea.vue_vue_type_script_setup_true_lang-O7VUcRC4.js → BuiScrollArea.vue_vue_type_script_setup_true_lang-lyWD8KAT.js} +1 -1
- package/dist/{BuiScrollBar.vue_vue_type_script_setup_true_lang-cV0od8j0.js → BuiScrollBar.vue_vue_type_script_setup_true_lang-BCvjzEmb.js} +4 -4
- package/dist/BuiTable.vue_vue_type_script_setup_true_lang-BQRl7YR1.js +37 -0
- package/dist/{BuiTableEmpty.vue_vue_type_script_setup_true_lang-Da4qHIWo.js → BuiTableEmpty.vue_vue_type_script_setup_true_lang-CuffOAuP.js} +1 -1
- package/dist/BuiTableRow.vue_vue_type_script_setup_true_lang-BQnadEa7.js +51 -0
- package/dist/components/pagination/BuiPaginationCommon.js +1 -1
- package/dist/components/pagination/BuiPaginationCommon.vue.d.ts +4 -0
- package/dist/components/pagination/index.js +1 -1
- package/dist/components/scroll-area/BuiScrollArea.js +1 -1
- package/dist/components/scroll-area/BuiScrollBar.js +1 -1
- package/dist/components/scroll-area/index.js +2 -2
- package/dist/components/table/BuiDataTable.vue.d.ts +8 -4
- package/dist/components/table/BuiTable.js +1 -1
- package/dist/components/table/BuiTableEmpty.js +1 -1
- package/dist/components/table/BuiTableRow.js +1 -1
- package/dist/components/table/index.d.ts +1 -0
- package/dist/components/table/index.js +713 -672
- package/dist/index.js +6 -6
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/pagination/BuiPaginationCommon.vue +16 -4
- package/src/components/scroll-area/BuiScrollBar.vue +4 -4
- package/src/components/table/BuiDataTable.vue +113 -23
- package/src/components/table/BuiTable.vue +1 -0
- package/src/components/table/BuiTableRow.vue +6 -0
- package/src/components/table/index.ts +2 -0
- package/src/stories/BuiDataTable.stories.ts +13 -0
- package/src/stories/components/BuiDataTableStory.vue +3 -3
- package/src/stories/components/BuiDataTableWithScrollStory.vue +292 -0
- package/dist/BuiPaginationCommon.vue_vue_type_script_setup_true_lang-DhSRYKth.js +0 -170
- package/dist/BuiTable.vue_vue_type_script_setup_true_lang-CQpc0Sr1.js +0 -36
- package/dist/BuiTableRow.vue_vue_type_script_setup_true_lang-BJk8Yk1B.js +0 -54
|
@@ -1,17 +1,38 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { PaginationState } from '@tanstack/vue-table'
|
|
3
|
+
|
|
4
|
+
export type PaginationAutoState = PaginationState & {
|
|
5
|
+
pageAuto?: boolean
|
|
6
|
+
}
|
|
7
|
+
</script>
|
|
8
|
+
|
|
1
9
|
<script setup lang="ts" generic="TData, TValue">
|
|
10
|
+
import { BuiButton } from '@/components/button'
|
|
2
11
|
import {
|
|
3
12
|
BuiCollapsible,
|
|
4
13
|
BuiCollapsibleContent,
|
|
5
14
|
BuiCollapsibleTrigger
|
|
6
15
|
} from '@/components/collapsible'
|
|
16
|
+
import {
|
|
17
|
+
BuiCommand,
|
|
18
|
+
BuiCommandEmpty,
|
|
19
|
+
BuiCommandInput,
|
|
20
|
+
BuiCommandItem,
|
|
21
|
+
BuiCommandList,
|
|
22
|
+
BuiCommandSeparator
|
|
23
|
+
} from '@/components/command'
|
|
24
|
+
import { BuiContextMenuContent, BuiContextMenuItem } from '@/components/context-menu'
|
|
7
25
|
import { BuiPaginationCommon, type PageSize } from '@/components/pagination'
|
|
26
|
+
import { BuiPopover, BuiPopoverContent, BuiPopoverTrigger } from '@/components/popover'
|
|
27
|
+
import { BuiScrollArea } from '@/components/scroll-area'
|
|
8
28
|
import BuiTableRowSubrow from '@/components/table/BuiTableRowSubrow.vue'
|
|
29
|
+
import { useResizeColumns } from '@/lib/useResizeColumns'
|
|
30
|
+
import { cn, valueUpdater } from '@/lib/utils'
|
|
9
31
|
import type {
|
|
10
32
|
Column,
|
|
11
33
|
ColumnDef,
|
|
12
34
|
ColumnOrderState,
|
|
13
35
|
Header,
|
|
14
|
-
PaginationState,
|
|
15
36
|
Row,
|
|
16
37
|
RowSelectionState,
|
|
17
38
|
SortingState,
|
|
@@ -24,8 +45,18 @@ import {
|
|
|
24
45
|
getSortedRowModel,
|
|
25
46
|
useVueTable
|
|
26
47
|
} from '@tanstack/vue-table'
|
|
27
|
-
import { computed, watchEffect, ref, watch, onMounted, onBeforeMount, nextTick } from 'vue'
|
|
28
48
|
import {
|
|
49
|
+
useElementSize,
|
|
50
|
+
useEventListener,
|
|
51
|
+
useSessionStorage,
|
|
52
|
+
useIntersectionObserver,
|
|
53
|
+
useElementVisibility
|
|
54
|
+
} from '@vueuse/core'
|
|
55
|
+
import { isEqual } from 'lodash-es'
|
|
56
|
+
import { ChevronDown, Settings2Icon } from 'lucide-vue-next'
|
|
57
|
+
import { computed, nextTick, onBeforeMount, onMounted, ref, watch, watchEffect } from 'vue'
|
|
58
|
+
import {
|
|
59
|
+
BuiDataTableColumnList,
|
|
29
60
|
BuiTable,
|
|
30
61
|
BuiTableBody,
|
|
31
62
|
BuiTableCell,
|
|
@@ -33,27 +64,8 @@ import {
|
|
|
33
64
|
BuiTableHead,
|
|
34
65
|
BuiTableHeader,
|
|
35
66
|
BuiTableRow,
|
|
36
|
-
getPinningStyle
|
|
37
|
-
BuiDataTableColumnList
|
|
67
|
+
getPinningStyle
|
|
38
68
|
} from './'
|
|
39
|
-
import {
|
|
40
|
-
BuiCommand,
|
|
41
|
-
BuiCommandEmpty,
|
|
42
|
-
BuiCommandInput,
|
|
43
|
-
BuiCommandList,
|
|
44
|
-
BuiCommandItem,
|
|
45
|
-
BuiCommandSeparator
|
|
46
|
-
} from '@/components/command'
|
|
47
|
-
import { BuiContextMenuContent, BuiContextMenuItem } from '@/components/context-menu'
|
|
48
|
-
import { BuiPopover, BuiPopoverContent, BuiPopoverTrigger } from '@/components/popover'
|
|
49
|
-
import { BuiScrollArea } from '@/components/scroll-area'
|
|
50
|
-
import { BuiButton } from '@/components/button'
|
|
51
|
-
import { Settings2Icon, ChevronDown } from 'lucide-vue-next'
|
|
52
|
-
import { useElementSize, useEventListener } from '@vueuse/core'
|
|
53
|
-
import { isEqual } from 'lodash-es'
|
|
54
|
-
import { cn, valueUpdater } from '@/lib/utils'
|
|
55
|
-
import { useResizeColumns } from '@/lib/useResizeColumns'
|
|
56
|
-
import { useSessionStorage } from '@vueuse/core'
|
|
57
69
|
import { useGlobalCursor } from '@/lib/useGlobalCursor'
|
|
58
70
|
|
|
59
71
|
const NO_GROUP_KEY = '#UNDEFINED#'
|
|
@@ -87,6 +99,7 @@ const props = withDefaults(
|
|
|
87
99
|
columnResetVisibility?: string
|
|
88
100
|
paginationTranslations?: {
|
|
89
101
|
itemsPerPage: string
|
|
102
|
+
itemsPerPageAuto: string
|
|
90
103
|
page: string
|
|
91
104
|
of: string
|
|
92
105
|
}
|
|
@@ -113,7 +126,7 @@ const props = withDefaults(
|
|
|
113
126
|
)
|
|
114
127
|
|
|
115
128
|
const sorting = defineModel<SortingState>('sorting')
|
|
116
|
-
const pagination = defineModel<
|
|
129
|
+
const pagination = defineModel<PaginationAutoState>('pagination')
|
|
117
130
|
const rowSelection = defineModel<RowSelectionState>('selection')
|
|
118
131
|
const columnVisibility = defineModel<VisibilityState>('columnVisibility')
|
|
119
132
|
const columnOrder = defineModel<ColumnOrderState>('columnOrder')
|
|
@@ -182,6 +195,15 @@ const table = useVueTable({
|
|
|
182
195
|
getRowId: props.getRowId
|
|
183
196
|
})
|
|
184
197
|
|
|
198
|
+
const pageAuto = computed({
|
|
199
|
+
get() {
|
|
200
|
+
return pagination.value?.pageAuto || false
|
|
201
|
+
},
|
|
202
|
+
set: (state: boolean) => {
|
|
203
|
+
if (!pagination.value) return
|
|
204
|
+
pagination.value.pageAuto = state
|
|
205
|
+
}
|
|
206
|
+
})
|
|
185
207
|
const tablePageSize = computed<PageSize>({
|
|
186
208
|
get() {
|
|
187
209
|
return table.getState().pagination.pageSize as PageSize
|
|
@@ -417,6 +439,65 @@ watch(isResizing, () => {
|
|
|
417
439
|
resetCursor()
|
|
418
440
|
}
|
|
419
441
|
})
|
|
442
|
+
|
|
443
|
+
const rows = computed(() => table.getRowModel().rows)
|
|
444
|
+
const rowsLength = computed(() => rows.value.length)
|
|
445
|
+
const sentinel = ref<HTMLElement | null>(null)
|
|
446
|
+
const sentinelVisible = useElementVisibility(sentinel)
|
|
447
|
+
const sentinelKey = computed(() => `sentinel-${rowsLength.value}`)
|
|
448
|
+
|
|
449
|
+
const { stop } = useIntersectionObserver(
|
|
450
|
+
sentinel,
|
|
451
|
+
async ([{ isIntersecting }]) => {
|
|
452
|
+
if (isIntersecting && pageAuto.value) {
|
|
453
|
+
await nextTick()
|
|
454
|
+
loadMore()
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (tablePageSize.value >= props.totalItems) {
|
|
458
|
+
stop()
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
rootMargin: '0px 0px 50px 0px'
|
|
463
|
+
}
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
let loadingMore = false
|
|
467
|
+
async function loadMore() {
|
|
468
|
+
if (loadingMore) return
|
|
469
|
+
loadingMore = true
|
|
470
|
+
|
|
471
|
+
const newSize = Math.min(tablePageSize.value + 50, props.totalItems)
|
|
472
|
+
table.setPageSize(newSize)
|
|
473
|
+
|
|
474
|
+
await nextTick()
|
|
475
|
+
loadingMore = false
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
watch(pageAuto, (newPageAuto, oldPageAuto) => {
|
|
479
|
+
if (newPageAuto && !oldPageAuto) {
|
|
480
|
+
if (tableElementRef.value && tableElementRef.value.scrollAreaElementRef?.tableWrapperRef) {
|
|
481
|
+
tableElementRef.value.scrollAreaElementRef.tableWrapperRef.scrollTop()
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
})
|
|
485
|
+
|
|
486
|
+
const isUpdating = ref(false)
|
|
487
|
+
watch(rowsLength, async () => {
|
|
488
|
+
if (!pageAuto.value || isUpdating.value) return
|
|
489
|
+
|
|
490
|
+
isUpdating.value = true
|
|
491
|
+
await nextTick()
|
|
492
|
+
|
|
493
|
+
//Add a small delay to ensure rendering is complete
|
|
494
|
+
setTimeout(() => {
|
|
495
|
+
if (sentinelVisible.value) {
|
|
496
|
+
loadMore()
|
|
497
|
+
}
|
|
498
|
+
isUpdating.value = false
|
|
499
|
+
}, 50)
|
|
500
|
+
})
|
|
420
501
|
</script>
|
|
421
502
|
|
|
422
503
|
<template>
|
|
@@ -635,6 +716,14 @@ watch(isResizing, () => {
|
|
|
635
716
|
<slot name="nodata">No data</slot>
|
|
636
717
|
</BuiTableEmpty>
|
|
637
718
|
</template>
|
|
719
|
+
|
|
720
|
+
<tr
|
|
721
|
+
v-if="pageAuto && tablePageSize < props.totalItems"
|
|
722
|
+
ref="sentinel"
|
|
723
|
+
class="h-4 w-full"
|
|
724
|
+
:data-key="sentinelKey"
|
|
725
|
+
:key="sentinelKey"
|
|
726
|
+
/>
|
|
638
727
|
</BuiTableBody>
|
|
639
728
|
</BuiTable>
|
|
640
729
|
<div
|
|
@@ -654,6 +743,7 @@ watch(isResizing, () => {
|
|
|
654
743
|
:total="computedItems"
|
|
655
744
|
v-model:pageIndex="pageIndex"
|
|
656
745
|
v-model:pageSize="tablePageSize"
|
|
746
|
+
v-model:pageAuto="pageAuto"
|
|
657
747
|
:translations="paginationTranslations"
|
|
658
748
|
>
|
|
659
749
|
</BuiPaginationCommon>
|
|
@@ -14,6 +14,7 @@ defineExpose({ tableRef, scrollAreaElementRef })
|
|
|
14
14
|
<BuiScrollArea
|
|
15
15
|
ref="scrollAreaElementRef"
|
|
16
16
|
class="border-border/16 w-full grow overflow-auto rounded-sm border"
|
|
17
|
+
:scroll-hide-delay="100"
|
|
17
18
|
>
|
|
18
19
|
<slot name="columnVisibility" />
|
|
19
20
|
<div class="flex min-h-[90px] w-full grow flex-col">
|
|
@@ -4,6 +4,10 @@ import { cn } from '@/lib/utils'
|
|
|
4
4
|
|
|
5
5
|
const props = defineProps<{ class?: string }>()
|
|
6
6
|
const emits = defineEmits(['mouseenter', 'mouseleave'])
|
|
7
|
+
|
|
8
|
+
defineOptions({
|
|
9
|
+
inheritAttrs: false
|
|
10
|
+
})
|
|
7
11
|
</script>
|
|
8
12
|
|
|
9
13
|
<template>
|
|
@@ -19,6 +23,7 @@ const emits = defineEmits(['mouseenter', 'mouseleave'])
|
|
|
19
23
|
:data-row-state="$attrs['data-row-state']"
|
|
20
24
|
@mouseenter="emits('mouseenter')"
|
|
21
25
|
@mouseleave="emits('mouseleave')"
|
|
26
|
+
v-bind="$attrs"
|
|
22
27
|
>
|
|
23
28
|
<slot />
|
|
24
29
|
</tr>
|
|
@@ -36,6 +41,7 @@ const emits = defineEmits(['mouseenter', 'mouseleave'])
|
|
|
36
41
|
:data-row-state="$attrs['data-row-state']"
|
|
37
42
|
@mouseenter="emits('mouseenter')"
|
|
38
43
|
@mouseleave="emits('mouseleave')"
|
|
44
|
+
v-bind="$attrs"
|
|
39
45
|
>
|
|
40
46
|
<slot />
|
|
41
47
|
</tr>
|
|
@@ -14,6 +14,8 @@ export { default as BuiTableFooter } from './BuiTableFooter.vue'
|
|
|
14
14
|
export { default as BuiDataTable } from './BuiDataTable.vue'
|
|
15
15
|
export { default as BuiDataTableColumnList } from './BuiDataTableColumnList.vue'
|
|
16
16
|
|
|
17
|
+
export type { PaginationAutoState } from './BuiDataTable.vue'
|
|
18
|
+
|
|
17
19
|
export function getPinningStyle<TData>(_column: Column<TData, unknown>): CSSProperties {
|
|
18
20
|
// FYI sticky columns not possible with transparent background colors
|
|
19
21
|
// const isPinned = column.getIsPinned()
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BuiDataTable } from '@/components/table'
|
|
2
2
|
import BuiDataTableStory from '@/stories/components/BuiDataTableStory.vue'
|
|
3
|
+
import BuiDataTableWithScrollStory from '@/stories/components/BuiDataTableWithScrollStory.vue'
|
|
3
4
|
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
4
5
|
|
|
5
6
|
const meta = {
|
|
@@ -24,3 +25,15 @@ export const Default: Story = {
|
|
|
24
25
|
template: `<BuiDataTableStory v-bind="args" />`
|
|
25
26
|
})
|
|
26
27
|
}
|
|
28
|
+
|
|
29
|
+
export const WithScroll: Story = {
|
|
30
|
+
// @ts-expect-error no need to describe all args, see BuiDataTableStory
|
|
31
|
+
args: {},
|
|
32
|
+
render: (args) => ({
|
|
33
|
+
components: { BuiDataTableWithScrollStory },
|
|
34
|
+
setup() {
|
|
35
|
+
return { args }
|
|
36
|
+
},
|
|
37
|
+
template: `<BuiDataTableWithScrollStory v-bind="args" />`
|
|
38
|
+
})
|
|
39
|
+
}
|
|
@@ -3,7 +3,6 @@ import { BuiDataTable } from '@/components/table'
|
|
|
3
3
|
import RowActionsMenuContent from './ActionsMenuContent.vue'
|
|
4
4
|
import type {
|
|
5
5
|
ColumnDef,
|
|
6
|
-
PaginationState,
|
|
7
6
|
Row,
|
|
8
7
|
RowSelectionState,
|
|
9
8
|
VisibilityState,
|
|
@@ -25,6 +24,7 @@ import { BuiCheckbox } from '@/components/checkbox'
|
|
|
25
24
|
import { tableColumnSortCommon } from '@/lib/utils'
|
|
26
25
|
import { BuiButton } from '@/components/button'
|
|
27
26
|
import { BuiTabs, BuiTabsList, BuiTabsTrigger } from '@/components/tabs'
|
|
27
|
+
import type { PaginationAutoState } from '@/components/table/BuiDataTable.vue'
|
|
28
28
|
|
|
29
29
|
const taskSchema = z.object({
|
|
30
30
|
id: z.string(),
|
|
@@ -102,7 +102,7 @@ function onGroupAction(group: string | number, action: string) {
|
|
|
102
102
|
|
|
103
103
|
type TaskSortingState = { id: keyof Task; desc: boolean }
|
|
104
104
|
const sorting = ref<TaskSortingState[]>([{ id: 'id', desc: false }])
|
|
105
|
-
const pagination = ref<
|
|
105
|
+
const pagination = ref<PaginationAutoState>({
|
|
106
106
|
pageIndex: 0,
|
|
107
107
|
pageSize: 10
|
|
108
108
|
})
|
|
@@ -202,7 +202,6 @@ function groupName(group: string | number) {
|
|
|
202
202
|
v-model:column-order="columnOrder"
|
|
203
203
|
@update:selection="updateSelection"
|
|
204
204
|
:total-items="totalItems"
|
|
205
|
-
class="caption-top"
|
|
206
205
|
:manualPagination="false"
|
|
207
206
|
:getRowId="(row) => row.id"
|
|
208
207
|
:groupBy="groupBy === 'none' ? undefined : groupBy"
|
|
@@ -212,6 +211,7 @@ function groupName(group: string | number) {
|
|
|
212
211
|
:enable-group-folding="true"
|
|
213
212
|
:pagination-translations="{
|
|
214
213
|
itemsPerPage: 'Tasks per page',
|
|
214
|
+
itemsPerPageAuto: 'Auto',
|
|
215
215
|
page: 'Page',
|
|
216
216
|
of: 'of'
|
|
217
217
|
}"
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { BuiDataTable } from '@/components/table'
|
|
3
|
+
import RowActionsMenuContent from './ActionsMenuContent.vue'
|
|
4
|
+
import type {
|
|
5
|
+
ColumnDef,
|
|
6
|
+
PaginationState,
|
|
7
|
+
Row,
|
|
8
|
+
RowSelectionState,
|
|
9
|
+
VisibilityState,
|
|
10
|
+
ColumnOrderState
|
|
11
|
+
} from '@tanstack/vue-table'
|
|
12
|
+
import { sort, type ISortByObjectSorter } from 'fast-sort'
|
|
13
|
+
import {
|
|
14
|
+
AlignJustifyIcon,
|
|
15
|
+
ArrowUpNarrowWideIcon,
|
|
16
|
+
FolderIcon,
|
|
17
|
+
SignalHighIcon,
|
|
18
|
+
SignalMediumIcon,
|
|
19
|
+
SignalLowIcon
|
|
20
|
+
} from 'lucide-vue-next'
|
|
21
|
+
import { computed, h, ref, withModifiers } from 'vue'
|
|
22
|
+
import { z } from 'zod'
|
|
23
|
+
import tasks from '@/stories/data/tasks.json'
|
|
24
|
+
import { BuiCheckbox } from '@/components/checkbox'
|
|
25
|
+
import { tableColumnSortCommon } from '@/lib/utils'
|
|
26
|
+
import { BuiButton } from '@/components/button'
|
|
27
|
+
import { BuiTabs, BuiTabsList, BuiTabsTrigger } from '@/components/tabs'
|
|
28
|
+
|
|
29
|
+
const taskSchema = z.object({
|
|
30
|
+
id: z.string(),
|
|
31
|
+
title: z.string(),
|
|
32
|
+
status: z.string().nullable().optional(),
|
|
33
|
+
label: z.string(),
|
|
34
|
+
priority: z.string(),
|
|
35
|
+
errorMessage: z.string().optional(),
|
|
36
|
+
age: z.string().optional()
|
|
37
|
+
})
|
|
38
|
+
type Task = z.infer<typeof taskSchema>
|
|
39
|
+
|
|
40
|
+
const columns: ColumnDef<Task>[] = [
|
|
41
|
+
{
|
|
42
|
+
id: 'id',
|
|
43
|
+
accessorKey: 'id',
|
|
44
|
+
header: ({ table, column }) => {
|
|
45
|
+
return h('div', { class: 'flex items-center gap-2' }, [
|
|
46
|
+
h(BuiCheckbox, {
|
|
47
|
+
modelValue: table.getIsSomePageRowsSelected()
|
|
48
|
+
? 'indeterminate'
|
|
49
|
+
: table.getIsAllPageRowsSelected(),
|
|
50
|
+
'onUpdate:modelValue': (value: boolean | 'indeterminate') =>
|
|
51
|
+
table.getIsSomePageRowsSelected()
|
|
52
|
+
? table.toggleAllPageRowsSelected(false)
|
|
53
|
+
: table.toggleAllPageRowsSelected(!!value),
|
|
54
|
+
ariaLabel: 'Select row',
|
|
55
|
+
onClick: withModifiers(() => {}, ['stop'])
|
|
56
|
+
}),
|
|
57
|
+
tableColumnSortCommon(column, 'ID')
|
|
58
|
+
])
|
|
59
|
+
},
|
|
60
|
+
cell: ({ row }) =>
|
|
61
|
+
h('div', { class: 'flex items-center gap-2' }, [
|
|
62
|
+
h(BuiCheckbox, {
|
|
63
|
+
modelValue: row.getIsSelected(),
|
|
64
|
+
'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value),
|
|
65
|
+
ariaLabel: 'Select row'
|
|
66
|
+
}),
|
|
67
|
+
`${row.getValue('id')}`
|
|
68
|
+
]),
|
|
69
|
+
enableHiding: false,
|
|
70
|
+
meta: { title: 'ID', pinLeft: true }
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
accessorKey: 'title',
|
|
74
|
+
header: 'Title',
|
|
75
|
+
enableSorting: false
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
accessorKey: 'status',
|
|
79
|
+
header: ({ column }) => tableColumnSortCommon(column, 'Очень длинный заголовок для статуса'),
|
|
80
|
+
meta: { title: 'Статус таска' }
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
accessorKey: 'priority',
|
|
84
|
+
header: ({ column }) => tableColumnSortCommon(column, 'Priorities')
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
accessorKey: 'age',
|
|
88
|
+
header: ({ column }) => tableColumnSortCommon(column, 'Age')
|
|
89
|
+
},
|
|
90
|
+
{ id: 'hiddenColumn', header: 'Hidden Column', cell: 'secret info' }
|
|
91
|
+
]
|
|
92
|
+
const data = ref<Task[]>(tasks)
|
|
93
|
+
|
|
94
|
+
function onRowAction(row: Task, action: string) {
|
|
95
|
+
const str = `${action}: ${row.id}`
|
|
96
|
+
alert(str)
|
|
97
|
+
}
|
|
98
|
+
function onGroupAction(group: string | number, action: string) {
|
|
99
|
+
const str = `${action}: ${group}`
|
|
100
|
+
alert(str)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
type TaskSortingState = { id: keyof Task; desc: boolean }
|
|
104
|
+
const sorting = ref<TaskSortingState[]>([{ id: 'id', desc: false }])
|
|
105
|
+
const pagination = ref<PaginationState>({
|
|
106
|
+
pageIndex: 0,
|
|
107
|
+
pageSize: 10
|
|
108
|
+
})
|
|
109
|
+
const totalItems = tasks.length
|
|
110
|
+
|
|
111
|
+
const selection = ref<RowSelectionState | undefined>({})
|
|
112
|
+
function updateSelection(val?: RowSelectionState) {
|
|
113
|
+
console.log('selection was changed', val)
|
|
114
|
+
selection.value = val
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const columnVisibility = ref<VisibilityState>({ hiddenColumn: false })
|
|
118
|
+
const columnSizing = ref<Record<string, number>>({ title: 450 })
|
|
119
|
+
const columnOrder = ref<ColumnOrderState>()
|
|
120
|
+
|
|
121
|
+
type GroupBy = 'none' | 'status' | 'priority'
|
|
122
|
+
const groupBy = ref<GroupBy>('none')
|
|
123
|
+
const groupLabels = {
|
|
124
|
+
status: ['Status', 'Not in any status'],
|
|
125
|
+
priority: ['Priority', 'Not in any priorities']
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const sortedData = computed(() => {
|
|
129
|
+
const sortDirection = (sorting.value[0].desc ? 'desc' : 'asc') as 'asc'
|
|
130
|
+
const sortColumn = sorting.value[0].id
|
|
131
|
+
const groupByStr = groupBy.value
|
|
132
|
+
|
|
133
|
+
const sortBy: ISortByObjectSorter<Task> | ISortByObjectSorter<Task>[] = [
|
|
134
|
+
{
|
|
135
|
+
[sortDirection]: sortColumn
|
|
136
|
+
}
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
// sort by grouping column first, but not when manually sorting by it
|
|
140
|
+
if (groupByStr !== 'none' && sortColumn !== groupByStr) {
|
|
141
|
+
sortBy.unshift({
|
|
142
|
+
asc: groupByStr
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// sort by ID when possible
|
|
147
|
+
if (sortColumn !== 'id') {
|
|
148
|
+
sortBy.push({
|
|
149
|
+
asc: 'id'
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return sort(data.value).by([...sortBy])
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
function renderSubComponent(row: Row<Task>) {
|
|
157
|
+
if (row.original.errorMessage) {
|
|
158
|
+
return () => h('span', { style: 'color: red' }, `Subrow: ${row.original.errorMessage}`)
|
|
159
|
+
} else {
|
|
160
|
+
return undefined
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function deleteRow() {
|
|
165
|
+
data.value = data.value.map((a) => a)
|
|
166
|
+
}
|
|
167
|
+
function updateRows() {
|
|
168
|
+
data.value.shift()
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function groupName(group: string | number) {
|
|
172
|
+
if (groupBy.value === 'priority') {
|
|
173
|
+
if (group === 'high') {
|
|
174
|
+
return () => h(SignalHighIcon, { class: 'size-4 inline-block' })
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (group === 'medium') {
|
|
178
|
+
return () => h(SignalMediumIcon, { class: 'size-4 inline-block' })
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (group === 'low') {
|
|
182
|
+
return () => h(SignalLowIcon, { class: 'size-4 inline-block' })
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return () => group
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return () => group
|
|
189
|
+
}
|
|
190
|
+
</script>
|
|
191
|
+
|
|
192
|
+
<template>
|
|
193
|
+
<Story title="BuiDataTable" autoPropsDisabled :layout="{ type: 'grid', width: '95%' }">
|
|
194
|
+
<Variant key="variant" title="Sorting, Pagination, Grouping, Subrow">
|
|
195
|
+
<div class="page-wrapper">
|
|
196
|
+
<div class="table-wrapper">
|
|
197
|
+
<BuiDataTable
|
|
198
|
+
:columns="columns"
|
|
199
|
+
:data="sortedData"
|
|
200
|
+
v-model:sorting="sorting"
|
|
201
|
+
v-model:pagination="pagination"
|
|
202
|
+
v-model:column-visibility="columnVisibility"
|
|
203
|
+
v-model:column-sizing="columnSizing"
|
|
204
|
+
v-model:column-order="columnOrder"
|
|
205
|
+
@update:selection="updateSelection"
|
|
206
|
+
:total-items="totalItems"
|
|
207
|
+
class="caption-top"
|
|
208
|
+
:manualPagination="false"
|
|
209
|
+
:getRowId="(row) => row.id"
|
|
210
|
+
:groupBy="groupBy === 'none' ? undefined : groupBy"
|
|
211
|
+
:groupLabels="groupLabels"
|
|
212
|
+
:renderSubComponent="renderSubComponent"
|
|
213
|
+
:freeze-header="true"
|
|
214
|
+
enable-column-list-control
|
|
215
|
+
:enable-group-folding="true"
|
|
216
|
+
:pagination-translations="{
|
|
217
|
+
itemsPerPage: 'Tasks per page',
|
|
218
|
+
itemsPerPageAuto: 'Auto',
|
|
219
|
+
page: 'Page',
|
|
220
|
+
of: 'of'
|
|
221
|
+
}"
|
|
222
|
+
>
|
|
223
|
+
<template #caption="{ table }">
|
|
224
|
+
<div class="flex h-fit items-center justify-between">
|
|
225
|
+
<div class="flex h-full flex-row items-center gap-3">
|
|
226
|
+
<BuiButton variant="outline">Download YAML</BuiButton>
|
|
227
|
+
<BuiButton variant="outline" @click="updateRows"> Delete row </BuiButton>
|
|
228
|
+
<BuiButton variant="outline" @click="deleteRow"> Update rows </BuiButton>
|
|
229
|
+
</div>
|
|
230
|
+
|
|
231
|
+
<div class="flex h-full flex-row items-center gap-3">
|
|
232
|
+
<BuiTabs v-model="groupBy">
|
|
233
|
+
<BuiTabsList class="grid w-full grid-cols-3" variant="default">
|
|
234
|
+
<BuiTabsTrigger value="none" variant="default">
|
|
235
|
+
<AlignJustifyIcon :size="14" />
|
|
236
|
+
</BuiTabsTrigger>
|
|
237
|
+
<BuiTabsTrigger value="status" variant="default">
|
|
238
|
+
<FolderIcon :size="14" />
|
|
239
|
+
</BuiTabsTrigger>
|
|
240
|
+
<BuiTabsTrigger value="priority" variant="default">
|
|
241
|
+
<ArrowUpNarrowWideIcon :size="14" />
|
|
242
|
+
</BuiTabsTrigger>
|
|
243
|
+
</BuiTabsList>
|
|
244
|
+
</BuiTabs>
|
|
245
|
+
|
|
246
|
+
<span>
|
|
247
|
+
{{ table.getFilteredSelectedRowModel().rows?.length }} of
|
|
248
|
+
{{ table.getFilteredRowModel().rows?.length }} row(s) selected
|
|
249
|
+
</span>
|
|
250
|
+
</div>
|
|
251
|
+
</div>
|
|
252
|
+
</template>
|
|
253
|
+
<template #nodata>No data</template>
|
|
254
|
+
<template #groupByRow="{ group }"> Optional slot for: `{{ group }}` </template>
|
|
255
|
+
<template #groupName="{ group }">
|
|
256
|
+
<component :is="groupName(group)"></component>
|
|
257
|
+
</template>
|
|
258
|
+
<template #groupActions="{ group }">
|
|
259
|
+
<RowActionsMenuContent
|
|
260
|
+
:actions="['Group action']"
|
|
261
|
+
@select="(action) => onGroupAction(group, action)"
|
|
262
|
+
/>
|
|
263
|
+
</template>
|
|
264
|
+
<template #rowActions="{ row }">
|
|
265
|
+
<RowActionsMenuContent
|
|
266
|
+
:actions="['action 1', 'action 2']"
|
|
267
|
+
@select="(action) => onRowAction(row, action)"
|
|
268
|
+
/>
|
|
269
|
+
</template>
|
|
270
|
+
<template #numberOfItems>{{ data.length }} tasks</template>
|
|
271
|
+
</BuiDataTable>
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
</Variant>
|
|
275
|
+
</Story>
|
|
276
|
+
</template>
|
|
277
|
+
|
|
278
|
+
<style scoped>
|
|
279
|
+
.page-wrapper {
|
|
280
|
+
height: 95vh;
|
|
281
|
+
flex-direction: column;
|
|
282
|
+
flex-grow: 1;
|
|
283
|
+
display: flex;
|
|
284
|
+
overflow: hidden auto;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.table-wrapper {
|
|
288
|
+
height: 100%;
|
|
289
|
+
flex-direction: column;
|
|
290
|
+
display: flex;
|
|
291
|
+
}
|
|
292
|
+
</style>
|