Skip to content

Commit a76af99

Browse files
authored
feat: Empty component (#1438)
* feat: implement empty component * chore: add spinner demo to v4 playground index * docs: add empty component with demos
1 parent 760e925 commit a76af99

35 files changed

+974
-0
lines changed

apps/v4/components/EmptyDemo.vue

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<script setup lang="ts">
2+
import { ArrowUpRightIcon, FolderCode } from 'lucide-vue-next'
3+
import { Button } from '@/registry/new-york-v4/ui/button'
4+
import {
5+
Empty,
6+
EmptyContent,
7+
EmptyDescription,
8+
EmptyHeader,
9+
EmptyMedia,
10+
EmptyTitle,
11+
} from '@/registry/new-york-v4/ui/empty'
12+
</script>
13+
14+
<template>
15+
<Empty>
16+
<EmptyHeader>
17+
<EmptyMedia variant="icon">
18+
<FolderCode />
19+
</EmptyMedia>
20+
<EmptyTitle>No Projects Yet</EmptyTitle>
21+
<EmptyDescription>
22+
You haven't created any projects yet. Get started by creating your first
23+
project.
24+
</EmptyDescription>
25+
</EmptyHeader>
26+
<EmptyContent>
27+
<div class="flex gap-2">
28+
<Button>Create Project</Button>
29+
<Button variant="outline">
30+
Import Project
31+
</Button>
32+
</div>
33+
</EmptyContent>
34+
<Button variant="link" as-child class="text-muted-foreground" size="sm">
35+
<a href="#">
36+
Learn More <ArrowUpRightIcon />
37+
</a>
38+
</Button>
39+
</Empty>
40+
</template>

apps/v4/pages/index.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@
6666
<ComponentWrapper name="DropdownMenu">
6767
<LazyDropdownMenuDemo />
6868
</ComponentWrapper>
69+
<ComponentWrapper name="Empty">
70+
<LazyEmptyDemo />
71+
</ComponentWrapper>
6972
<ComponentWrapper name="Form">
7073
<LazyFormDemo />
7174
</ComponentWrapper>
@@ -120,6 +123,9 @@
120123
<ComponentWrapper name="Sheet">
121124
<LazySheetDemo />
122125
</ComponentWrapper>
126+
<ComponentWrapper name="Spinner">
127+
<LazySpinnerDemo />
128+
</ComponentWrapper>
123129
<ComponentWrapper name="Skeleton">
124130
<LazySkeletonDemo />
125131
</ComponentWrapper>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from "vue"
3+
import { cn } from "@/lib/utils"
4+
5+
const props = defineProps<{
6+
class?: HTMLAttributes["class"]
7+
}>()
8+
</script>
9+
10+
<template>
11+
<div
12+
data-slot="empty"
13+
:class="cn(
14+
'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',
15+
props.class,
16+
)"
17+
v-bind="$attrs"
18+
>
19+
<slot />
20+
</div>
21+
</template>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from "vue"
3+
import { cn } from "@/lib/utils"
4+
5+
const props = defineProps<{
6+
class?: HTMLAttributes["class"]
7+
}>()
8+
</script>
9+
10+
<template>
11+
<div
12+
data-slot="empty-content"
13+
:class="cn(
14+
'flex w-full min-w-0 max-w-sm flex-col items-center gap-4 text-balance text-sm',
15+
props.class,
16+
)"
17+
v-bind="$attrs"
18+
>
19+
<slot />
20+
</div>
21+
</template>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from "vue"
3+
import { cn } from "@/lib/utils"
4+
5+
defineProps<{
6+
class?: HTMLAttributes["class"]
7+
}>()
8+
</script>
9+
10+
<template>
11+
<p
12+
data-slot="empty-description"
13+
:class="cn(
14+
'text-muted-foreground [&>a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4',
15+
$attrs.class ?? '',
16+
)"
17+
v-bind="$attrs"
18+
>
19+
<slot />
20+
</p>
21+
</template>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from "vue"
3+
import { cn } from "@/lib/utils"
4+
5+
defineProps<{
6+
class?: HTMLAttributes["class"]
7+
}>()
8+
</script>
9+
10+
<template>
11+
<div
12+
data-slot="empty-header"
13+
:class="cn(
14+
'flex max-w-sm flex-col items-center gap-2 text-center',
15+
$attrs.class ?? '',
16+
)"
17+
v-bind="$attrs"
18+
>
19+
<slot />
20+
</div>
21+
</template>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from "vue"
3+
import type { EmptyMediaVariants } from "."
4+
import { cn } from "@/lib/utils"
5+
import { emptyMediaVariants } from "."
6+
7+
const props = defineProps<{
8+
class?: HTMLAttributes["class"]
9+
variant?: EmptyMediaVariants["variant"]
10+
}>()
11+
</script>
12+
13+
<template>
14+
<div
15+
data-slot="empty-icon"
16+
:data-variant="variant"
17+
:class="cn(emptyMediaVariants({ variant }), props.class)"
18+
v-bind="$attrs"
19+
>
20+
<slot />
21+
</div>
22+
</template>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from "vue"
3+
import type { EmptyMediaVariants } from "."
4+
import { cn } from "@/lib/utils"
5+
import { emptyMediaVariants } from "."
6+
7+
const props = defineProps<{
8+
class?: HTMLAttributes["class"]
9+
variant?: EmptyMediaVariants["variant"]
10+
}>()
11+
</script>
12+
13+
<template>
14+
<div
15+
data-slot="empty-icon"
16+
:data-variant="variant"
17+
:class="cn(emptyMediaVariants({ variant }), props.class)"
18+
v-bind="$attrs"
19+
>
20+
<slot />
21+
</div>
22+
</template>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { VariantProps } from "class-variance-authority"
2+
import { cva } from "class-variance-authority"
3+
4+
export { default as Empty } from "./Empty.vue"
5+
export { default as EmptyContent } from "./EmptyContent.vue"
6+
export { default as EmptyDescription } from "./EmptyDescription.vue"
7+
export { default as EmptyHeader } from "./EmptyHeader.vue"
8+
export { default as EmptyMedia } from "./EmptyMedia.vue"
9+
export { default as EmptyTitle } from "./EmptyTitle.vue"
10+
11+
export const emptyMediaVariants = cva(
12+
"mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
13+
{
14+
variants: {
15+
variant: {
16+
default: "bg-transparent",
17+
icon: "bg-muted text-foreground flex size-10 shrink-0 items-center justify-center rounded-lg [&_svg:not([class*='size-'])]:size-6",
18+
},
19+
},
20+
defaultVariants: {
21+
variant: "default",
22+
},
23+
},
24+
)
25+
26+
export type EmptyMediaVariants = VariantProps<typeof emptyMediaVariants>

apps/www/.vitepress/theme/config/docs.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,10 @@ export const docsConfig: DocsConfig = {
266266
href: '/docs/components/drawer',
267267
items: [],
268268
},
269+
{
270+
title: 'Empty',
271+
href: '/docs/components/empty',
272+
},
269273
{
270274
title: 'Dropdown Menu',
271275
href: '/docs/components/dropdown-menu',

0 commit comments

Comments
 (0)