mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-25 17:53:48 +02:00
add empty component and poilsh styles
This commit is contained in:
34
src/components/ui/data-table/DataTableEmpty.vue
Normal file
34
src/components/ui/data-table/DataTableEmpty.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<Empty>
|
||||
<EmptyHeader>
|
||||
<EmptyMedia variant="icon">
|
||||
<SearchAlert v-if="props.type === 'nomatch'" class="text-lg" />
|
||||
<Inbox v-else class="text-lg" />
|
||||
</EmptyMedia>
|
||||
<EmptyDescription>
|
||||
{{ emptyText }}
|
||||
</EmptyDescription>
|
||||
</EmptyHeader>
|
||||
</Empty>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Inbox, SearchAlert } from 'lucide-vue-next';
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { Empty, EmptyDescription, EmptyHeader, EmptyMedia } from '../empty';
|
||||
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
default: 'nodata'
|
||||
}
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const emptyText = computed(() => {
|
||||
return props.type === 'nomatch' ? t('common.no_matching_records') : t('common.no_data');
|
||||
});
|
||||
</script>
|
||||
@@ -59,9 +59,9 @@
|
||||
</template>
|
||||
|
||||
<TableRow v-else>
|
||||
<TableCell class="h-24 text-center">
|
||||
<TableCell class="h-24 text-center" :colspan="table.getVisibleLeafColumns().length">
|
||||
<slot name="empty">
|
||||
{{ emptyText }}
|
||||
<DataTableEmpty :type="emptyType" />
|
||||
</slot>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@@ -134,6 +134,8 @@
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../table';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../select';
|
||||
|
||||
import DataTableEmpty from './DataTableEmpty.vue';
|
||||
|
||||
const appearanceSettingsStore = useAppearanceSettingsStore();
|
||||
const { isDataTableStriped } = storeToRefs(appearanceSettingsStore);
|
||||
|
||||
@@ -162,10 +164,6 @@
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
emptyText: {
|
||||
type: String,
|
||||
default: 'No results.'
|
||||
},
|
||||
showPagination: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
@@ -187,6 +185,11 @@
|
||||
const { t } = useI18n();
|
||||
const tableScrollRef = ref(null);
|
||||
|
||||
const emptyType = computed(() => {
|
||||
const totalRows = props.table?.getCoreRowModel?.().rows?.length ?? 0;
|
||||
return totalRows === 0 ? 'nodata' : 'nomatch';
|
||||
});
|
||||
|
||||
const expandedRenderer = computed(() => {
|
||||
const columns = props.table.getAllColumns?.() ?? [];
|
||||
for (const column of columns) {
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export { default as DataTableLayout } from './DataTableLayout.vue';
|
||||
export { default as DataTableEmpty } from './DataTableEmpty.vue';
|
||||
|
||||
20
src/components/ui/empty/Empty.vue
Normal file
20
src/components/ui/empty/Empty.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<script setup>
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const props = defineProps({
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
data-slot="empty"
|
||||
:class="
|
||||
cn(
|
||||
'flex min-w-0 flex-1 flex-col items-center justify-center gap-6 text-balance rounded-lg border-dashed p-6 text-center md:p-12',
|
||||
props.class
|
||||
)
|
||||
">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
15
src/components/ui/empty/EmptyContent.vue
Normal file
15
src/components/ui/empty/EmptyContent.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<script setup>
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const props = defineProps({
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
data-slot="empty-content"
|
||||
:class="cn('flex w-full min-w-0 max-w-sm flex-col items-center gap-4 text-balance text-sm', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
20
src/components/ui/empty/EmptyDescription.vue
Normal file
20
src/components/ui/empty/EmptyDescription.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<script setup>
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
defineProps({
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p
|
||||
data-slot="empty-description"
|
||||
:class="
|
||||
cn(
|
||||
'text-muted-foreground [&>a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4',
|
||||
$attrs.class ?? ''
|
||||
)
|
||||
">
|
||||
<slot />
|
||||
</p>
|
||||
</template>
|
||||
13
src/components/ui/empty/EmptyHeader.vue
Normal file
13
src/components/ui/empty/EmptyHeader.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<script setup>
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const props = defineProps({
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div data-slot="empty-header" :class="cn('flex max-w-sm flex-col items-center gap-2 text-center', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
16
src/components/ui/empty/EmptyMedia.vue
Normal file
16
src/components/ui/empty/EmptyMedia.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<script setup>
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
import { emptyMediaVariants } from '.';
|
||||
|
||||
const props = defineProps({
|
||||
class: { type: null, required: false },
|
||||
variant: { type: null, required: false }
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div data-slot="empty-icon" :data-variant="variant" :class="cn(emptyMediaVariants({ variant }), props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
13
src/components/ui/empty/EmptyTitle.vue
Normal file
13
src/components/ui/empty/EmptyTitle.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<script setup>
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const props = defineProps({
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div data-slot="empty-title" :class="cn('text-lg font-medium tracking-tight', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
23
src/components/ui/empty/index.js
Normal file
23
src/components/ui/empty/index.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import { cva } from 'class-variance-authority';
|
||||
|
||||
export { default as Empty } from './Empty.vue';
|
||||
export { default as EmptyContent } from './EmptyContent.vue';
|
||||
export { default as EmptyDescription } from './EmptyDescription.vue';
|
||||
export { default as EmptyHeader } from './EmptyHeader.vue';
|
||||
export { default as EmptyMedia } from './EmptyMedia.vue';
|
||||
export { default as EmptyTitle } from './EmptyTitle.vue';
|
||||
|
||||
export const emptyMediaVariants = cva(
|
||||
'mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-transparent',
|
||||
icon: "bg-muted text-foreground flex size-10 shrink-0 items-center justify-center rounded-lg [&_svg:not([class*='size-'])]:size-6"
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default'
|
||||
}
|
||||
}
|
||||
);
|
||||
Reference in New Issue
Block a user