feat(ui): add typed navbar menu items
This commit is contained in:
@@ -44,6 +44,60 @@ size: string = "default"
|
||||
const slug = String(source).replace(/^\/+|\/+$/g, "").replace(/[^A-Za-z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase()
|
||||
return slug ? translationPrefix + "." + slug + (field === "description" ? ".description" : "") : ""
|
||||
}
|
||||
shared function itemType(item) {
|
||||
if (item && item.type) {
|
||||
return item.type
|
||||
}
|
||||
return item && ((item.items && item.items.length) || (item.children && item.children.length)) ? "dropdown" : "link"
|
||||
}
|
||||
|
||||
shared function dropdownItems(item) {
|
||||
if (!item) {
|
||||
return []
|
||||
}
|
||||
if (Array.isArray(item.items)) {
|
||||
return item.items
|
||||
}
|
||||
return Array.isArray(item.children) ? item.children : []
|
||||
}
|
||||
|
||||
shared function dropdownGroupItems(item) {
|
||||
if (!item) {
|
||||
return []
|
||||
}
|
||||
if (Array.isArray(item.items)) {
|
||||
return item.items
|
||||
}
|
||||
return Array.isArray(item.children) ? item.children : []
|
||||
}
|
||||
|
||||
shared function megaData(item) {
|
||||
return item && item.mega && typeof item.mega === "object" ? item.mega : item || {}
|
||||
}
|
||||
|
||||
shared function megaColumns(item) {
|
||||
const data = megaData(item)
|
||||
if (Array.isArray(data.columns)) {
|
||||
return data.columns
|
||||
}
|
||||
const legacy = dropdownItems(item)
|
||||
if (!legacy.length) {
|
||||
return []
|
||||
}
|
||||
const grouped = legacy.some(function (entry) {
|
||||
return dropdownGroupItems(entry).length > 0
|
||||
})
|
||||
if (grouped) {
|
||||
return legacy.map(function (entry) {
|
||||
return {
|
||||
heading: entry.label || "",
|
||||
description: entry.description || "",
|
||||
items: dropdownGroupItems(entry)
|
||||
}
|
||||
})
|
||||
}
|
||||
return [{ heading: item.description || "", items: legacy }]
|
||||
}
|
||||
client function toggleNavigation() {
|
||||
mobileOpen = !mobileOpen
|
||||
output.toggle({ open: mobileOpen })
|
||||
@@ -61,10 +115,31 @@ size: string = "default"
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!item) {
|
||||
return false
|
||||
}
|
||||
if (item.value && item.value === active) {
|
||||
return true
|
||||
}
|
||||
if (itemType(item) === "mega") {
|
||||
const data = megaData(item)
|
||||
const links = Array.isArray(data.rail) ? data.rail.slice() : []
|
||||
megaColumns(item).forEach(function (column) {
|
||||
if (column && Array.isArray(column.items)) {
|
||||
column.items.forEach(function (child) {
|
||||
links.push(child)
|
||||
})
|
||||
}
|
||||
})
|
||||
return links.some(function (child) {
|
||||
return child.value && child.value === active
|
||||
})
|
||||
}
|
||||
const entries = dropdownItems(item)
|
||||
if (!entries.length) {
|
||||
return false
|
||||
}
|
||||
return entries.some(function (child) {
|
||||
return isItemActive(child)
|
||||
})
|
||||
}
|
||||
@@ -104,7 +179,27 @@ size: string = "default"
|
||||
<div class="wrn-navbar__collapse {mobileOpen ? 'is-open' : ''}">
|
||||
<nav class="wrn-navbar__menus" aria-label="{label}" data-wrn-roving="horizontal">
|
||||
{#each items as item}
|
||||
{#if item.children && item.children.length}
|
||||
{#if itemType(item) === "mega"}
|
||||
<MegaMenu
|
||||
class="wrn-navbar__mega"
|
||||
data-active="{isItemActive(item) ? 'true' : 'false'}"
|
||||
label="{item.label}"
|
||||
icon="{item.icon || ''}"
|
||||
columns="{megaColumns(item)}"
|
||||
rail="{megaData(item).rail || []}"
|
||||
featured="{megaData(item).featured || {}}"
|
||||
variant="{megaData(item).variant || 'default'}"
|
||||
density="{megaData(item).density || 'default'}"
|
||||
panelWidth="{megaData(item).panelWidth || 'xl'}"
|
||||
panelAlign="{megaData(item).panelAlign || 'start'}"
|
||||
columnCount="{megaData(item).columnCount || 0}"
|
||||
fullWidth="{megaData(item).fullWidth || false}"
|
||||
actionLabel="{megaData(item).actionLabel || ''}"
|
||||
actionHref="{megaData(item).actionHref || ''}"
|
||||
actionIcon="{megaData(item).actionIcon || 'icon-[lucide--arrow-right]'}"
|
||||
footer="{megaData(item).footer || ''}"
|
||||
/>
|
||||
{:else if itemType(item) === "dropdown"}
|
||||
<details class="wrn-navbar__dropdown wrn-navbar__dropdown--{item.type || 'dropdown'}" name="wrn-navbar-menu" @toggle="toggleDropdown(event, item)">
|
||||
<summary data-wrn-roving-item="true" aria-current="{isItemActive(item) ? 'page' : 'false'}">
|
||||
{#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if}
|
||||
@@ -113,12 +208,12 @@ size: string = "default"
|
||||
</summary>
|
||||
<div class="wrn-navbar__panel wrn-navbar__panel--columns-{item.columns || 1}">
|
||||
{#if item.description}<p class="wrn-navbar__panel-intro" data-t="{translationKey(item, 'description')}">{item.description}</p>{/if}
|
||||
{#each item.children as child}
|
||||
{#each dropdownItems(item) as child}
|
||||
<div class="wrn-navbar__group">
|
||||
{#if child.children && child.children.length}
|
||||
{#if dropdownGroupItems(child).length}
|
||||
{#if child.label}<strong class="wrn-navbar__group-title" data-t="{translationKey(child, 'label')}">{child.label}</strong>{/if}
|
||||
{#if child.description}<small data-t="{translationKey(child, 'description')}">{child.description}</small>{/if}
|
||||
{#each child.children as nested}
|
||||
{#each dropdownGroupItems(child) as nested}
|
||||
<a href="{nested.href || '#'}" target="{nested.target || ''}" rel="{nested.rel || ''}" aria-current="{nested.value === active ? 'page' : 'false'}" @click="selectItem(nested, 3)">
|
||||
{#if nested.icon}<span class="{nested.icon}" aria-hidden="true"></span>{/if}
|
||||
<span><strong data-t="{translationKey(nested, 'label')}">{nested.label}</strong>{#if nested.description}<small data-t="{translationKey(nested, 'description')}">{nested.description}</small>{/if}</span>
|
||||
@@ -309,6 +404,18 @@ size: string = "default"
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.wrn-navbar__mega {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.wrn-navbar__mega .wrn-mega__trigger {
|
||||
min-height: 2.75rem;
|
||||
padding: 0.65rem 0.8rem;
|
||||
border-radius: 0.65rem;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.wrn-navbar__menu-link,
|
||||
.wrn-navbar__dropdown > summary,
|
||||
.wrn-navbar__action {
|
||||
@@ -325,13 +432,15 @@ size: string = "default"
|
||||
}
|
||||
|
||||
.wrn-navbar__menu-link:hover,
|
||||
.wrn-navbar__dropdown > summary:hover {
|
||||
.wrn-navbar__dropdown > summary:hover,
|
||||
.wrn-navbar__mega .wrn-mega__trigger:hover {
|
||||
color: var(--wrn-color-text);
|
||||
background: var(--wrn-color-surface-2);
|
||||
}
|
||||
|
||||
.wrn-navbar__menu-link[aria-current="page"],
|
||||
.wrn-navbar__dropdown > summary[aria-current="page"] {
|
||||
.wrn-navbar__dropdown > summary[aria-current="page"],
|
||||
.wrn-navbar__mega[data-active="true"] .wrn-mega__trigger {
|
||||
color: var(--wrn-component-color, var(--wrn-color-primary));
|
||||
font-weight: 600;
|
||||
background: color-mix(
|
||||
@@ -343,7 +452,8 @@ size: string = "default"
|
||||
}
|
||||
|
||||
.wrn-navbar__menu-link[aria-current="page"]:hover,
|
||||
.wrn-navbar__dropdown > summary[aria-current="page"]:hover {
|
||||
.wrn-navbar__dropdown > summary[aria-current="page"]:hover,
|
||||
.wrn-navbar__mega[data-active="true"] .wrn-mega__trigger:hover {
|
||||
color: var(--wrn-component-color, var(--wrn-color-primary));
|
||||
background: color-mix(
|
||||
in srgb,
|
||||
@@ -587,6 +697,10 @@ size: string = "default"
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
}
|
||||
.wrn-navbar__mega,
|
||||
.wrn-navbar__mega .wrn-mega__trigger {
|
||||
width: 100%;
|
||||
}
|
||||
.wrn-navbar__menu-link,
|
||||
.wrn-navbar__dropdown > summary,
|
||||
.wrn-navbar__action {
|
||||
@@ -641,6 +755,7 @@ size: string = "default"
|
||||
flex-direction: column;
|
||||
}
|
||||
.wrn-navbar[data-collapse-at="1100px"] :is(.wrn-navbar__menu-link, .wrn-navbar__dropdown > summary, .wrn-navbar__action) { width: 100%; }
|
||||
.wrn-navbar[data-collapse-at="1100px"] :is(.wrn-navbar__mega, .wrn-navbar__mega .wrn-mega__trigger) { width: 100%; }
|
||||
.wrn-navbar[data-collapse-at="1100px"] .wrn-navbar__panel { position: static; width: 100%; max-width: none; transform: none; }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user