355 lines
11 KiB
Plaintext
355 lines
11 KiB
Plaintext
component Breadcrumb {
|
||
outputs {
|
||
select(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
|
||
}
|
||
|
||
props {
|
||
label: string = "Breadcrumb"
|
||
items: unknown[] = []
|
||
active: string = ""
|
||
activeIcon: string = "icon-[lucide--file-text]"
|
||
separator: string = "chevron"
|
||
showHome: boolean = false
|
||
homeLabel: string = "Home"
|
||
homeHref: string = "/"
|
||
homeIcon: string = "icon-[lucide--house]"
|
||
size: string = "default"
|
||
color: string = "primary"
|
||
variant: string = "minimal"
|
||
class: string = ""
|
||
}
|
||
|
||
view {
|
||
<nav
|
||
{...attrs}
|
||
data-ui-component="Breadcrumb"
|
||
data-size='{size}'
|
||
data-color='{color}'
|
||
data-variant='{variant}'
|
||
aria-label='{label}'
|
||
class='wrn-breadcrumb {class}'
|
||
>
|
||
<ol class="wrn-breadcrumb__list">
|
||
{#if showHome}
|
||
<li class="wrn-breadcrumb__item wrn-breadcrumb__item--home">
|
||
<a
|
||
href='{homeHref || "/"}'
|
||
class="wrn-breadcrumb__link wrn-breadcrumb__home"
|
||
@click='output.select({ item: { label: homeLabel, href: homeHref || "/", value: "home" }, itemIndex: -1 })'
|
||
>
|
||
{#if homeIcon === "icon-[lucide--house]"}
|
||
<span class="icon-[lucide--house] wrn-breadcrumb__icon" aria-hidden="true"></span>
|
||
{:else if homeIcon}
|
||
<span class='{homeIcon} wrn-breadcrumb__icon' aria-hidden="true"></span>
|
||
{/if}
|
||
|
||
<span class="wrn-breadcrumb__home-label">{homeLabel}</span>
|
||
</a>
|
||
</li>
|
||
{/if}
|
||
|
||
{#each items as item, itemIndex}
|
||
<li
|
||
class="wrn-breadcrumb__item"
|
||
data-current='{item.active || item.current || (active && active === (item.value || item.label || item.title)) || (!active && itemIndex === items.length - 1) ? "true" : "false"}'
|
||
>
|
||
{#if showHome || itemIndex > 0}
|
||
<span class="wrn-breadcrumb__separator" aria-hidden="true">
|
||
{#if separator === "slash"}
|
||
<span>/</span>
|
||
{:else if separator === "dot"}
|
||
<span>•</span>
|
||
{:else if separator === "arrow"}
|
||
<span>→</span>
|
||
{:else}
|
||
<span class="wrn-breadcrumb__separator-icon">›</span>
|
||
{/if}
|
||
</span>
|
||
{/if}
|
||
|
||
{#if item.href && !item.disabled && !(item.active || item.current || (active && active === (item.value || item.label || item.title)) || (!active && itemIndex === items.length - 1))}
|
||
<a
|
||
href='{item.href}'
|
||
target='{item.target || ""}'
|
||
rel='{item.external ? "noopener noreferrer" : (item.rel || "")}'
|
||
class="wrn-breadcrumb__link"
|
||
@click='output.select({ item: item, itemIndex: itemIndex })'
|
||
>
|
||
{#if item.icon}
|
||
<span class='{item.icon} wrn-breadcrumb__icon' aria-hidden="true"></span>
|
||
{/if}
|
||
|
||
<span class="wrn-breadcrumb__label">{item.label || item.title}</span>
|
||
|
||
{#if item.external}
|
||
<span class="icon-[lucide--arrow-up-right] wrn-breadcrumb__external" aria-hidden="true"></span>
|
||
{/if}
|
||
</a>
|
||
{:else}
|
||
<span
|
||
class="wrn-breadcrumb__current"
|
||
aria-current='{item.active || item.current || (active && active === (item.value || item.label || item.title)) || (!active && itemIndex === items.length - 1) ? "page" : "false"}'
|
||
aria-disabled='{item.disabled ? "true" : "false"}'
|
||
>
|
||
{#if item.icon}
|
||
<span class='{item.icon} wrn-breadcrumb__icon' aria-hidden="true"></span>
|
||
{/if}
|
||
|
||
<span class="wrn-breadcrumb__label">{item.label || item.title}</span>
|
||
</span>
|
||
{/if}
|
||
</li>
|
||
{/each}
|
||
|
||
{#if active && !items.some((item) => active === (item.value || item.label || item.title))}
|
||
<li class="wrn-breadcrumb__item" data-current="true">
|
||
{#if showHome || items.length > 0}
|
||
<span class="wrn-breadcrumb__separator" aria-hidden="true">
|
||
{#if separator === "slash"}
|
||
<span>/</span>
|
||
{:else if separator === "dot"}
|
||
<span>•</span>
|
||
{:else if separator === "arrow"}
|
||
<span>→</span>
|
||
{:else}
|
||
<span class="wrn-breadcrumb__separator-icon">›</span>
|
||
{/if}
|
||
</span>
|
||
{/if}
|
||
<span class="wrn-breadcrumb__current" aria-current="page">
|
||
{#if activeIcon}
|
||
<span class='{activeIcon} wrn-breadcrumb__icon' aria-hidden="true"></span>
|
||
{/if}
|
||
<span class="wrn-breadcrumb__label">{active}</span>
|
||
</span>
|
||
</li>
|
||
{/if}
|
||
</ol>
|
||
</nav>
|
||
}
|
||
|
||
style {
|
||
.wrn-breadcrumb {
|
||
--breadcrumb-accent: var(--wrn-color-primary);
|
||
--breadcrumb-soft: var(--wrn-color-primary-soft);
|
||
--breadcrumb-muted: var(--wrn-color-primary-muted);
|
||
--breadcrumb-text: var(--wrn-color-primary-text);
|
||
|
||
min-width: 0;
|
||
color: var(--wrn-color-text-muted);
|
||
}
|
||
|
||
.wrn-breadcrumb[data-color="secondary"] {
|
||
--breadcrumb-accent: var(--wrn-color-secondary);
|
||
--breadcrumb-soft: var(--wrn-color-secondary-soft);
|
||
--breadcrumb-muted: var(--wrn-color-secondary-muted);
|
||
--breadcrumb-text: var(--wrn-color-secondary-text);
|
||
}
|
||
|
||
.wrn-breadcrumb[data-color="info"] {
|
||
--breadcrumb-accent: var(--wrn-color-info);
|
||
--breadcrumb-soft: var(--wrn-color-info-soft);
|
||
--breadcrumb-muted: var(--wrn-color-info-muted);
|
||
--breadcrumb-text: var(--wrn-color-info-text);
|
||
}
|
||
|
||
.wrn-breadcrumb[data-color="success"] {
|
||
--breadcrumb-accent: var(--wrn-color-success);
|
||
--breadcrumb-soft: var(--wrn-color-success-soft);
|
||
--breadcrumb-muted: var(--wrn-color-success-muted);
|
||
--breadcrumb-text: var(--wrn-color-success-text);
|
||
}
|
||
|
||
.wrn-breadcrumb[data-color="warning"] {
|
||
--breadcrumb-accent: var(--wrn-color-warning);
|
||
--breadcrumb-soft: var(--wrn-color-warning-soft);
|
||
--breadcrumb-muted: var(--wrn-color-warning-muted);
|
||
--breadcrumb-text: var(--wrn-color-warning-text);
|
||
}
|
||
|
||
.wrn-breadcrumb[data-color="danger"] {
|
||
--breadcrumb-accent: var(--wrn-color-danger);
|
||
--breadcrumb-soft: var(--wrn-color-danger-soft);
|
||
--breadcrumb-muted: var(--wrn-color-danger-muted);
|
||
--breadcrumb-text: var(--wrn-color-danger-text);
|
||
}
|
||
|
||
.wrn-breadcrumb[data-variant="soft"] {
|
||
width: fit-content;
|
||
max-width: 100%;
|
||
padding: 0.45rem 0.65rem;
|
||
background: var(--breadcrumb-soft);
|
||
border: 1px solid var(--breadcrumb-muted);
|
||
border-radius: 0.75rem;
|
||
}
|
||
|
||
.wrn-breadcrumb[data-variant="outline"] {
|
||
width: fit-content;
|
||
max-width: 100%;
|
||
padding: 0.45rem 0.65rem;
|
||
background: var(--wrn-color-surface-raised);
|
||
border: 1px solid var(--wrn-color-border);
|
||
border-radius: 0.75rem;
|
||
box-shadow: var(--wrn-shadow-sm);
|
||
}
|
||
|
||
.wrn-breadcrumb[data-variant="contrast"] {
|
||
color: rgba(255, 255, 255, 0.78);
|
||
}
|
||
|
||
.wrn-breadcrumb__list {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0;
|
||
min-width: 0;
|
||
margin: 0;
|
||
padding: 0;
|
||
overflow-x: auto;
|
||
list-style: none;
|
||
scrollbar-width: none;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.wrn-breadcrumb__list::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
|
||
.wrn-breadcrumb__item {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
min-width: 0;
|
||
flex: 0 0 auto;
|
||
}
|
||
|
||
.wrn-breadcrumb__separator {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-inline: 0.45rem;
|
||
color: var(--wrn-color-text-subtle);
|
||
font-size: 0.75rem;
|
||
opacity: 0.72;
|
||
}
|
||
|
||
.wrn-breadcrumb__separator-icon {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 0.9rem;
|
||
height: 0.9rem;
|
||
font-size: 1rem;
|
||
font-weight: 700;
|
||
line-height: 1;
|
||
}
|
||
|
||
.wrn-breadcrumb__link,
|
||
.wrn-breadcrumb__current {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.4rem;
|
||
min-width: 0;
|
||
min-height: 2rem;
|
||
padding: 0.25rem 0.4rem;
|
||
border-radius: 0.55rem;
|
||
font-size: 0.8125rem;
|
||
line-height: 1.25rem;
|
||
text-decoration: none;
|
||
}
|
||
|
||
.wrn-breadcrumb[data-size="sm"] .wrn-breadcrumb__link,
|
||
.wrn-breadcrumb[data-size="sm"] .wrn-breadcrumb__current {
|
||
min-height: 1.75rem;
|
||
font-size: 0.75rem;
|
||
}
|
||
|
||
.wrn-breadcrumb[data-size="lg"] .wrn-breadcrumb__link,
|
||
.wrn-breadcrumb[data-size="lg"] .wrn-breadcrumb__current {
|
||
min-height: 2.25rem;
|
||
font-size: 0.875rem;
|
||
}
|
||
|
||
.wrn-breadcrumb__link {
|
||
color: var(--wrn-color-text-muted);
|
||
font-weight: 600;
|
||
transition:
|
||
color 160ms ease,
|
||
background 160ms ease;
|
||
}
|
||
|
||
.wrn-breadcrumb__link:hover {
|
||
color: var(--breadcrumb-accent);
|
||
background: var(--breadcrumb-soft);
|
||
}
|
||
|
||
.wrn-breadcrumb__link:focus-visible {
|
||
color: var(--breadcrumb-accent);
|
||
outline: 2px solid var(--breadcrumb-accent);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
.wrn-breadcrumb__current {
|
||
max-width: 22rem;
|
||
color: var(--wrn-color-text);
|
||
font-weight: 750;
|
||
}
|
||
|
||
.wrn-breadcrumb__item[data-current="true"] .wrn-breadcrumb__current {
|
||
color: var(--breadcrumb-text);
|
||
background: var(--breadcrumb-soft);
|
||
}
|
||
|
||
.wrn-breadcrumb__current[aria-disabled="true"] {
|
||
opacity: 0.58;
|
||
}
|
||
|
||
.wrn-breadcrumb__icon,
|
||
.wrn-breadcrumb__external {
|
||
flex: 0 0 auto;
|
||
width: 0.95rem;
|
||
height: 0.95rem;
|
||
}
|
||
|
||
.wrn-breadcrumb__home {
|
||
color: var(--breadcrumb-accent);
|
||
}
|
||
|
||
.wrn-breadcrumb__label {
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.wrn-breadcrumb[data-variant="contrast"] .wrn-breadcrumb__link,
|
||
.wrn-breadcrumb[data-variant="contrast"] .wrn-breadcrumb__current,
|
||
.wrn-breadcrumb[data-variant="contrast"] .wrn-breadcrumb__home {
|
||
color: inherit;
|
||
}
|
||
|
||
.wrn-breadcrumb[data-variant="contrast"] .wrn-breadcrumb__link:hover,
|
||
.wrn-breadcrumb[data-variant="contrast"] .wrn-breadcrumb__item[data-current="true"] .wrn-breadcrumb__current {
|
||
color: #ffffff;
|
||
background: rgba(255, 255, 255, 0.12);
|
||
}
|
||
|
||
.wrn-breadcrumb[data-variant="contrast"] .wrn-breadcrumb__separator {
|
||
color: rgba(255, 255, 255, 0.62);
|
||
}
|
||
|
||
@media (max-width: 639px) {
|
||
.wrn-breadcrumb__home-label {
|
||
display: none;
|
||
}
|
||
|
||
.wrn-breadcrumb__current {
|
||
max-width: 13rem;
|
||
}
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.wrn-breadcrumb__link {
|
||
transition: none;
|
||
}
|
||
}
|
||
}
|
||
}
|