@soft-stech/bootsman-ui-shadcn 1.1.16 → 1.1.18

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soft-stech/bootsman-ui-shadcn",
3
- "version": "1.1.16",
3
+ "version": "1.1.18",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -60,6 +60,7 @@
60
60
  "bumpp": "9.4.0",
61
61
  "eslint": "8.57.0",
62
62
  "eslint-plugin-vue": "9.24.0",
63
+ "fast-sort": "^3.4.0",
63
64
  "glob": "10.3.10",
64
65
  "histoire": "0.17.15",
65
66
  "jsdom": "23.2.0",
@@ -8,7 +8,7 @@ const isOpen = ref(false)
8
8
 
9
9
  <template>
10
10
  <Story title="BuiCollapsible" autoPropsDisabled>
11
- <Variant :key="`variant-default`" :title="`Tooltip`">
11
+ <Variant :key="`variant-default`" :title="`Default`">
12
12
  <div class="flex justify-center pt-12">
13
13
  <BuiCollapsible v-model:open="isOpen" class="w-[350px] space-y-2">
14
14
  <div class="flex items-center justify-between space-x-4 px-4">
@@ -1,16 +1,13 @@
1
1
  <script setup lang="ts">
2
2
  import { BuiDataTable } from '@/components/ui/table'
3
- import type {
4
- ColumnDef,
5
- PaginationState,
6
- RowSelectionState,
7
- SortingState
8
- } from '@tanstack/vue-table'
9
- import { h, ref } from 'vue'
10
- import { z } from 'zod'
11
- import { BuiButton, BuiCheckbox } from '@/index'
3
+ import { BuiButton, BuiCheckbox, BuiTabs, BuiTabsList, BuiTabsTrigger } from '@/index'
12
4
  import { tableColumnSortCommon } from '@/lib/utils'
5
+ import type { ColumnDef, PaginationState, Row, RowSelectionState } from '@tanstack/vue-table'
6
+ import { sort, type ISortByObjectSorter } from 'fast-sort'
13
7
  import { logEvent } from 'histoire/client'
8
+ import { AlignJustifyIcon, FolderIcon } from 'lucide-vue-next'
9
+ import { computed, h, ref } from 'vue'
10
+ import { z } from 'zod'
14
11
  import tasks from './data/tasks.json'
15
12
 
