refector: add table

This commit is contained in:
Quicy
2026-01-19 14:32:08 +08:00
parent bc63e85d13
commit 7e22919c80
14 changed files with 223 additions and 4 deletions
+1
View File
@@ -20,6 +20,7 @@
},
"dependencies": {
"@tailwindcss/vite": "^4.1.18",
"@tanstack/vue-table": "^8.21.3",
"@vee-validate/zod": "^4.15.1",
"@vueuse/core": "^14.1.0",
"class-variance-authority": "^0.7.1",
@@ -0,0 +1,16 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from '#/lib/utils'
const props = defineProps<{
class?: HTMLAttributes["class"]
}>()
</script>
<template>
<div data-slot="table-container" class="relative w-full overflow-auto">
<table data-slot="table" :class="cn('w-full caption-bottom text-sm', props.class)">
<slot />
</table>
</div>
</template>
@@ -0,0 +1,17 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from '#/lib/utils'
const props = defineProps<{
class?: HTMLAttributes["class"]
}>()
</script>
<template>
<tbody
data-slot="table-body"
:class="cn('[&_tr:last-child]:border-0', props.class)"
>
<slot />
</tbody>
</template>
@@ -0,0 +1,17 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from '#/lib/utils'
const props = defineProps<{
class?: HTMLAttributes["class"]
}>()
</script>
<template>
<caption
data-slot="table-caption"
:class="cn('text-muted-foreground mt-4 text-sm', props.class)"
>
<slot />
</caption>
</template>
@@ -0,0 +1,22 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from '#/lib/utils'
const props = defineProps<{
class?: HTMLAttributes["class"]
}>()
</script>
<template>
<td
data-slot="table-cell"
:class="
cn(
'p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
props.class,
)
"
>
<slot />
</td>
</template>
@@ -0,0 +1,34 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { reactiveOmit } from "@vueuse/core"
import { cn } from '#/lib/utils'
import TableCell from "./TableCell.vue"
import TableRow from "./TableRow.vue"
const props = withDefaults(defineProps<{
class?: HTMLAttributes["class"]
colspan?: number
}>(), {
colspan: 1,
})
const delegatedProps = reactiveOmit(props, "class")
</script>
<template>
<TableRow>
<TableCell
:class="
cn(
'p-4 whitespace-nowrap align-middle text-sm text-foreground',
props.class,
)
"
v-bind="delegatedProps"
>
<div class="flex items-center justify-center py-10">
<slot />
</div>
</TableCell>
</TableRow>
</template>
@@ -0,0 +1,17 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from '#/lib/utils'
const props = defineProps<{
class?: HTMLAttributes["class"]
}>()
</script>
<template>
<tfoot
data-slot="table-footer"
:class="cn('bg-muted/50 border-t font-medium [&>tr]:last:border-b-0', props.class)"
>
<slot />
</tfoot>
</template>
@@ -0,0 +1,17 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from '#/lib/utils'
const props = defineProps<{
class?: HTMLAttributes["class"]
}>()
</script>
<template>
<th
data-slot="table-head"
:class="cn('text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]', props.class)"
>
<slot />
</th>
</template>
@@ -0,0 +1,17 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from '#/lib/utils'
const props = defineProps<{
class?: HTMLAttributes["class"]
}>()
</script>
<template>
<thead
data-slot="table-header"
:class="cn('[&_tr]:border-b', props.class)"
>
<slot />
</thead>
</template>
@@ -0,0 +1,17 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from '#/lib/utils'
const props = defineProps<{
class?: HTMLAttributes["class"]
}>()
</script>
<template>
<tr
data-slot="table-row"
:class="cn('hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors', props.class)"
>
<slot />
</tr>
</template>
@@ -0,0 +1,9 @@
export { default as Table } from "./Table.vue"
export { default as TableBody } from "./TableBody.vue"
export { default as TableCaption } from "./TableCaption.vue"
export { default as TableCell } from "./TableCell.vue"
export { default as TableEmpty } from "./TableEmpty.vue"
export { default as TableFooter } from "./TableFooter.vue"
export { default as TableHead } from "./TableHead.vue"
export { default as TableHeader } from "./TableHeader.vue"
export { default as TableRow } from "./TableRow.vue"
+10
View File
@@ -0,0 +1,10 @@
import type { Updater } from "@tanstack/vue-table"
import type { Ref } from "vue"
import { isFunction } from "@tanstack/vue-table"
export function valueUpdater<T>(updaterOrValue: Updater<T>, ref: Ref<T>) {
ref.value = isFunction(updaterOrValue)
? updaterOrValue(ref.value)
: updaterOrValue
}
+1
View File
@@ -27,6 +27,7 @@ export * from './components/slider/index'
export * from './components/sonner/index'
export * from './components/spinner/index'
export * from './components/switch/index'
export * from './components/table/index'
export * from './components/tabs/index'
export * from './components/textarea/index'
export * from './components/tooltip/index'
+28 -4
View File
@@ -143,6 +143,9 @@ importers:
'@tailwindcss/vite':
specifier: ^4.1.18
version: 4.1.18(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))
'@tanstack/vue-table':
specifier: ^8.21.3
version: 8.21.3(vue@3.5.26(typescript@5.9.3))
'@vee-validate/zod':
specifier: ^4.15.1
version: 4.15.1(vue@3.5.26(typescript@5.9.3))(zod@3.25.76)
@@ -1790,9 +1793,19 @@ packages:
peerDependencies:
vite: ^5.2.0 || ^6 || ^7
'@tanstack/table-core@8.21.3':
resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==}
engines: {node: '>=12'}
'@tanstack/virtual-core@3.13.17':
resolution: {integrity: sha512-m5mRfGNcL5GUzluWNom0Rmg8P8Dg3h6PnJtJBmJcBiJvkV+vufmUfLnVzKSPGQtmvzMW/ZuUdvL+SyjIUvHV3A==}
'@tanstack/vue-table@8.21.3':
resolution: {integrity: sha512-rusRyd77c5tDPloPskctMyPLFEQUeBzxdQ+2Eow4F7gDPlPOB1UnnhzfpdvqZ8ZyX2rRNGmqNnQWm87OI2OQPw==}
engines: {node: '>=12'}
peerDependencies:
vue: '>=3.2'
'@tanstack/vue-virtual@3.13.17':
resolution: {integrity: sha512-w+Btl94IkuL7c2hSVSD0t8tXfhLRnKppOlGKlzBGjw0SrlIgKbiOJv/FcSTCO3SeyI9h0sx2gF/cO/PONtkidw==}
peerDependencies:
@@ -3692,13 +3705,14 @@ packages:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
type-is@2.0.1:
resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==}
engines: {node: '>= 0.6'}
type-fest@4.41.0:
resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
engines: {node: '>=16'}
type-is@2.0.1:
resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==}
engines: {node: '>= 0.6'}
typescript-eslint@8.52.0:
resolution: {integrity: sha512-atlQQJ2YkO4pfTVQmQ+wvYQwexPDOIgo+RaVcD7gHgzy/IQA+XTyuxNM9M9TVXvttkF7koBHmcwisKdOAf2EcA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -3805,6 +3819,7 @@ packages:
vary@1.1.2:
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
engines: {node: '>= 0.8'}
vee-validate@4.15.1:
resolution: {integrity: sha512-DkFsiTwEKau8VIxyZBGdO6tOudD+QoUBPuHj3e6QFqmbfCRj1ArmYWue9lEp6jLSWBIw4XPlDLjFIZNLdRAMSg==}
peerDependencies:
@@ -5394,8 +5409,15 @@ snapshots:
tailwindcss: 4.1.18
vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)
'@tanstack/table-core@8.21.3': {}
'@tanstack/virtual-core@3.13.17': {}
'@tanstack/vue-table@8.21.3(vue@3.5.26(typescript@5.9.3))':
dependencies:
'@tanstack/table-core': 8.21.3
vue: 3.5.26(typescript@5.9.3)
'@tanstack/vue-virtual@3.13.17(vue@3.5.26(typescript@5.9.3))':
dependencies:
'@tanstack/virtual-core': 3.13.17
@@ -7447,12 +7469,13 @@ snapshots:
dependencies:
prelude-ls: 1.2.1
type-fest@4.41.0: {}
type-is@2.0.1:
dependencies:
content-type: 1.0.5
media-typer: 1.1.0
mime-types: 3.0.2
type-fest@4.41.0: {}
typescript-eslint@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3):
dependencies:
@@ -7549,6 +7572,7 @@ snapshots:
util-deprecate@1.0.2: {}
vary@1.1.2: {}
vee-validate@4.15.1(vue@3.5.26(typescript@5.9.3)):
dependencies:
'@vue/devtools-api': 7.7.9