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:
Haytham A. Salama
2023-11-16 19:06:51 +02:00
committed by GitHub
parent a97593985c
commit a35bfc7343
11 changed files with 282 additions and 2 deletions

View 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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -369,13 +369,29 @@ export default defineAppConfig({
},
pagination: {
default: {
firstButton: {
icon: 'i-octicon-chevron-left-24'
},
prevButton: {
icon: 'i-octicon-arrow-left-24'
},
nextButton: {
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'
}
}
}
})

View File

@@ -13,7 +13,7 @@ Pass an array to the `links` prop of the VerticalNavigation component. Each link
- `label` - The label 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.
- `badge` - A badge to display next to the label.
- `click` - The click handler of the link.

View 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

View 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
View File

@@ -0,0 +1,7 @@
import type { Link } from './link'
export interface BreadcrumbLink extends Link {
label: string
icon?: string
iconClass?: string
}

View File

@@ -1,6 +1,7 @@
export * from './accordion'
export * from './avatar'
export * from './badge'
export * from './breadcrumb'
export * from './button'
export * from './clipboard'
export * from './command-palette'

View File

@@ -1124,7 +1124,7 @@ export const pagination = {
nextButton: {
color: 'white',
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
export const modal = {