Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: breadcrumb component #405

Merged
merged 6 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
6 changes: 6 additions & 0 deletions apps/www/.vitepress/theme/config/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,12 @@ export const docsConfig: DocsConfig = {
href: '/docs/components/badge',
items: [],
},
{
title: 'Breadcrumb',
href: '/docs/components/breadcrumb',
items: [],
label: 'New',
},
{
title: 'Button',
href: '/docs/components/button',
Expand Down
11 changes: 5 additions & 6 deletions apps/www/.vitepress/theme/layout/MainLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import { useMagicKeys, useToggle } from '@vueuse/core'
import { onMounted, ref, watch } from 'vue'
import { Content, useData, useRoute, useRouter } from 'vitepress'
import { SearchIcon } from 'lucide-vue-next'
import { type NavItem, docsConfig } from '../config/docs'
import Logo from '../components/Logo.vue'
import MobileNav from '../components/MobileNav.vue'
Expand Down Expand Up @@ -119,11 +118,11 @@ watch(() => $route.path, (n) => {
class="relative h-8 w-full justify-start rounded-[0.5rem] bg-background text-sm font-normal text-muted-foreground shadow-none sm:pr-12 md:w-40 lg:w-64"
@click="isOpen = true"
>
<span className="hidden lg:inline-flex">Search documentation...</span>
<span className="inline-flex lg:hidden">Search...</span>
<kbd className="pointer-events-none absolute right-[0.3rem] top-[0.3rem] hidden h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium opacity-100 sm:flex">
<span className="text-xs">⌘</span>K
</kbd>
<span class="hidden lg:inline-flex">Search documentation...</span>
<span class="inline-flex lg:hidden">Search...</span>
<Kbd class="pointer-events-none absolute right-[0.3rem] top-[0.3rem] hidden h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium opacity-100 sm:flex">
<span class="text-xs">⌘</span>K
</Kbd>
</Button>
</div>

Expand Down
84 changes: 84 additions & 0 deletions apps/www/__registry__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,48 @@ export const Index = {
component: () => import("../src/lib/registry/default/example/BadgeSecondaryDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/BadgeSecondaryDemo.vue"],
},
"BreadcrumbDemo": {
name: "BreadcrumbDemo",
type: "components:example",
registryDependencies: ["breadcrumb","dropdown-menu"],
component: () => import("../src/lib/registry/default/example/BreadcrumbDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/BreadcrumbDemo.vue"],
},
"BreadcrumbDropdown": {
name: "BreadcrumbDropdown",
type: "components:example",
registryDependencies: ["breadcrumb","dropdown-menu"],
component: () => import("../src/lib/registry/default/example/BreadcrumbDropdown.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/BreadcrumbDropdown.vue"],
},
"BreadcrumbEllipsisDemo": {
name: "BreadcrumbEllipsisDemo",
type: "components:example",
registryDependencies: ["breadcrumb"],
component: () => import("../src/lib/registry/default/example/BreadcrumbEllipsisDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/BreadcrumbEllipsisDemo.vue"],
},
"BreadcrumbLinkDemo": {
name: "BreadcrumbLinkDemo",
type: "components:example",
registryDependencies: ["breadcrumb"],
component: () => import("../src/lib/registry/default/example/BreadcrumbLinkDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/BreadcrumbLinkDemo.vue"],
},
"BreadcrumbResponsive": {
name: "BreadcrumbResponsive",
type: "components:example",
registryDependencies: ["breadcrumb","button","drawer","dropdown-menu"],
component: () => import("../src/lib/registry/default/example/BreadcrumbResponsive.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/BreadcrumbResponsive.vue"],
},
"BreadcrumbSeparatorDemo": {
name: "BreadcrumbSeparatorDemo",
type: "components:example",
registryDependencies: ["breadcrumb"],
component: () => import("../src/lib/registry/default/example/BreadcrumbSeparatorDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/BreadcrumbSeparatorDemo.vue"],
},
"ButtonAsChildDemo": {
name: "ButtonAsChildDemo",
type: "components:example",
Expand Down Expand Up @@ -1131,6 +1173,48 @@ export const Index = {
component: () => import("../src/lib/registry/new-york/example/BadgeSecondaryDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/BadgeSecondaryDemo.vue"],
},
"BreadcrumbDemo": {
name: "BreadcrumbDemo",
type: "components:example",
registryDependencies: ["breadcrumb","dropdown-menu"],
component: () => import("../src/lib/registry/new-york/example/BreadcrumbDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/BreadcrumbDemo.vue"],
},
"BreadcrumbDropdown": {
name: "BreadcrumbDropdown",
type: "components:example",
registryDependencies: ["breadcrumb","dropdown-menu"],
component: () => import("../src/lib/registry/new-york/example/BreadcrumbDropdown.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/BreadcrumbDropdown.vue"],
},
"BreadcrumbEllipsisDemo": {
name: "BreadcrumbEllipsisDemo",
type: "components:example",
registryDependencies: ["breadcrumb"],
component: () => import("../src/lib/registry/new-york/example/BreadcrumbEllipsisDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/BreadcrumbEllipsisDemo.vue"],
},
"BreadcrumbLinkDemo": {
name: "BreadcrumbLinkDemo",
type: "components:example",
registryDependencies: ["breadcrumb"],
component: () => import("../src/lib/registry/new-york/example/BreadcrumbLinkDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/BreadcrumbLinkDemo.vue"],
},
"BreadcrumbResponsive": {
name: "BreadcrumbResponsive",
type: "components:example",
registryDependencies: ["breadcrumb","button","drawer","dropdown-menu"],
component: () => import("../src/lib/registry/new-york/example/BreadcrumbResponsive.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/BreadcrumbResponsive.vue"],
},
"BreadcrumbSeparatorDemo": {
name: "BreadcrumbSeparatorDemo",
type: "components:example",
registryDependencies: ["breadcrumb"],
component: () => import("../src/lib/registry/new-york/example/BreadcrumbSeparatorDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/BreadcrumbSeparatorDemo.vue"],
},
"ButtonAsChildDemo": {
name: "ButtonAsChildDemo",
type: "components:example",
Expand Down
2 changes: 1 addition & 1 deletion apps/www/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"embla-carousel-autoplay": "^8.0.0",
"embla-carousel-vue": "^8.0.0",
"lucide-vue-next": "^0.350.0",
"radix-vue": "^1.5.0",
"radix-vue": "^1.5.2",
"tailwindcss-animate": "^1.0.7",
"v-calendar": "^3.1.2",
"vaul-vue": "^0.1.0",
Expand Down
205 changes: 205 additions & 0 deletions apps/www/src/content/docs/components/breadcrumb.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
---
title: Breadcrumb
description: Displays the path to the current resource using a hierarchy of links.
---

<ComponentPreview name="BreadcrumbDemo" class="[&_.preview]:p-2" />

## Installation

```bash
npx shadcn-ui@latest add breadcrumb
```

## Usage

```vue
<script setup lang="ts">
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from '@/lib/components/ui/breadcrumb'
</script>

<template>
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink href="/">
Home
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbLink href="/components">
Components
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</template>
```

## Examples

### Custom separator

Use a custom component as `slot` for `<BreadcrumbSeparator />` to create a custom separator.

<ComponentPreview name="BreadcrumbSeparatorDemo" />

```vue showLineNumbers {2,20-22}
<script setup lang="ts">
import { Slash } from 'lucide-react'
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbSeparator,
} from '@/lib/components/ui/breadcrumb'
</script>

<template>
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink href="/">
Home
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator>
<Slash />
</BreadcrumbSeparator>
<BreadcrumbItem>
<BreadcrumbLink href="/components">
Components
</BreadcrumbLink>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</template>
```

---

### Dropdown

You can compose `<BreadcrumbItem />` with a `<DropdownMenu />` to create a dropdown in the breadcrumb.

<ComponentPreview name="BreadcrumbDropdown" class="[&_.preview]:p-2" />

```vue showLineNumbers {2-7,16-26}
<script setup lang="ts">
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/lib/components/ui/dropdown-menu'

import { BreadcrumbItem } from '@/lib/components/ui/breadcrumb'

import ChevronDownIcon from '~icons/radix-icons/chevron-down'
</script>

<template>
<BreadcrumbItem>
<DropdownMenu>
<DropdownMenuTrigger class="flex items-center gap-1">
Components
<ChevronDownIcon />
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem>Documentation</DropdownMenuItem>
<DropdownMenuItem>Themes</DropdownMenuItem>
<DropdownMenuItem>GitHub</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</BreadcrumbItem>
</template>
```

---

### Collapsed

We provide a `<BreadcrumbEllipsis />` component to show a collapsed state when the breadcrumb is too long.

<ComponentPreview name="BreadcrumbEllipsisDemo" class="[&_.preview]:p-2" />

```vue showLineNumbers {3,15}
<script setup lang="ts">
import {
Breadcrumb,
BreadcrumbEllipsis,
BreadcrumbItem,
BreadcrumbList,
} from '@/lib/components/ui/breadcrumb'
</script>

<template>
<Breadcrumb>
<BreadcrumbList>
<!-- ... -->
<BreadcrumbItem>
<BreadcrumbEllipsis />
</BreadcrumbItem>
<!-- ... -->
</BreadcrumbList>
</Breadcrumb>
</template>
```

---

### Link component

To use a custom link component from your routing library, you can use the `asChild` prop on `<BreadcrumbLink />`.

<ComponentPreview name="BreadcrumbLinkDemo" />

```vue showLineNumbers {15-19}
<script setup lang="ts">
import { RouterLink } from 'vue-router'
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
} from '@/lib/components/ui/breadcrumb'
</script>

<template>
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink as-child>
<RouterLink to="/">
Home
</RouterLink>
</BreadcrumbLink>
</BreadcrumbItem>
<!-- -->
</BreadcrumbList>
</Breadcrumb>
</template>
```

---

### Responsive

Here's an example of a responsive breadcrumb that composes `<BreadcrumbItem />` with `<BreadcrumbEllipsis />`, `<DropdownMenu />`, and `<Drawer />`.

It displays a dropdown on desktop and a drawer on mobile.

<ComponentPreview name="BreadcrumbResponsive" class="[&_.preview]:p-2" />
Loading