@soft-stech/bootsman-ui-shadcn 2.0.20 → 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-lyWD8KAT.js +144 -0
- 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/input/index.js +22 -22
- 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/BuiScrollArea.vue.d.ts +256 -2
- 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 +9 -4
- package/dist/components/table/BuiTable.js +1 -1
- package/dist/components/table/BuiTable.vue.d.ts +1487 -2
- 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 +726 -663
- package/dist/index.js +6 -6
- package/dist/lib/useGlobalCursor.d.ts +4 -0
- package/dist/lib/useGlobalCursor.js +15 -0
- package/dist/lib/useResizeColumns.d.ts +3812 -0
- package/dist/lib/useResizeColumns.js +97 -79
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/input/BuiInput.vue +1 -1
- package/src/components/pagination/BuiPaginationCommon.vue +16 -4
- package/src/components/scroll-area/BuiScrollArea.vue +9 -2
- package/src/components/scroll-area/BuiScrollBar.vue +4 -4
- package/src/components/table/BuiDataTable.vue +152 -34
- package/src/components/table/BuiTable.vue +12 -3
- package/src/components/table/BuiTableRow.vue +6 -0
- package/src/components/table/index.ts +2 -0
- package/src/lib/useGlobalCursor.ts +17 -0
- package/src/lib/useResizeColumns.ts +151 -42
- package/src/stories/BuiDataTable.stories.ts +13 -0
- package/src/stories/components/BuiDataTableStory.vue +4 -4
- package/src/stories/components/BuiDataTableWithScrollStory.vue +292 -0
- package/dist/BuiPaginationCommon.vue_vue_type_script_setup_true_lang-DhSRYKth.js +0 -170
- package/dist/BuiScrollArea.vue_vue_type_script_setup_true_lang-XkIzRs-G.js +0 -141
- package/dist/BuiTable.vue_vue_type_script_setup_true_lang-Dd_dkcy4.js +0 -30
- package/dist/BuiTableRow.vue_vue_type_script_setup_true_lang-BJk8Yk1B.js +0 -54
|
@@ -1,20 +1,31 @@
|
|
|
1
|
-
import { ref } from 'vue'
|
|
1
|
+
import { computed, ref } from 'vue'
|
|
2
2
|
import BuiTableHeader from '@/components/table/BuiTableHeader.vue'
|
|
3
|
+
import BuiTable from '@/components/table/BuiTable.vue'
|
|
3
4
|
import { useEventListener } from '@vueuse/core'
|
|
4
5
|
|
|
6
|
+
const MIN_CELL_WIDTH = 90
|
|
7
|
+
const LAST_CELL_EXTRA_SPACE = 56
|
|
8
|
+
const ACTIONS_CELL_MIN_WIDTH = 10
|
|
9
|
+
|
|
5
10
|
export function useResizeColumns() {
|
|
6
11
|
type CELL = {
|
|
7
|
-
[key: string]: {
|
|
12
|
+
[key: string]: {
|
|
13
|
+
cell: HTMLTableCellElement
|
|
14
|
+
initialWidth: number
|
|
15
|
+
minWidth: number
|
|
16
|
+
baseWidth: number
|
|
17
|
+
isLast: boolean
|
|
18
|
+
}
|
|
8
19
|
}
|
|
9
20
|
const isResizing = ref<boolean>(false)
|
|
10
21
|
const resizingCellId = ref<string>('')
|
|
11
22
|
const neighborCellId = ref<string>('')
|
|
12
23
|
const cells = ref<CELL | undefined>(undefined)
|
|
13
|
-
const MIN_CELL_WIDTH = 90
|
|
14
|
-
const LAST_CELL_EXTRA_SPACE = 56
|
|
15
|
-
const ACTIONS_CELL_MIN_WIDTH = 10
|
|
16
24
|
const calculatedColumnSizing = ref<Record<string, number> | undefined>(undefined)
|
|
17
25
|
const tableHeaderElement = ref<InstanceType<typeof BuiTableHeader> | null>(null)
|
|
26
|
+
const tableElement = ref<InstanceType<typeof BuiTable> | null>(null)
|
|
27
|
+
const initialTableWidth = ref<number>(0)
|
|
28
|
+
const minTableWidth = ref<number>(0)
|
|
18
29
|
const unregisterMouseMove = ref<(() => void) | undefined>(undefined)
|
|
19
30
|
|
|
20
31
|
const setProvidedCellWidths = (columnSizing: Record<string, number> | undefined) => {
|
|
@@ -25,31 +36,57 @@ export function useResizeColumns() {
|
|
|
25
36
|
headerCells.forEach((cell) => {
|
|
26
37
|
const cellId = getCellId(cell)
|
|
27
38
|
|
|
28
|
-
|
|
29
|
-
cell.style.width = columnSizing[cellId] + 'px'
|
|
30
|
-
}
|
|
39
|
+
cell.style.width = columnSizing && columnSizing[cellId] ? columnSizing[cellId] + 'px' : ''
|
|
31
40
|
})
|
|
32
41
|
}
|
|
33
42
|
}
|
|
34
43
|
|
|
35
44
|
const getCells = () => {
|
|
36
|
-
if (
|
|
45
|
+
if (
|
|
46
|
+
tableHeaderElement.value &&
|
|
47
|
+
tableHeaderElement.value.headRef &&
|
|
48
|
+
tableElement.value &&
|
|
49
|
+
tableElement.value?.tableRef
|
|
50
|
+
) {
|
|
37
51
|
const headerCells = [...tableHeaderElement.value.headRef.querySelectorAll('th')]
|
|
38
|
-
const
|
|
52
|
+
const tableInitialWidth = getTableWidth()
|
|
53
|
+
|
|
54
|
+
tableElement.value.tableRef.style.width = 'min-content'
|
|
55
|
+
|
|
56
|
+
const headerCellsWidths: CELL = headerCells.reduce((acc, cell, index, array) => {
|
|
39
57
|
const cellId = getCellId(cell)
|
|
58
|
+
|
|
40
59
|
return {
|
|
41
60
|
...acc,
|
|
42
61
|
[cellId]: {
|
|
43
62
|
cell: cell,
|
|
44
|
-
|
|
63
|
+
isLast: index === array.length - 1,
|
|
64
|
+
initialWidth: Math.floor(cell.offsetWidth),
|
|
65
|
+
baseWidth: Math.floor(cell.offsetWidth),
|
|
45
66
|
minWidth:
|
|
46
67
|
cellId === 'actions'
|
|
47
68
|
? ACTIONS_CELL_MIN_WIDTH
|
|
48
|
-
:
|
|
69
|
+
: cell.offsetWidth < MIN_CELL_WIDTH
|
|
70
|
+
? MIN_CELL_WIDTH
|
|
71
|
+
: cell.offsetWidth
|
|
49
72
|
}
|
|
50
73
|
}
|
|
51
74
|
}, {})
|
|
52
75
|
|
|
76
|
+
tableElement.value.tableRef.style.width = ''
|
|
77
|
+
|
|
78
|
+
Object.values(headerCellsWidths).forEach((cellElement) => {
|
|
79
|
+
cellElement.baseWidth = Math.floor(cellElement.cell.offsetWidth)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
setProvidedCellWidths(calculatedColumnSizing.value)
|
|
83
|
+
tableElement.value.tableRef.style.width =
|
|
84
|
+
(calculatedColumnSizing.value?.['table'] || tableInitialWidth) + 'px'
|
|
85
|
+
|
|
86
|
+
Object.values(headerCellsWidths).forEach((cellElement) => {
|
|
87
|
+
cellElement.initialWidth = Math.floor(cellElement.cell.offsetWidth)
|
|
88
|
+
})
|
|
89
|
+
|
|
53
90
|
return headerCellsWidths
|
|
54
91
|
}
|
|
55
92
|
|
|
@@ -109,6 +146,10 @@ export function useResizeColumns() {
|
|
|
109
146
|
updatedColumnSizingValue[cell] = newWidth
|
|
110
147
|
}
|
|
111
148
|
|
|
149
|
+
if (tableElement.value && tableElement.value?.tableRef) {
|
|
150
|
+
updatedColumnSizingValue['table'] = tableElement.value.tableRef.offsetWidth
|
|
151
|
+
}
|
|
152
|
+
|
|
112
153
|
calculatedColumnSizing.value = updatedColumnSizingValue
|
|
113
154
|
}
|
|
114
155
|
}
|
|
@@ -121,16 +162,20 @@ export function useResizeColumns() {
|
|
|
121
162
|
return 0
|
|
122
163
|
}
|
|
123
164
|
|
|
165
|
+
const lastCell = computed(() => {
|
|
166
|
+
if (cells.value) {
|
|
167
|
+
return Object.values(cells.value).find((cell) => cell.isLast)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return undefined
|
|
171
|
+
})
|
|
172
|
+
|
|
124
173
|
const getCellId = (cell: HTMLTableCellElement) => {
|
|
125
174
|
return cell.id.split('_')[0]
|
|
126
175
|
}
|
|
127
176
|
|
|
128
|
-
const resizeCells = (
|
|
129
|
-
cell
|
|
130
|
-
neighborCell: HTMLTableCellElement | null,
|
|
131
|
-
e: MouseEvent
|
|
132
|
-
) => {
|
|
133
|
-
if (!cell || !neighborCell) {
|
|
177
|
+
const resizeCells = (cell: HTMLTableCellElement | null, e: MouseEvent) => {
|
|
178
|
+
if (!cell || !tableElement.value?.tableRef || !lastCell.value) {
|
|
134
179
|
resizingCellId.value = ''
|
|
135
180
|
neighborCellId.value = ''
|
|
136
181
|
|
|
@@ -140,7 +185,9 @@ export function useResizeColumns() {
|
|
|
140
185
|
const movementX = e.movementX
|
|
141
186
|
const direction: 'left' | 'right' = movementX < 0 ? 'left' : 'right'
|
|
142
187
|
const newCellWidth = Math.floor(parseInt(cell.style.width)) + movementX
|
|
143
|
-
const
|
|
188
|
+
const newTableWidth =
|
|
189
|
+
Math.floor(parseInt(tableElement.value?.tableRef?.style.width)) + movementX
|
|
190
|
+
const newLastCellWidth = Math.floor(parseInt(lastCell.value.cell.style.width)) - movementX
|
|
144
191
|
|
|
145
192
|
if (direction === 'left') {
|
|
146
193
|
const min =
|
|
@@ -148,29 +195,35 @@ export function useResizeColumns() {
|
|
|
148
195
|
? cells.value[getCellId(cell)].minWidth
|
|
149
196
|
: MIN_CELL_WIDTH
|
|
150
197
|
|
|
151
|
-
if (newCellWidth
|
|
152
|
-
|
|
198
|
+
if (newCellWidth >= min) {
|
|
199
|
+
cell.style.width = newCellWidth + 'px'
|
|
153
200
|
|
|
154
|
-
|
|
201
|
+
if (newTableWidth >= minTableWidth.value) {
|
|
202
|
+
tableElement.value.tableRef.style.width = newTableWidth + 'px'
|
|
203
|
+
} else {
|
|
204
|
+
tableElement.value.tableRef.style.width = minTableWidth.value + 'px'
|
|
205
|
+
lastCell.value.cell.style.width = newLastCellWidth + 'px'
|
|
206
|
+
}
|
|
155
207
|
} else {
|
|
156
|
-
|
|
157
|
-
neighborCell.style.width = newNeighborCellWidth + 'px'
|
|
208
|
+
return
|
|
158
209
|
}
|
|
159
210
|
} else {
|
|
160
211
|
const min =
|
|
161
|
-
cells.value && cells.value[getCellId(
|
|
162
|
-
? cells.value[getCellId(
|
|
163
|
-
getLastCellOnTheRightExtraSpace(
|
|
212
|
+
cells.value && cells.value[getCellId(lastCell.value.cell)]
|
|
213
|
+
? cells.value[getCellId(lastCell.value.cell)].minWidth +
|
|
214
|
+
getLastCellOnTheRightExtraSpace(lastCell.value.cell)
|
|
164
215
|
: MIN_CELL_WIDTH
|
|
165
216
|
|
|
166
|
-
if (
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
217
|
+
if (
|
|
218
|
+
newLastCellWidth >= min &&
|
|
219
|
+
tableElement.value.tableRef.offsetWidth <= minTableWidth.value
|
|
220
|
+
) {
|
|
221
|
+
lastCell.value.cell.style.width = newLastCellWidth + 'px'
|
|
170
222
|
} else {
|
|
171
|
-
|
|
172
|
-
neighborCell.style.width = newNeighborCellWidth + 'px'
|
|
223
|
+
tableElement.value.tableRef.style.width = newTableWidth + 'px'
|
|
173
224
|
}
|
|
225
|
+
|
|
226
|
+
cell.style.width = newCellWidth + 'px'
|
|
174
227
|
}
|
|
175
228
|
}
|
|
176
229
|
|
|
@@ -179,27 +232,66 @@ export function useResizeColumns() {
|
|
|
179
232
|
|
|
180
233
|
if (cells.value) {
|
|
181
234
|
const resizingCell = cells.value[resizingCellId.value]?.cell
|
|
182
|
-
const neighborCell = cells.value[neighborCellId.value]?.cell
|
|
183
235
|
|
|
184
|
-
resizeCells(resizingCell,
|
|
236
|
+
resizeCells(resizingCell, e)
|
|
185
237
|
}
|
|
186
238
|
}
|
|
187
239
|
|
|
188
240
|
const resetCells = () => {
|
|
189
|
-
if (cells.value) {
|
|
190
|
-
|
|
241
|
+
if (cells.value && tableElement.value && tableElement.value?.tableRef) {
|
|
242
|
+
tableElement.value.tableRef.style.width = ''
|
|
191
243
|
|
|
192
|
-
|
|
193
|
-
|
|
244
|
+
calculatedColumnSizing.value = {}
|
|
245
|
+
setInitialColumnWidths()
|
|
246
|
+
}
|
|
247
|
+
}
|
|
194
248
|
|
|
195
|
-
|
|
196
|
-
|
|
249
|
+
const resetCell = (cellId: string) => {
|
|
250
|
+
if (
|
|
251
|
+
cells.value &&
|
|
252
|
+
calculatedColumnSizing.value &&
|
|
253
|
+
lastCell.value &&
|
|
254
|
+
tableElement.value &&
|
|
255
|
+
tableElement.value?.tableRef
|
|
256
|
+
) {
|
|
257
|
+
const thisCellBaseWidth = cells.value[cellId].baseWidth
|
|
258
|
+
const thisCellCurrentWidth = calculatedColumnSizing.value[cellId]
|
|
259
|
+
const diff = thisCellBaseWidth - thisCellCurrentWidth
|
|
260
|
+
|
|
261
|
+
let newTableWidth = calculatedColumnSizing.value['table'] + diff
|
|
262
|
+
|
|
263
|
+
if (newTableWidth < minTableWidth.value) {
|
|
264
|
+
const tableWidthDiff = minTableWidth.value - newTableWidth
|
|
265
|
+
const newLastCellWidth =
|
|
266
|
+
calculatedColumnSizing.value[getCellId(lastCell.value.cell)] + tableWidthDiff
|
|
267
|
+
|
|
268
|
+
lastCell.value.cell.style.width = newLastCellWidth + 'px'
|
|
269
|
+
newTableWidth = minTableWidth.value
|
|
197
270
|
}
|
|
198
271
|
|
|
199
|
-
|
|
272
|
+
cells.value[cellId].cell.style.width = thisCellBaseWidth + 'px'
|
|
273
|
+
tableElement.value.tableRef.style.width = newTableWidth + 'px'
|
|
274
|
+
calculatedColumnSizing.value[cellId] = thisCellBaseWidth
|
|
275
|
+
calculatedColumnSizing.value['table'] = newTableWidth
|
|
200
276
|
}
|
|
201
277
|
}
|
|
202
278
|
|
|
279
|
+
const getTableWrapperWidth = () => {
|
|
280
|
+
if (tableElement.value && tableElement.value.scrollAreaElementRef?.tableWrapperRef) {
|
|
281
|
+
return tableElement.value.scrollAreaElementRef?.tableWrapperRef.$el.offsetWidth - 3
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return 0
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const getTableWidth = () => {
|
|
288
|
+
if (tableElement.value && tableElement.value?.tableRef) {
|
|
289
|
+
return tableElement.value.tableRef.offsetWidth
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return 0
|
|
293
|
+
}
|
|
294
|
+
|
|
203
295
|
const setInitialColumnWidths = () => {
|
|
204
296
|
cells.value = getCells()
|
|
205
297
|
|
|
@@ -211,21 +303,38 @@ export function useResizeColumns() {
|
|
|
211
303
|
cells.value[cell].cell.style.width = cells.value[cell].initialWidth + 'px'
|
|
212
304
|
}
|
|
213
305
|
|
|
306
|
+
cells.value[cell].cell.style.minWidth = cells.value[cell].minWidth + 'px'
|
|
307
|
+
|
|
214
308
|
updatedColumnSizingValue[cell] = cells.value[cell].cell.offsetWidth
|
|
215
309
|
}
|
|
216
310
|
|
|
311
|
+
if (tableElement.value && tableElement.value?.tableRef) {
|
|
312
|
+
const tableOffsetWidth = getTableWidth()
|
|
313
|
+
|
|
314
|
+
if (calculatedColumnSizing.value?.['table'] && !tableElement.value.tableRef.style.width) {
|
|
315
|
+
tableElement.value.tableRef.style.width = calculatedColumnSizing.value['table'] + 'px'
|
|
316
|
+
} else {
|
|
317
|
+
initialTableWidth.value = tableOffsetWidth
|
|
318
|
+
tableElement.value.tableRef.style.width = tableOffsetWidth + 'px'
|
|
319
|
+
updatedColumnSizingValue['table'] = tableOffsetWidth
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
minTableWidth.value = getTableWrapperWidth()
|
|
217
324
|
calculatedColumnSizing.value = updatedColumnSizingValue
|
|
218
325
|
}
|
|
219
326
|
}
|
|
220
327
|
|
|
221
328
|
return {
|
|
222
329
|
cells,
|
|
330
|
+
tableElement,
|
|
223
331
|
tableHeaderElement,
|
|
224
332
|
calculatedColumnSizing,
|
|
225
333
|
isResizing,
|
|
226
334
|
resizingCellId,
|
|
227
335
|
handleResizeControlMouseDown,
|
|
228
336
|
handleResizeControlMouseUp,
|
|
337
|
+
resetCell,
|
|
229
338
|
resetCells,
|
|
230
339
|
setInitialColumnWidths,
|
|
231
340
|
setProvidedCellWidths,
|
|
@@ -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
|
})
|
|
@@ -115,7 +115,7 @@ function updateSelection(val?: RowSelectionState) {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
const columnVisibility = ref<VisibilityState>({ hiddenColumn: false })
|
|
118
|
-
const columnSizing = ref<Record<string, number>>({
|
|
118
|
+
const columnSizing = ref<Record<string, number>>({})
|
|
119
119
|
const columnOrder = ref<ColumnOrderState>()
|
|
120
120
|
|
|
121
121
|
type GroupBy = 'none' | 'status' | 'priority'
|
|
@@ -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>
|