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

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/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'