Files
WRNexusJS/packages/ui/components/Breadcrumb.wrn
T
ClintchizandClaude Opus 5.5 1bed8e4b74 feat(ui): component redesign pass and fixes, release ui 0.8.87
Redesign
- Shared field foundation rules (duplicated across 15 form components and
  emitted globally) are now identical everywhere, so later copies no longer
  silently override earlier redesigns.
- Drawn checkbox and radio controls, white switch thumbs (the thumb was the
  text colour, a black blob in light mode), recessed field wells, keycap
  actions in Modal, Drawer, Popover, Toaster, Pagination, Stepper, DataTable.
- Menus (Dropdown, ContextMenu, Popover, Select dropdown, Navbar panels) share
  one flat raised panel with neutral hover rows and mono section labels.
- Alerts with an accent rail and icon chip; card headers without the grey
  band; accordion, collapse, list, list group, timeline, stats, metrics,
  chat, skeleton, avatar group, clipboard, splitter, section, sidebar,
  navbar and upload progress restyled.
- Chart rebuilt: nice y-axis ticks, gridlines, rounded bars, line and area
  modes, hover tooltips, hidden series leave the scale.

Fixes
- Checkbox and radio labels toggle their input again (empty for="" broke it).
- TimePicker kept every clicked time pressed; only the chosen hour and
  minute are pressed now, and nothing is pressed before a value exists.
- ColorPicker hex readout now follows the picked colour.
- Disabled FileInput no longer shows the native file-input text.
- Date and time panels float instead of pushing content.
- ContextMenu opened at the pointer closes on scroll.
- Drawer left/top/bottom placements slide from their own edge again.
- Image-only Card no longer renders an empty body strip.
- Timeline supports item.time, current/upcoming states, sizes and
  compact/alternate variants; showcase demos use distinct variants.
- ChatBubble: delivery receipts (sending/sent/delivered/read/failed/error)
  and meta aligned to its bubble.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 16:18:13 +05:30

353 lines
11 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
max-width: 100%;
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.25rem 0.5rem;
background: var(--wrn-color-surface-soft, var(--breadcrumb-soft));
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
}
.wrn-breadcrumb[data-variant="outline"] {
width: fit-content;
max-width: 100%;
padding: 0.25rem 0.5rem;
background: var(--wrn-color-surface-raised);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
box-shadow: var(--wrn-shadow-1);
}
.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.25rem;
color: var(--wrn-color-border-strong, var(--wrn-color-text-subtle));
font-size: 0.75rem;
}
.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.375rem;
min-width: 0;
min-height: 1.75rem;
padding: 0.125rem 0.375rem;
border-radius: calc(var(--wrn-radius-sm) - 2px);
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: 500;
transition: color 120ms ease, background 120ms ease;
}
.wrn-breadcrumb__link:hover {
color: var(--wrn-color-text);
background: var(--wrn-color-surface-muted, 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: 600;
}
.wrn-breadcrumb__item[data-current="true"] .wrn-breadcrumb__current {
color: var(--wrn-color-text);
background: transparent;
}
.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(--wrn-color-text-muted);
}
.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;
}
}
}
}