fix(runtime): stabilize navigation and custom errors
Quality / quality (ubuntu-latest) (push) Failing after 23s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
2026-08-15 10:44:06 +05:30
parent f0447fddb0
commit f88dd47408
14 changed files with 246 additions and 36 deletions
+12 -8
View File
@@ -10,14 +10,18 @@ component Card {
props {
title: string = "Card title"
titleKey: string = ""
subtitle: string = ""
subtitleKey: string = ""
description: string = ""
descriptionKey: string = ""
header: string = ""
footer: string = ""
imageSrc: string = ""
imageAlt: string = ""
imagePosition: string = "top"
actionLabel: string = ""
actionLabelKey: string = ""
actionHref: string = ""
headerActions: unknown[] = []
navigation: unknown[] = []
@@ -105,13 +109,13 @@ component Card {
<div class="wrn-next__card-content">
{#if item.title || item.label}
<h3>{item.title || item.label}</h3>
<h3 data-t='{item.titleKey || item.labelKey || ""}'>{item.title || item.label}</h3>
{/if}
{#if item.subtitle}
<p class="wrn-next__card-subtitle">{item.subtitle}</p>
<p class="wrn-next__card-subtitle" data-t='{item.subtitleKey || ""}'>{item.subtitle}</p>
{/if}
{#if item.description}
<p class="wrn-next__card-description">{item.description}</p>
<p class="wrn-next__card-description" data-t='{item.descriptionKey || ""}'>{item.description}</p>
{/if}
{#if item.actionLabel || item.href}
<a
@@ -119,7 +123,7 @@ component Card {
class="wrn-next__card-action"
@click='output.action({ item: item, index: itemIndex })'
>
<span>{item.actionLabel || "Learn more"}</span>
<span data-t='{item.actionLabelKey || ""}'>{item.actionLabel || "Learn more"}</span>
<span class="icon-[lucide--arrow-right]" aria-hidden="true"></span>
</a>
{/if}
@@ -168,10 +172,10 @@ component Card {
<p class="wrn-next__card-eyebrow">{header}</p>
{/if}
{#if title}
<h3>{title}</h3>
<h3 data-t='{titleKey}'>{title}</h3>
{/if}
{#if subtitle}
<p class="wrn-next__card-subtitle">{subtitle}</p>
<p class="wrn-next__card-subtitle" data-t='{subtitleKey}'>{subtitle}</p>
{/if}
</div>
@@ -268,7 +272,7 @@ component Card {
</div>
{:else}
{#if description}
<p class="wrn-next__card-description">{description}</p>
<p class="wrn-next__card-description" data-t='{descriptionKey}'>{description}</p>
{/if}
<slot></slot>
{/if}
@@ -285,7 +289,7 @@ component Card {
class="wrn-next__card-action"
@click='output.action({ href: actionHref, label: actionLabel })'
>
<span>{actionLabel}</span>
<span data-t='{actionLabelKey}'>{actionLabel}</span>
<span class="icon-[lucide--arrow-right]" aria-hidden="true"></span>
</a>
{/if}
+4 -4
View File
@@ -388,11 +388,11 @@ component Carousel {
<img src="{item.imageSrc}" alt="{item.imageAlt || item.title || ''}" />
{/if}
<div class="wrn-next__carousel-slide-content">
{#if item.eyebrow}<span>{item.eyebrow}</span>{/if}
{#if item.title || item.label}<h4>{item.title || item.label}</h4>{/if}
{#if item.description}<p>{item.description}</p>{/if}
{#if item.eyebrow}<span data-t='{item.eyebrowKey || ""}'>{item.eyebrow}</span>{/if}
{#if item.title || item.label}<h4 data-t='{item.titleKey || item.labelKey || ""}'>{item.title || item.label}</h4>{/if}
{#if item.description}<p data-t='{item.descriptionKey || ""}'>{item.description}</p>{/if}
{#if item.actionLabel}
<a href="{item.actionHref || '#'}">{item.actionLabel}</a>
<a href="{item.actionHref || '#'}" data-t='{item.actionLabelKey || ""}'>{item.actionLabel}</a>
{/if}
</div>
</article>
+22 -6
View File
@@ -13,9 +13,23 @@ component Footer {
columns: number = 3
maxWidth: string = "compact"
copyright: string = ""
copyrightKey: string = ""
translationPrefix: string = ""
class: string = ""
}
functions {
shared function translationKey(item, field) {
if (!item) return ""
const explicit = item[field + "Key"]
if (explicit) return explicit
if (!translationPrefix) return ""
const source = item.value || item.href || item.label || item.title || ""
const slug = String(source).replace(/^\/+|\/+$/g, "").replace(/[^A-Za-z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase()
return slug ? translationPrefix + "." + slug + (field === "description" ? ".description" : "") : ""
}
}
view {
<footer
{...attrs}
@@ -58,12 +72,13 @@ component Footer {
<h2
id='footer-heading-{itemIndex}'
class="wrn-footer__heading"
data-t='{translationKey(item, "label")}'
>
{item.label || item.title}
</h2>
{#if item.description}
<p>
<p data-t='{translationKey(item, "description")}'>
{item.description}
</p>
{/if}
@@ -77,7 +92,7 @@ component Footer {
href='{child.href || "#"}'
target='{child.target || ""}'
rel='{child.external ? "noopener noreferrer" : (child.rel || "")}'
aria-current='{child.active ? "page" : ""}'
aria-current='{child.active ? "page" : "false"}'
class="wrn-footer__link"
@click='output.select({ item: child, parent: item, itemIndex: childIndex, sectionIndex: itemIndex }); child.action && output.action({ item: child, parent: item, itemIndex: childIndex, sectionIndex: itemIndex })'
>
@@ -89,7 +104,7 @@ component Footer {
</span>
{/if}
<span>
<span data-t='{translationKey(child, "label")}'>
{child.label || child.title}
</span>
@@ -125,7 +140,7 @@ component Footer {
href='{item.href || "#"}'
target='{item.target || ""}'
rel='{item.external ? "noopener noreferrer" : (item.rel || "")}'
aria-current='{item.active ? "page" : ""}'
aria-current='{item.active ? "page" : "false"}'
class="wrn-footer__link"
@click='output.select({ item: item, itemIndex: itemIndex }); item.action && output.action({ item: item, itemIndex: itemIndex })'
>
@@ -137,7 +152,7 @@ component Footer {
</span>
{/if}
<span>
<span data-t='{translationKey(item, "label")}'>
{item.label || item.title}
</span>
@@ -177,6 +192,7 @@ component Footer {
<h2
id='footer-heading-{itemIndex}'
class="wrn-footer__heading"
data-t='{translationKey(item, "label")}'
>
{item.title || item.label}
</h2>
@@ -264,7 +280,7 @@ component Footer {
<p
class="wrn-footer__copyright"
>
{copyright}
<span data-t='{copyrightKey}'>{copyright}</span>
</p>
{/if}
+20 -10
View File
@@ -20,12 +20,22 @@ size: string = "default"
openOnHover: boolean = false
maxWidth: string = "full"
mobileLabel: string = "Toggle navigation"
translationPrefix: string = ""
class: string = ""
}
state mobileOpen: boolean = false
functions {
shared function translationKey(item, field) {
if (!item) return ""
const explicit = item[field + "Key"]
if (explicit) return explicit
if (!translationPrefix) return ""
const source = item.value || item.href || item.label || item.title || ""
const slug = String(source).replace(/^\/+|\/+$/g, "").replace(/[^A-Za-z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase()
return slug ? translationPrefix + "." + slug + (field === "description" ? ".description" : "") : ""
}
client function toggleNavigation() {
mobileOpen = !mobileOpen
output.toggle({ open: mobileOpen })
@@ -75,8 +85,8 @@ size: string = "default"
{/if}
{#if brand.label || brand.description}
<span class="wrn-navbar__brand-copy">
{#if brand.label}<strong>{brand.label}</strong>{/if}
{#if brand.description}<small>{brand.description}</small>{/if}
{#if brand.label}<strong data-t="{brand.labelKey || (translationPrefix ? translationPrefix + '.brand' : '')}">{brand.label}</strong>{/if}
{#if brand.description}<small data-t="{brand.descriptionKey || (translationPrefix ? translationPrefix + '.brand.description' : '')}">{brand.description}</small>{/if}
</span>
{/if}
</a>
@@ -92,26 +102,26 @@ size: string = "default"
<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}
<span>{item.label}</span>
<span data-t="{translationKey(item, 'label')}">{item.label}</span>
<span class="wrn-navbar__chevron" aria-hidden="true"></span>
</summary>
<div class="wrn-navbar__panel wrn-navbar__panel--columns-{item.columns || 1}">
{#if item.description}<p class="wrn-navbar__panel-intro">{item.description}</p>{/if}
{#if item.description}<p class="wrn-navbar__panel-intro" data-t="{translationKey(item, 'description')}">{item.description}</p>{/if}
{#each item.children as child}
<div class="wrn-navbar__group">
{#if child.children && child.children.length}
{#if child.label}<strong class="wrn-navbar__group-title">{child.label}</strong>{/if}
{#if child.description}<small>{child.description}</small>{/if}
{#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}
<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>{nested.label}</strong>{#if nested.description}<small>{nested.description}</small>{/if}</span>
<span><strong data-t="{translationKey(nested, 'label')}">{nested.label}</strong>{#if nested.description}<small data-t="{translationKey(nested, 'description')}">{nested.description}</small>{/if}</span>
</a>
{/each}
{:else}
<a href="{child.href || '#'}" target="{child.target || ''}" rel="{child.rel || ''}" aria-current="{child.value === active ? 'page' : 'false'}" @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>
<span><strong data-t="{translationKey(child, 'label')}">{child.label}</strong>{#if child.description}<small data-t="{translationKey(child, 'description')}">{child.description}</small>{/if}</span>
</a>
{/if}
</div>
@@ -121,7 +131,7 @@ size: string = "default"
{:else}
<a class="wrn-navbar__menu-link" data-wrn-roving-item="true" href="{item.href || '#'}" target="{item.target || ''}" rel="{item.rel || ''}" aria-current="{item.value === active ? 'page' : 'false'}" @click="selectItem(item, 1)">
{#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if}
<span>{item.label}</span>
<span data-t="{translationKey(item, 'label')}">{item.label}</span>
</a>
{/if}
{/each}
@@ -131,7 +141,7 @@ size: string = "default"
{#each actions as item}
<a class="wrn-navbar__action wrn-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>
<span data-t="{translationKey(item, 'label')}">{item.label}</span>
</a>
{/each}
<slot name="actions" />