Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions app/pages/components/Empty.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<script setup lang="ts">
import { ArrowUpRightIcon, FolderCode } from "lucide-vue-next"
import { Button } from "@/components/button"
import {
Empty,
EmptyContent,
EmptyDescription,
EmptyHeader,
EmptyMedia,
EmptyTitle,
} from "@/components/empty"
</script>

<template>
<Empty>
<EmptyHeader>
<EmptyMedia variant="icon">
<FolderCode />
</EmptyMedia>

<EmptyTitle>No Projects Yet</EmptyTitle>

<EmptyDescription>
You haven't created any projects yet. Get started by creating your first project.
</EmptyDescription>
</EmptyHeader>

<EmptyContent>
<div class="flex gap-2">
<Button>Create Project</Button>

<Button variant="outline"> Import Project </Button>
</div>
</EmptyContent>

<Button variant="link" as-child class="text-muted-foreground" size="sm">
<a href="#"> Learn More <ArrowUpRightIcon /> </a>
</Button>
</Empty>
</template>
1 change: 1 addition & 0 deletions app/pages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export { default as ConfirmDialog } from "./components/ConfirmDialog.vue"
export { default as Dialog } from "./components/Dialog.vue"
export { default as Drawer } from "./components/Drawer.vue"
export { default as DropdownMenu } from "./components/DropdownMenu.vue"
export { default as Empty } from "./components/Empty.vue"
export { default as Flasher } from "./components/Flasher.vue"
export { default as Heading } from "./components/Heading.vue"
export { default as Input } from "./components/Input.vue"
Expand Down
7 changes: 7 additions & 0 deletions app/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
Dialog,
Drawer,
DropdownMenu,
Empty,
Flasher,
Heading,
Input,
Expand Down Expand Up @@ -138,6 +139,12 @@ const routes = [
component: DropdownMenu,
meta: { layout: ComponentLayout, shadcn: true },
},
{
name: "Empty",
path: "/components/empty",
component: Empty,
meta: { layout: ComponentLayout, shadcn: true },
},
{
name: "Flasher",
path: "/components/flasher",
Expand Down
22 changes: 22 additions & 0 deletions src/components/empty/Empty.vue
Original file line number Diff line number Diff line change
@@ -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>
<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>
22 changes: 22 additions & 0 deletions src/components/empty/EmptyContent.vue
Original file line number Diff line number Diff line change
@@ -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>
<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>
22 changes: 22 additions & 0 deletions src/components/empty/EmptyDescription.vue
Original file line number Diff line number Diff line change
@@ -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>
<p
data-slot="empty-description"
:class="
cn(
'text-muted-foreground [&>a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4',
props.class
)
"
>
<slot />
</p>
</template>
17 changes: 17 additions & 0 deletions src/components/empty/EmptyHeader.vue
Original file line number Diff line number Diff line change
@@ -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>
<div
data-slot="empty-header"
:class="cn('flex max-w-sm flex-col items-center gap-2 text-center', props.class)"
>
<slot />
</div>
</template>
21 changes: 21 additions & 0 deletions src/components/empty/EmptyMedia.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from "@/lib/utils"
import { emptyMediaVariants } from "."
import type { EmptyMediaVariants } from "."

const props = defineProps<{
class?: HTMLAttributes["class"]
variant?: EmptyMediaVariants["variant"]
}>()
</script>

<template>
<div
data-slot="empty-icon"
:data-variant="variant"
:class="cn(emptyMediaVariants({ variant }), props.class)"
>
<slot />
</div>
</template>
14 changes: 14 additions & 0 deletions src/components/empty/EmptyTitle.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<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="empty-title" :class="cn('text-lg font-medium tracking-tight', props.class)">
<slot />
</div>
</template>
5 changes: 5 additions & 0 deletions src/components/empty/empty.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
describe("template spec", () => {
it("passes", () => {
cy.visit("/components/empty")
})
})
26 changes: 26 additions & 0 deletions src/components/empty/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { VariantProps } from "class-variance-authority"
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: "flex size-10 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_svg:not([class*='size-'])]:size-6",
},
},
defaultVariants: {
variant: "default",
},
}
)

export type EmptyMediaVariants = VariantProps<typeof emptyMediaVariants>
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export * from "@/components/command"
export * from "@/components/dialog"
export * from "@/components/drawer"
export * from "@/components/dropdown-menu"
export * from "@/components/empty"
export * from "@/components/input"
export * from "@/components/label"
export * from "@/components/pagination"
Expand Down