mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-29 03:10:42 +01:00
feat(Breadcrumb): new component (#506)
Co-authored-by: Eduard Aymerich <eduardaymerich@gmail.com> Co-authored-by: Benjamin Canac <canacb1@gmail.com>
This commit is contained in:
committed by
GitHub
parent
a97593985c
commit
a35bfc7343
17
docs/components/content/examples/BreadcrumbExampleBasic.vue
Normal file
17
docs/components/content/examples/BreadcrumbExampleBasic.vue
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<script setup>
|
||||||
|
const links = [{
|
||||||
|
label: 'Home',
|
||||||
|
icon: 'i-heroicons-home',
|
||||||
|
to: '/'
|
||||||
|
}, {
|
||||||
|
label: 'Navigation',
|
||||||
|
icon: 'i-heroicons-square-3-stack-3d'
|
||||||
|
}, {
|
||||||
|
label: 'Breadcrumb',
|
||||||
|
icon: 'i-heroicons-link'
|
||||||
|
}]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UBreadcrumb :links="links" />
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<script setup>
|
||||||
|
const links = [{
|
||||||
|
label: 'Home',
|
||||||
|
to: '/'
|
||||||
|
}, {
|
||||||
|
label: 'Navigation'
|
||||||
|
}, {
|
||||||
|
label: 'Breadcrumb'
|
||||||
|
}]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UBreadcrumb :links="links">
|
||||||
|
<template #default="{ link, isActive, index }">
|
||||||
|
<UBadge :color="isActive ? 'primary' : 'gray'" class="rounded-full">
|
||||||
|
{{ index + 1 }}. {{ link.label }}
|
||||||
|
</UBadge>
|
||||||
|
</template>
|
||||||
|
</UBreadcrumb>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<script setup>
|
||||||
|
const links = [{
|
||||||
|
label: 'Home',
|
||||||
|
icon: 'i-heroicons-home',
|
||||||
|
to: '/'
|
||||||
|
}, {
|
||||||
|
label: 'Navigation',
|
||||||
|
icon: 'i-heroicons-square-3-stack-3d'
|
||||||
|
}, {
|
||||||
|
label: 'Breadcrumb',
|
||||||
|
icon: 'i-heroicons-link'
|
||||||
|
}]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UBreadcrumb :links="links" :ui="{ ol: 'gap-x-3', li: 'gap-x-3' }">
|
||||||
|
<template #divider>
|
||||||
|
<span class="w-8 h-1 rounded-full bg-gray-300 dark:bg-gray-700" />
|
||||||
|
</template>
|
||||||
|
</UBreadcrumb>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<script setup>
|
||||||
|
const links = [{
|
||||||
|
label: 'Home',
|
||||||
|
to: '/'
|
||||||
|
}, {
|
||||||
|
label: 'Navigation'
|
||||||
|
}, {
|
||||||
|
label: 'Breadcrumb'
|
||||||
|
}]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UBreadcrumb :links="links" :divider="null" :ui="{ ol: 'gap-x-3' }">
|
||||||
|
<template #icon="{ link, index, isActive }">
|
||||||
|
<UAvatar
|
||||||
|
:alt="(index + 1 ).toString()"
|
||||||
|
:ui="{
|
||||||
|
background: isActive ? 'bg-primary-500 dark:bg-primary-400' : undefined,
|
||||||
|
placeholder: isActive ? 'text-white dark:text-gray-900' : !!link.to ? 'group-hover:text-gray-700 dark:group-hover:text-gray-200' : ''
|
||||||
|
}"
|
||||||
|
size="xs"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</UBreadcrumb>
|
||||||
|
</template>
|
||||||
@@ -369,13 +369,29 @@ export default defineAppConfig({
|
|||||||
},
|
},
|
||||||
pagination: {
|
pagination: {
|
||||||
default: {
|
default: {
|
||||||
|
firstButton: {
|
||||||
|
icon: 'i-octicon-chevron-left-24'
|
||||||
|
},
|
||||||
prevButton: {
|
prevButton: {
|
||||||
icon: 'i-octicon-arrow-left-24'
|
icon: 'i-octicon-arrow-left-24'
|
||||||
},
|
},
|
||||||
nextButton: {
|
nextButton: {
|
||||||
icon: 'i-octicon-arrow-right-24'
|
icon: 'i-octicon-arrow-right-24'
|
||||||
|
},
|
||||||
|
lastButton: {
|
||||||
|
icon: 'i-octicon-chevron-right-24'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
accordion: {
|
||||||
|
default: {
|
||||||
|
openIcon: 'i-octicon-chevron-down-24'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
breadcrumb: {
|
||||||
|
default: {
|
||||||
|
divider: 'i-octicon-chevron-right-24'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ Pass an array to the `links` prop of the VerticalNavigation component. Each link
|
|||||||
|
|
||||||
- `label` - The label of the link.
|
- `label` - The label of the link.
|
||||||
- `icon` - The icon of the link.
|
- `icon` - The icon of the link.
|
||||||
- `iconClass` - The class of the icon of the link.
|
- `iconClass` - The class of the icon link.
|
||||||
- `avatar` - The avatar of the link. You can pass all the props of the [Avatar](/elements/avatar) component.
|
- `avatar` - The avatar of the link. You can pass all the props of the [Avatar](/elements/avatar) component.
|
||||||
- `badge` - A badge to display next to the label.
|
- `badge` - A badge to display next to the label.
|
||||||
- `click` - The click handler of the link.
|
- `click` - The click handler of the link.
|
||||||
|
|||||||
69
docs/content/5.navigation/5.breadcrumb.md
Normal file
69
docs/content/5.navigation/5.breadcrumb.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
---
|
||||||
|
title: Breadcrumb
|
||||||
|
description: A list of links that indicate the current page's location within a navigational hierarchy.
|
||||||
|
navigation:
|
||||||
|
badge: New
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Pass an array to the `links` prop of the Breadcrumb component. Each link can have the following properties:
|
||||||
|
|
||||||
|
- `label` - The label of the link.
|
||||||
|
- `icon` - The icon of the link.
|
||||||
|
- `iconClass` - The class of the icon link.
|
||||||
|
|
||||||
|
You can also pass any property from the [NuxtLink](https://nuxt.com/docs/api/components/nuxt-link#props) component such as `to`, `exact`, etc.
|
||||||
|
|
||||||
|
:component-example{component="breadcrumb-example-basic"}
|
||||||
|
|
||||||
|
::callout{icon="i-heroicons-light-bulb"}
|
||||||
|
A `span` will be rendered instead of a link when the `to` property is not defined.
|
||||||
|
::
|
||||||
|
|
||||||
|
## Divider
|
||||||
|
|
||||||
|
Use the `divider` prop to customize the divider between each link, it can be **an icon or a string**. You can change it globally in `ui.breadcrumb.default.divider`. Defaults to `i-heroicons-chevron-right-20-solid`.
|
||||||
|
|
||||||
|
You can set the prop to `null` to hide the divider. Additionally, you can customize it using the [`divider`](#divider-1) slot.
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
baseProps:
|
||||||
|
links:
|
||||||
|
- label: Home
|
||||||
|
to: /
|
||||||
|
- label: Navigation
|
||||||
|
- label: Breadcrumb
|
||||||
|
props:
|
||||||
|
divider: '/'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
## Slots
|
||||||
|
|
||||||
|
### `default`
|
||||||
|
|
||||||
|
Use the `#default` slot to customize the link label. You will have access to the `link`, `index` and `isActive` properties in the slot scope.
|
||||||
|
|
||||||
|
:component-example{component="breadcrumb-example-default-slot"}
|
||||||
|
|
||||||
|
### `icon`
|
||||||
|
|
||||||
|
Use the `#icon` slot to customize the link icon. You will have access to the `link`, `index` and `isActive` properties in the slot scope.
|
||||||
|
|
||||||
|
:component-example{component="breadcrumb-example-icon-slot"}
|
||||||
|
|
||||||
|
### `divider`
|
||||||
|
|
||||||
|
Use the `divider` slot to customize the divider of the Breadcrumb.
|
||||||
|
|
||||||
|
:component-example{component="breadcrumb-example-divider-slot"}
|
||||||
|
|
||||||
|
## Props
|
||||||
|
|
||||||
|
:component-props
|
||||||
|
|
||||||
|
## Config
|
||||||
|
|
||||||
|
:component-preset
|
||||||
84
src/runtime/components/navigation/Breadcrumb.vue
Normal file
84
src/runtime/components/navigation/Breadcrumb.vue
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<template>
|
||||||
|
<nav aria-label="Breadcrumb" :class="ui.wrapper" v-bind="attrs">
|
||||||
|
<ol :class="ui.ol">
|
||||||
|
<li v-for="(link, index) in links" :key="index" :class="ui.li">
|
||||||
|
<ULink
|
||||||
|
as="span"
|
||||||
|
:class="[ui.base, index === links.length - 1 ? ui.active : !!link.to ? ui.inactive : '']"
|
||||||
|
v-bind="omit(link, ['label', 'icon', 'iconClass'])"
|
||||||
|
:aria-current="index === links.length - 1 ? 'page' : undefined"
|
||||||
|
>
|
||||||
|
<slot name="icon" :link="link" :index="index" :is-active="index === links.length - 1">
|
||||||
|
<UIcon
|
||||||
|
v-if="link.icon"
|
||||||
|
:name="link.icon"
|
||||||
|
:class="[ui.icon.base, index === links.length - 1 ? ui.icon.active : !!link.to ? ui.icon.inactive : '', link.iconClass]"
|
||||||
|
/>
|
||||||
|
</slot>
|
||||||
|
|
||||||
|
<slot :link="link" :index="index" :is-active="index === links.length - 1">
|
||||||
|
{{ link.label }}
|
||||||
|
</slot>
|
||||||
|
</ULink>
|
||||||
|
|
||||||
|
<slot v-if="index < links.length - 1" name="divider">
|
||||||
|
<template v-if="divider">
|
||||||
|
<UIcon v-if="divider.startsWith('i-')" :name="divider" :class="ui.divider.base" role="presentation" />
|
||||||
|
<span v-else role="presentation">{{ divider }}</span>
|
||||||
|
</template>
|
||||||
|
</slot>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, toRef } from 'vue'
|
||||||
|
import type { PropType } from 'vue'
|
||||||
|
import UIcon from '../elements/Icon.vue'
|
||||||
|
import ULink from '../elements/Link.vue'
|
||||||
|
import { useUI } from '../../composables/useUI'
|
||||||
|
import { mergeConfig, omit } from '../../utils'
|
||||||
|
import type { BreadcrumbLink, Strategy } from '../../types'
|
||||||
|
// @ts-expect-error
|
||||||
|
import appConfig from '#build/app.config'
|
||||||
|
import { breadcrumb } from '#ui/ui.config'
|
||||||
|
|
||||||
|
const config = mergeConfig<typeof breadcrumb>(appConfig.ui.strategy, appConfig.ui.breadcrumb, breadcrumb)
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
UIcon,
|
||||||
|
ULink
|
||||||
|
},
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: {
|
||||||
|
links: {
|
||||||
|
type: Array as PropType<BreadcrumbLink[]>,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
divider: {
|
||||||
|
type: String,
|
||||||
|
default: () => config.default.divider
|
||||||
|
},
|
||||||
|
class: {
|
||||||
|
type: [String, Object, Array] as PropType<any>,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
|
ui: {
|
||||||
|
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
||||||
|
default: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup (props) {
|
||||||
|
const { ui, attrs } = useUI('breadcrumb', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
|
|
||||||
|
return {
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
ui,
|
||||||
|
attrs,
|
||||||
|
omit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
7
src/runtime/types/breadcrumb.d.ts
vendored
Normal file
7
src/runtime/types/breadcrumb.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import type { Link } from './link'
|
||||||
|
|
||||||
|
export interface BreadcrumbLink extends Link {
|
||||||
|
label: string
|
||||||
|
icon?: string
|
||||||
|
iconClass?: string
|
||||||
|
}
|
||||||
1
src/runtime/types/index.d.ts
vendored
1
src/runtime/types/index.d.ts
vendored
@@ -1,6 +1,7 @@
|
|||||||
export * from './accordion'
|
export * from './accordion'
|
||||||
export * from './avatar'
|
export * from './avatar'
|
||||||
export * from './badge'
|
export * from './badge'
|
||||||
|
export * from './breadcrumb'
|
||||||
export * from './button'
|
export * from './button'
|
||||||
export * from './clipboard'
|
export * from './clipboard'
|
||||||
export * from './command-palette'
|
export * from './command-palette'
|
||||||
|
|||||||
@@ -1124,7 +1124,7 @@ export const pagination = {
|
|||||||
nextButton: {
|
nextButton: {
|
||||||
color: 'white',
|
color: 'white',
|
||||||
class: 'rtl:[&_span:last-child]:rotate-180',
|
class: 'rtl:[&_span:last-child]:rotate-180',
|
||||||
icon: 'i-heroicons-chevron-right-20-solid '
|
icon: 'i-heroicons-chevron-right-20-solid'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1163,6 +1163,26 @@ export const tabs = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const breadcrumb = {
|
||||||
|
wrapper: 'relative',
|
||||||
|
ol: 'flex items-center gap-x-1.5',
|
||||||
|
li: 'flex items-center gap-x-1.5 text-gray-500 dark:text-gray-400 text-sm',
|
||||||
|
base: 'flex items-center gap-x-1.5 group font-semibold',
|
||||||
|
icon: {
|
||||||
|
base: 'flex-shrink-0 w-4 h-4',
|
||||||
|
active: '',
|
||||||
|
inactive: ''
|
||||||
|
},
|
||||||
|
divider: {
|
||||||
|
base: 'flex-shrink-0 w-5 h-5'
|
||||||
|
},
|
||||||
|
active: 'text-primary-500 dark:text-primary-400',
|
||||||
|
inactive: ' hover:text-gray-700 dark:hover:text-gray-200',
|
||||||
|
default: {
|
||||||
|
divider: 'i-heroicons-chevron-right-20-solid'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Overlays
|
// Overlays
|
||||||
|
|
||||||
export const modal = {
|
export const modal = {
|
||||||
|
|||||||
Reference in New Issue
Block a user