16
13
  const taskSchema = z.object({
@@ -18,7 +15,8 @@ const taskSchema = z.object({
18
15
  title: z.string(),
19
16
  status: z.string(),
20
17
  label: z.string(),
21
- priority: z.string()
18
+ priority: z.string(),
19
+ errorMessage: z.string().optional()
22
20
  })
23
21
  type Task = z.infer<typeof taskSchema>
24
22
  const columns: ColumnDef<Task>[] = [
@@ -59,7 +57,8 @@ const columns: ColumnDef<Task>[] = [
59
57
  ]
60
58
  const data = ref<Task[]>(tasks)
61
59
 
62
- const sorting = ref<SortingState>([{ id: 'status', desc: false }])
60
+ type TaskSortingState = { id: keyof Task; desc: boolean }
61
+ const sorting = ref<TaskSortingState[]>([{ id: 'id', desc: false }])
63
62
  const pagination = ref<PaginationState>({
64
63
  pageIndex: 0,
65
64
  pageSize: 10
@@ -71,14 +70,42 @@ function updateSelection(val: RowSelectionState) {
71
70
  logEvent('selection was changed', val)
72
71
  selection.value = val
73
72
  }
73
+
74
+ type GroupBy = 'none' | 'status'
75
+ const groupBy = ref<GroupBy>('none')
76
+ const groupLabels = {
77
+ status: 'Status'
78
+ }
79
+
80
+ const sortedData = computed(() => {
81
+ const sortDirection = (sorting.value[0].desc ? 'desc' : 'asc') as 'asc'
82
+ const sortColumn = sorting.value[0].id
83
+ const groupByStr = groupBy.value
84
+
85
+ const sortBy: ISortByObjectSorter<Task> | ISortByObjectSorter<Task>[] = [
86
+ {
87
+ [sortDirection]: groupByStr === 'none' ? sortColumn : [(row) => row[groupByStr], sortColumn]
88
+ }
89
+ ]
90
+
91
+ return sort(data.value).by([...sortBy])
92
+ })
93
+
94
+ function renderSubComponent(row: Row<Task>) {
95
+ if (row.original.errorMessage) {
96
+ return () => h('span', { style: 'color: red' }, `Subrow: ${row.original.errorMessage}`)
97
+ } else {
98
+ return undefined
99
+ }
100
+ }
74
101
  </script>
75
102
 
76
103
  <template>
77
104
  <Story title="BuiDataTable" autoPropsDisabled :layout="{ type: 'grid', width: '95%' }">
78
- <Variant key="variant" title="Sorting, Pagination">
105
+ <Variant key="variant" title="Sorting, Pagination, Grouping, Subrow">
79
106
  <BuiDataTable
80
107
  :columns="columns"
81
- :data="data"
108
+ :data="sortedData"
82
109
  v-model:sorting="sorting"
83
110
  v-model:pagination="pagination"
84
111
  @update:selection="updateSelection"
@@ -86,14 +113,29 @@ function updateSelection(val: RowSelectionState) {
86
113
  class="caption-top"
87
114
  :manualPagination="false"
88
115
  :getRowId="(row) => row.id"
116
+ :groupBy="groupBy === 'none' ? undefined : groupBy"
117
+ :groupLabels="groupLabels"
118
+ :renderSubComponent="renderSubComponent"
89
119
  >
90
120
  <template #caption="{ table }">
91
121
  <div class="flex justify-between">
92
122
  <BuiButton variant="outline">Download YAML</BuiButton>
93
- <span
94
- >{{ table.getFilteredSelectedRowModel().rows?.length }} of
95
- {{ table.getFilteredRowModel().rows?.length }} row(s) selected</span
96
- >
123
+
124
+ <BuiTabs v-model="groupBy">
125
+ <BuiTabsList class="grid w-full grid-cols-2" :variant="'default'">
126
+ <BuiTabsTrigger value="none" :variant="'default'"
127
+ ><AlignJustifyIcon
128
+ /></BuiTabsTrigger>
129
+ <BuiTabsTrigger value="status" :variant="'default'">
130
+ <FolderIcon />
131
+ </BuiTabsTrigger>
132
+ </BuiTabsList>
133
+ </BuiTabs>
134
+
135
+ <span>
136
+ {{ table.getFilteredSelectedRowModel().rows?.length }} of
137
+ {{ table.getFilteredRowModel().rows?.length }} row(s) selected
138
+ </span>
97
139
  </div>
98
140
  </template>
99
141
  </BuiDataTable>
@@ -417,7 +417,8 @@
417
417
  "title": "Backing up the driver won't do anything, we need to parse the redundant RAM pixel!",
418
418
  "status": "done",
419
419
  "label": "feature",
420
- "priority": "medium"
420
+ "priority": "medium",
421
+ "errorMessage": "ERROR"
421
422
  },
422
423
  {
423
424
  "id": "TASK-1427",
@@ -46,7 +46,7 @@ const pageSizeString = computed({
46
46
  :itemsPerPage="pageSize"
47
47
  v-model:page="pageIndex"
48
48
  >
49
- <BuiPaginationList class="flex items-center justify-center gap-2">
49
+ <BuiPaginationList class="relative flex items-center justify-center gap-2">
50
50
  <p class="text-sm text-muted-foreground">Items per page</p>
51
51
  <BuiSelect v-model.number="pageSizeString">
52
52
  <BuiSelectTrigger class="mr-2 w-[70px]">
@@ -1,10 +1,18 @@
1
1
  <script setup lang="ts" generic="TData, TValue">
2
+ import {
3
+ BuiCollapsible,
4
+ BuiCollapsibleContent,
5
+ BuiCollapsibleTrigger
6
+ } from '@/components/ui/collapsible'
7
+ import { BuiPaginationCommon, type PageSize } from '@/components/ui/pagination'
8
+ import BuiTableRowSubrow from '@/components/ui/table/BuiTableRowSubrow.vue'
9
+ import { valueUpdater } from '@/lib/utils'
2
10
  import type {
3
11
  ColumnDef,
4
- SortingState,
5
12
  PaginationState,
13
+ Row,
6
14
  RowSelectionState,
7
- Row
15
+ SortingState
8
16
  } from '@tanstack/vue-table'
9
17
  import {
10
18
  FlexRender,
@@ -13,6 +21,7 @@ import {
13
21
  getSortedRowModel,
14
22
  useVueTable
15
23
  } from '@tanstack/vue-table'
24
+ import { computed } from 'vue'
16
25
  import {
17
26
  BuiTable,
18
27
  BuiTableBody,
@@ -24,9 +33,6 @@ import {
24
33
  BuiTableHeader,
25
34
  BuiTableRow
26
35
  } from './'
27
- import { BuiPaginationCommon, type PageSize } from '@/components/ui/pagination'
28
- import { valueUpdater } from '@/lib/utils'
29
- import { computed } from 'vue'
30
36
 
31
37
  const props = withDefaults(
32
38
  defineProps<{
@@ -37,7 +43,10 @@ const props = withDefaults(
37
43
  totalItems?: number
38
44
  manualPagination?: boolean
39
45
  manualSorting?: boolean
46
+ groupBy?: keyof TData
47
+ groupLabels?: { [key in keyof TData]?: string }
40
48
  getRowId?: (originalRow: TData, index: number, parent?: Row<TData>) => string
49
+ renderSubComponent?: (row: Row<TData>) => (() => any) | undefined
41
50
  }>(),
42
51
  { pageSize: 10, showPagination: true, manualPagination: true, manualSorting: true, totalItems: 0 }
43
52
  )
@@ -105,6 +114,17 @@ const pageIndex = computed({
105
114
  table.setPageIndex(index - 1)
106
115
  }
107
116
  })
117
+
118
+ const groupedRows = computed(() => {
119
+ if (!props.groupBy) return null
120
+
121
+ return table.getRowModel().rows.reduce((acc, row) => {
122
+ const col = row.getValue(props.groupBy! as string) as string
123
+ acc[col] = acc[col] || []
124
+ acc[col].push(row)
125
+ return acc
126
+ }, Object.create(null))
127
+ })
108
128
  </script>
109
129
 
110
130
  <template>
@@ -123,21 +143,44 @@ const pageIndex = computed({
123
143
  </BuiTableHeader>
124
144
  <BuiTableBody>
125
145
  <template v-if="table.getRowModel().rows?.length">
126
- <BuiTableRow
127
- v-for="row in table.getRowModel().rows"
128
- :key="row.id"
129
- :data-state="row.getIsSelected() ? 'selected' : undefined"
130
- >
131
- <BuiTableCell v-for="cell in row.getVisibleCells()" :key="cell.id">
132
- <FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
133
- </BuiTableCell>
134
- </BuiTableRow>
146
+ <template v-if="props.groupBy && groupedRows">
147
+ <BuiCollapsible asChild v-for="(value, key) in groupedRows" :key="key" :open="true">
148
+ <BuiCollapsibleTrigger asChild>
149
+ <BuiTableRow class="bg-foreground/[0.04]">
150
+ <BuiTableCell :colspan="columns.length" class="!pb-0">
151
+ <div class="inline-block rounded-t bg-background px-4 py-3">
152
+ {{ props.groupLabels && props.groupLabels[props.groupBy] }}: {{ key }}
153
+ </div>
154
+ </BuiTableCell>
155
+ </BuiTableRow>
156
+ </BuiCollapsibleTrigger>
157
+ <BuiCollapsibleContent asChild>
158
+ <template v-for="row in value" :key="row.id">
159
+ <BuiTableRowSubrow
160
+ :columns="props.columns"
161
+ :row="row"
162
+ :renderSubComponent="props.renderSubComponent"
163
+ />
164
+ </template>
165
+ </BuiCollapsibleContent>
166
+ </BuiCollapsible>
167
+ </template>
168
+
169
+ <template v-else>
170
+ <template v-for="row in table.getRowModel().rows" :key="row.id">
171
+ <BuiTableRowSubrow
172
+ :columns="props.columns"
173
+ :row="row"
174
+ :renderSubComponent="props.renderSubComponent"
175
+ />
176
+ </template>
177
+ </template>
135
178
  </template>
136
179
  <template v-else>
137
180
  <BuiTableEmpty :colspan="columns.length">No data</BuiTableEmpty>
138
181
  </template>
139
182
  </BuiTableBody>
140
- <BuiTableFooter v-if="showPagination && table.getPageCount()">
183
+ <BuiTableFooter v-if="showPagination && table.getPageCount() > 1">
141
184
  <BuiTableRow>
142
185
  <BuiTableCell :colspan="columns.length">
143
186
  <BuiPaginationCommon
@@ -0,0 +1,30 @@
1
+ <script setup lang="ts" generic="TData, TValue">
2
+ import { BuiTableCell, BuiTableRow } from '@/components/ui/table'
3
+ import { FlexRender, type ColumnDef, type Row } from '@tanstack/vue-table'
4
+
5
+ const props = defineProps<{
6
+ row: Row<TData>
7
+ renderSubComponent?: (row: Row<TData>) => (() => any) | undefined
8
+ columns: ColumnDef<TData, TValue>[]
9
+ }>()
10
+ </script>
11
+
12
+ <template>
13
+ <BuiTableRow
14
+ :data-state="row.getIsSelected() ? 'selected' : undefined"
15
+ :class="props.renderSubComponent?.(row) ? 'border-b-0' : ''"
16
+ >
17
+ <BuiTableCell v-for="cell in row.getVisibleCells()" :key="cell.id">
18
+ <FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
19
+ </BuiTableCell>
20
+ </BuiTableRow>
21
+
22
+ <BuiTableRow
23
+ v-if="props.renderSubComponent?.(row)"
24
+ :data-state="row.getIsSelected() ? 'selected' : undefined"
25
+ >
26
+ <BuiTableCell :colspan="columns.length" class="pt-0">
27
+ <component :is="props.renderSubComponent?.(row)?.()"></component>
28
+ </BuiTableCell>
29
+ </BuiTableRow>
30
+ </template>
@@ -4,6 +4,7 @@ export { default as BuiTableCell } from './BuiTableCell.vue'
4
4
  export { default as BuiTableHead } from './BuiTableHead.vue'
5
5
  export { default as BuiTableHeader } from './BuiTableHeader.vue'
6
6
  export { default as BuiTableRow } from './BuiTableRow.vue'
7
+ export { default as BuiTableRowSubrow } from './BuiTableRowSubrow.vue'
7
8
  export { default as BuiTableCaption } from './BuiTableCaption.vue'
8
9
  export { default as BuiTableEmpty } from './BuiTableEmpty.vue'
9
10
  export { default as BuiTableFooter } from './BuiTableFooter.vue'