144 lines
6.7 KiB
Plaintext
144 lines
6.7 KiB
Plaintext
component Navbar {
|
|
outputs {
|
|
toggle(payload: { open: boolean })
|
|
open(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
|
close(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
|
select(payload: { item: string | number | boolean | null | object; value: string | number | boolean; level: string })
|
|
action(payload: { item: string | number | boolean | null | object; value: string | number | boolean })
|
|
}
|
|
|
|
props {
|
|
size: string = "default"
|
|
color: string = "primary"
|
|
label: string = "Primary navigation"
|
|
topbarLabel: string = "Utility navigation"
|
|
brand: Record<string, unknown> = {}
|
|
items: unknown[] = []
|
|
actions: unknown[] = []
|
|
active: string = ""
|
|
sticky: boolean = false
|
|
openOnHover: boolean = false
|
|
maxWidth: string = "full"
|
|
mobileLabel: string = "Toggle navigation"
|
|
class: string = ""
|
|
}
|
|
|
|
state mobileOpen: boolean = false
|
|
|
|
functions {
|
|
client function toggleNavigation() {
|
|
mobileOpen = !mobileOpen
|
|
output.toggle({ open: mobileOpen })
|
|
output[mobileOpen ? "open" : "close"]({ source: "mobile" })
|
|
}
|
|
|
|
client function selectItem(item, level) {
|
|
mobileOpen = false
|
|
output.select({ item: item, value: item.value || "", level: level })
|
|
}
|
|
|
|
client function selectAction(item) {
|
|
mobileOpen = false
|
|
output.action({ item: item, value: item.value || "" })
|
|
}
|
|
|
|
shared function isItemActive(item) {
|
|
if (!item) return false
|
|
if (item.value && item.value === active) return true
|
|
if (!item.children || !item.children.length) return false
|
|
return item.children.some(function (child) {
|
|
return isItemActive(child)
|
|
})
|
|
}
|
|
|
|
client function toggleDropdown(event, item) {
|
|
output[event.currentTarget.open ? "open" : "close"]({
|
|
source: "menu",
|
|
item: item
|
|
})
|
|
}
|
|
}
|
|
|
|
view {
|
|
<header class="wire-navbar wire-navbar--width-{maxWidth} wire-next--color-{color} wire-next--size-{size} {sticky ? 'wire-navbar--sticky' : ''} {class}" data-wrn-navbar data-open-on-hover="{openOnHover}">
|
|
<div class="wire-navbar__topbar" aria-label="{topbarLabel}">
|
|
<slot name="topbar" />
|
|
</div>
|
|
|
|
<div class="wire-navbar__main">
|
|
<a class="wire-navbar__brand" href="{brand.href || '/'}" aria-label="{brand.ariaLabel || brand.label || label}">
|
|
{#if brand.logo}
|
|
<img class="wire-navbar__brand-logo" src="{brand.logo}" alt="{brand.logoAlt || brand.label || ''}" width="{brand.logoWidth || ''}" height="{brand.logoHeight || ''}" />
|
|
{/if}
|
|
{#if brand.icon}
|
|
<span class="wire-navbar__brand-icon {brand.icon}" aria-hidden="true"></span>
|
|
{/if}
|
|
{#if brand.label || brand.description}
|
|
<span class="wire-navbar__brand-copy">
|
|
{#if brand.label}<strong>{brand.label}</strong>{/if}
|
|
{#if brand.description}<small>{brand.description}</small>{/if}
|
|
</span>
|
|
{/if}
|
|
</a>
|
|
|
|
<button type="button" class="wire-navbar__toggle" aria-label="{mobileLabel}" aria-expanded="{mobileOpen}" @click="toggleNavigation()">
|
|
<span aria-hidden="true"></span><span aria-hidden="true"></span><span aria-hidden="true"></span>
|
|
</button>
|
|
|
|
<div class="wire-navbar__collapse {mobileOpen ? 'is-open' : ''}">
|
|
<nav class="wire-navbar__menus" aria-label="{label}">
|
|
{#each items as item}
|
|
{#if item.children && item.children.length}
|
|
<details class="wire-navbar__dropdown wire-navbar__dropdown--{item.type || 'dropdown'}" name="wire-navbar-menu" @toggle="toggleDropdown(event, item)">
|
|
<summary aria-current="{isItemActive(item) ? 'page' : ''}">
|
|
{#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if}
|
|
<span>{item.label}</span>
|
|
<span class="wire-navbar__chevron" aria-hidden="true"></span>
|
|
</summary>
|
|
<div class="wire-navbar__panel wire-navbar__panel--columns-{item.columns || 1}">
|
|
{#if item.description}<p class="wire-navbar__panel-intro">{item.description}</p>{/if}
|
|
{#each item.children as child}
|
|
<div class="wire-navbar__group">
|
|
{#if child.children && child.children.length}
|
|
{#if child.label}<strong class="wire-navbar__group-title">{child.label}</strong>{/if}
|
|
{#if child.description}<small>{child.description}</small>{/if}
|
|
{#each child.children as nested}
|
|
<a href="{nested.href || '#'}" target="{nested.target || ''}" rel="{nested.rel || ''}" aria-current="{nested.value === active ? 'page' : ''}" @click="selectItem(nested, 3)">
|
|
{#if nested.icon}<span class="{nested.icon}" aria-hidden="true"></span>{/if}
|
|
<span><strong>{nested.label}</strong>{#if nested.description}<small>{nested.description}</small>{/if}</span>
|
|
</a>
|
|
{/each}
|
|
{:else}
|
|
<a href="{child.href || '#'}" target="{child.target || ''}" rel="{child.rel || ''}" aria-current="{child.value === active ? 'page' : ''}" @click="selectItem(child, 2)">
|
|
{#if child.icon}<span class="{child.icon}" aria-hidden="true"></span>{/if}
|
|
<span><strong>{child.label}</strong>{#if child.description}<small>{child.description}</small>{/if}</span>
|
|
</a>
|
|
{/if}
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
</details>
|
|
{:else}
|
|
<a class="wire-navbar__menu-link" href="{item.href || '#'}" target="{item.target || ''}" rel="{item.rel || ''}" aria-current="{item.value === active ? 'page' : ''}" @click="selectItem(item, 1)">
|
|
{#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if}
|
|
<span>{item.label}</span>
|
|
</a>
|
|
{/if}
|
|
{/each}
|
|
</nav>
|
|
|
|
<div class="wire-navbar__actions">
|
|
{#each actions as item}
|
|
<a class="wire-navbar__action wire-navbar__action--{item.variant || 'link'}" href="{item.href || '#'}" target="{item.target || ''}" rel="{item.rel || ''}" @click="selectAction(item)">
|
|
{#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if}
|
|
<span>{item.label}</span>
|
|
</a>
|
|
{/each}
|
|
<slot name="actions" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
}
|
|
}
|