shoelace/src/components/breadcrumb/breadcrumb.ts

105 wiersze
3.7 KiB
TypeScript
Czysty Zwykły widok Historia

2023-01-13 20:43:55 +00:00
import '../icon/icon';
2021-09-01 13:06:19 +00:00
import { customElement, property, query } from 'lit/decorators.js';
2023-01-13 20:43:55 +00:00
import { html } from 'lit';
import { LocalizeController } from '../../utilities/localize';
2023-01-13 20:43:55 +00:00
import ShoelaceElement from '../../internal/shoelace-element';
2021-09-01 13:06:19 +00:00
import styles from './breadcrumb.styles';
import type { CSSResultGroup } from 'lit';
2023-01-13 20:43:55 +00:00
import type SlBreadcrumbItem from '../breadcrumb-item/breadcrumb-item';
2021-09-01 13:06:19 +00:00
/**
Enrich components `@summary` with description from docs (#962) * keep header styles with repositioned description text * `animated-image` move description to component * code style * `avatar` add summary from docs * `badge` add summary from docs * `breadcrumb` add summary from docs * `button` add summary from docs * lead sentence is now part of the header * `button-group` add summary from docs * `card` add summary from docs * `checkbox` add summary from docs * `color-picker` add summary from docs * `details` add summary from docs * `dialog` add summary from docs * `divider` add summary from docs * `drawer` add summary from docs * `dropdown` add summary from docs * `format-bytes` add summary from docs * `format-date` add summary from docs * `format-number` add summary from docs * `icon` add summary from docs * `icon-button` add summary from docs * `image-comparer` add summary from docs * `include` add summary from docs * `input` add summary from docs * `menu` add summary from docs * `menu-item` add summary from docs * `menu-label` add summary from docs * `popup` add summary from docs * `progressbar` add summary from docs * `progress-ring` add summary from docs * `radio` add summary from docs * `radio-button` add summary from docs * `range` add summary from docs * `rating` add summary from docs * `relative-time` add summary from docs * `select` add summary from docs * `skeleton` add summary from docs * `spinner` add summary from docs * `split-panel` add summary from docs * `switch` add summary from docs * `tab-group` add summary from docs * `tag` add summary from docs * `textarea` add summary from docs * `tooltip` add summary from docs * `visually-hidden` add summary from docs * `animation` add summary from docs * `breadcrumb-item` add summary from docs * `mutation-observer` add summary from docs * `radio-group` add summary from docs * `resize-observer` add summary from docs * `tab` add summary from docs * `tab-panel` add summary from docs * `tree` add summary from docs * `tree-item` add summary from docs * remove `title` for further usage of `Sl` classnames in docs * revert: use markdown parser for component summary
2022-10-21 13:56:35 +00:00
* @summary Breadcrumbs provide a group of links so users can easily navigate a website's hierarchy.
2023-01-12 15:26:25 +00:00
* @documentation https://shoelace.style/components/breadcrumb
* @status stable
2023-01-12 15:26:25 +00:00
* @since 2.0
2022-03-24 11:48:03 +00:00
*
2021-09-01 13:06:19 +00:00
* @slot - One or more breadcrumb items to display.
2022-12-06 16:18:14 +00:00
* @slot separator - The separator to use between breadcrumb items. Works best with `<sl-icon>`.
2021-09-01 13:06:19 +00:00
*
* @dependency sl-icon
*
2022-12-06 16:18:14 +00:00
* @csspart base - The component's base wrapper.
2021-09-01 13:06:19 +00:00
*/
@customElement('sl-breadcrumb')
2022-08-17 15:37:37 +00:00
export default class SlBreadcrumb extends ShoelaceElement {
static styles: CSSResultGroup = styles;
2021-09-01 13:06:19 +00:00
private readonly localize = new LocalizeController(this);
private separatorDir = this.localize.dir();
2023-01-03 20:04:07 +00:00
@query('slot') defaultSlot: HTMLSlotElement;
@query('slot[name="separator"]') separatorSlot: HTMLSlotElement;
2021-09-01 13:06:19 +00:00
/**
2022-12-06 16:18:14 +00:00
* The label to use for the breadcrumb control. This will not be shown on the screen, but it will be announced by
* screen readers and other assistive devices to provide more context for users.
2021-09-01 13:06:19 +00:00
*/
2022-12-06 16:18:14 +00:00
@property() label = '';
2021-09-01 13:06:19 +00:00
// Generates a clone of the separator element to use for each breadcrumb item
private getSeparator() {
const separator = this.separatorSlot.assignedElements({ flatten: true })[0] as HTMLElement;
// Clone it, remove ids, and slot it
const clone = separator.cloneNode(true) as HTMLElement;
[clone, ...clone.querySelectorAll('[id]')].forEach(el => el.removeAttribute('id'));
clone.setAttribute('data-default', '');
2021-09-01 13:06:19 +00:00
clone.slot = 'separator';
return clone;
}
2023-01-03 20:04:07 +00:00
private handleSlotChange() {
2021-09-01 13:06:19 +00:00
const items = [...this.defaultSlot.assignedElements({ flatten: true })].filter(
item => item.tagName.toLowerCase() === 'sl-breadcrumb-item'
) as SlBreadcrumbItem[];
items.forEach((item, index) => {
2021-09-01 13:06:19 +00:00
// Append separators to each item if they don't already have one
const separator = item.querySelector('[slot="separator"]');
if (separator === null) {
// No separator exists, add one
2021-09-01 13:06:19 +00:00
item.append(this.getSeparator());
} else if (separator.hasAttribute('data-default')) {
// A default separator exists, replace it
separator.replaceWith(this.getSeparator());
} else {
// The user provided a custom separator, leave it alone
2021-09-01 13:06:19 +00:00
}
// The last breadcrumb item is the "current page"
if (index === items.length - 1) {
item.setAttribute('aria-current', 'page');
} else {
item.removeAttribute('aria-current');
}
});
}
render() {
// We clone the separator and inject them into breadcrumb items, so we need to regenerate the default ones when
// directionality changes. We do this by storing the current separator direction, waiting for render, then calling
// the function that regenerates them.
if (this.separatorDir !== this.localize.dir()) {
this.separatorDir = this.localize.dir();
this.updateComplete.then(() => this.handleSlotChange());
}
2021-09-01 13:06:19 +00:00
return html`
<nav part="base" class="breadcrumb" aria-label=${this.label}>
<slot @slotchange=${this.handleSlotChange}></slot>
</nav>
<slot name="separator" hidden aria-hidden="true">
<sl-icon name=${this.localize.dir() === 'rtl' ? 'chevron-left' : 'chevron-right'} library="system"></sl-icon>
2021-09-01 13:06:19 +00:00
</slot>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
'sl-breadcrumb': SlBreadcrumb;
}
}