557 lines
16 KiB
Plaintext
557 lines
16 KiB
Plaintext
component Footer {
|
|
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)
|
|
action(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
|
}
|
|
|
|
props {
|
|
|
|
size: string = "default"
|
|
color: string = "primary"
|
|
label: string = "Footer navigation"
|
|
items: unknown[] = []
|
|
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}
|
|
data-ui-component="Footer"
|
|
data-size='{size}'
|
|
data-color='{color}'
|
|
class='wrn-footer {maxWidth === "full" ? "wrn-footer--width-full" : ""} {class}'
|
|
>
|
|
<div
|
|
class="wrn-footer__grid"
|
|
>
|
|
<div
|
|
class="wrn-footer__pre"
|
|
>
|
|
<slot
|
|
name="pre-footer"
|
|
>
|
|
</slot>
|
|
</div>
|
|
|
|
{#if items.length > 0}
|
|
<nav
|
|
class='wrn-footer__links wrn-footer--columns-{columns}'
|
|
aria-label='{label}'
|
|
>
|
|
{#each items as item, itemIndex}
|
|
{#if item.type === "header"}
|
|
<section
|
|
class="wrn-footer__column"
|
|
aria-labelledby='footer-heading-{itemIndex}'
|
|
>
|
|
{#if item.icon}
|
|
<span
|
|
class='{item.icon}'
|
|
aria-hidden="true"
|
|
>
|
|
</span>
|
|
{/if}
|
|
|
|
<h2
|
|
id='footer-heading-{itemIndex}'
|
|
class="wrn-footer__heading"
|
|
data-t='{translationKey(item, "label")}'
|
|
>
|
|
{item.label || item.title}
|
|
</h2>
|
|
|
|
{#if item.description}
|
|
<p data-t='{translationKey(item, "description")}'>
|
|
{item.description}
|
|
</p>
|
|
{/if}
|
|
|
|
{#if (item.items || item.links || []).length > 0}
|
|
<div
|
|
class="wrn-footer__column-links"
|
|
>
|
|
{#each item.items || item.links || [] as child, childIndex}
|
|
<a
|
|
href='{child.href || "#"}'
|
|
target='{child.target || ""}'
|
|
rel='{child.external ? "noopener noreferrer" : (child.rel || "")}'
|
|
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 })'
|
|
>
|
|
{#if child.icon}
|
|
<span
|
|
class='{child.icon}'
|
|
aria-hidden="true"
|
|
>
|
|
</span>
|
|
{/if}
|
|
|
|
<span data-t='{translationKey(child, "label")}'>
|
|
{child.label || child.title}
|
|
</span>
|
|
|
|
{#if child.badge}
|
|
<span
|
|
class="wrn-badge wrn-badge--default"
|
|
>
|
|
{child.badge}
|
|
</span>
|
|
{/if}
|
|
|
|
{#if child.external}
|
|
<span
|
|
class="icon-[lucide--arrow-up-right]"
|
|
aria-hidden="true"
|
|
>
|
|
</span>
|
|
{/if}
|
|
</a>
|
|
{/each}
|
|
</div>
|
|
{/if}
|
|
</section>
|
|
|
|
{:else if item.type === "link" || item.href}
|
|
<section
|
|
class="wrn-footer__column"
|
|
>
|
|
<div
|
|
class="wrn-footer__column-links"
|
|
>
|
|
<a
|
|
href='{item.href || "#"}'
|
|
target='{item.target || ""}'
|
|
rel='{item.external ? "noopener noreferrer" : (item.rel || "")}'
|
|
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 })'
|
|
>
|
|
{#if item.icon}
|
|
<span
|
|
class='{item.icon}'
|
|
aria-hidden="true"
|
|
>
|
|
</span>
|
|
{/if}
|
|
|
|
<span data-t='{translationKey(item, "label")}'>
|
|
{item.label || item.title}
|
|
</span>
|
|
|
|
{#if item.badge}
|
|
<span
|
|
class="wrn-badge wrn-badge--default"
|
|
>
|
|
{item.badge}
|
|
</span>
|
|
{/if}
|
|
|
|
{#if item.external}
|
|
<span
|
|
class="icon-[lucide--arrow-up-right]"
|
|
aria-hidden="true"
|
|
>
|
|
</span>
|
|
{/if}
|
|
</a>
|
|
</div>
|
|
</section>
|
|
|
|
{:else}
|
|
<section
|
|
class="wrn-footer__column"
|
|
aria-labelledby='footer-heading-{itemIndex}'
|
|
>
|
|
{#if item.icon}
|
|
<span
|
|
class='{item.icon}'
|
|
aria-hidden="true"
|
|
>
|
|
</span>
|
|
{/if}
|
|
|
|
{#if item.title || item.label}
|
|
<h2
|
|
id='footer-heading-{itemIndex}'
|
|
class="wrn-footer__heading"
|
|
data-t='{translationKey(item, "label")}'
|
|
>
|
|
{item.title || item.label}
|
|
</h2>
|
|
{/if}
|
|
|
|
{#if item.description}
|
|
<p>
|
|
{item.description}
|
|
</p>
|
|
{/if}
|
|
|
|
{#if (item.items || item.links || []).length > 0}
|
|
<div
|
|
class="wrn-footer__column-links"
|
|
>
|
|
{#each item.items || item.links || [] as child, childIndex}
|
|
<a
|
|
href='{child.href || "#"}'
|
|
target='{child.target || ""}'
|
|
rel='{child.external ? "noopener noreferrer" : (child.rel || "")}'
|
|
aria-current='{child.active ? "page" : ""}'
|
|
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 })'
|
|
>
|
|
{#if child.icon}
|
|
<span
|
|
class='{child.icon}'
|
|
aria-hidden="true"
|
|
>
|
|
</span>
|
|
{/if}
|
|
|
|
<span>
|
|
{child.label || child.title}
|
|
</span>
|
|
|
|
{#if child.badge}
|
|
<span
|
|
class="wrn-badge wrn-badge--default"
|
|
>
|
|
{child.badge}
|
|
</span>
|
|
{/if}
|
|
|
|
{#if child.external}
|
|
<span
|
|
class="icon-[lucide--arrow-up-right]"
|
|
aria-hidden="true"
|
|
>
|
|
</span>
|
|
{/if}
|
|
</a>
|
|
{/each}
|
|
</div>
|
|
{/if}
|
|
</section>
|
|
{/if}
|
|
{/each}
|
|
</nav>
|
|
{/if}
|
|
|
|
<div
|
|
class="wrn-footer__post"
|
|
>
|
|
<slot
|
|
name="post-footer"
|
|
>
|
|
</slot>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
class="wrn-footer__bottom wrn-footer__bottom--slots"
|
|
>
|
|
<div
|
|
class="wrn-footer__copyright-left"
|
|
>
|
|
<slot
|
|
name="copyright-left"
|
|
>
|
|
</slot>
|
|
</div>
|
|
|
|
{#if copyright}
|
|
<p
|
|
class="wrn-footer__copyright"
|
|
>
|
|
<span data-t='{copyrightKey}'>{copyright}</span>
|
|
</p>
|
|
{/if}
|
|
|
|
<div
|
|
class="wrn-footer__copyright-right"
|
|
>
|
|
<slot
|
|
name="copyright-right"
|
|
>
|
|
</slot>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
}
|
|
|
|
style {
|
|
.wrn-footer--columns-1 {
|
|
--wrn-footer-columns: 1;
|
|
}
|
|
|
|
.wrn-footer--columns-2 {
|
|
--wrn-footer-columns: 2;
|
|
}
|
|
|
|
.wrn-footer--columns-3 {
|
|
--wrn-footer-columns: 3;
|
|
}
|
|
|
|
.wrn-footer--columns-4 {
|
|
--wrn-footer-columns: 4;
|
|
}
|
|
|
|
.wrn-footer--columns-5 {
|
|
--wrn-footer-columns: 5;
|
|
}
|
|
|
|
.wrn-footer--columns-6 {
|
|
--wrn-footer-columns: 6;
|
|
}
|
|
|
|
@media (max-width: 900px) {
|
|
.wrn-footer--columns-1 .wrn-footer__links {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.wrn-footer--columns-2 .wrn-footer__links {
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
}
|
|
}
|
|
|
|
@media (max-width: 600px) {
|
|
.wrn-footer--columns-1 .wrn-footer__links {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
|
|
/* --- Badge ---------------------------------------------------------------- */
|
|
.wrn-badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
font-size: 0.75rem;
|
|
font-weight: 700;
|
|
line-height: 1;
|
|
padding: 0.25rem 0.5rem;
|
|
border-radius: 999px;
|
|
}
|
|
|
|
.wrn-badge--default {
|
|
background: var(--wrn-color-surface-2);
|
|
color: var(--wrn-color-text);
|
|
}
|
|
|
|
/* Structured responsive footer */
|
|
.wrn-footer {
|
|
width: 100%;
|
|
color: var(--wrn-color-text);
|
|
background: var(--wrn-color-surface);
|
|
border-top: 1px solid var(--wrn-color-border);
|
|
font-size: 0.875rem;
|
|
font-weight: 400;
|
|
}
|
|
|
|
.wrn-footer__grid {
|
|
display: grid;
|
|
grid-template-columns: minmax(13rem, 1.2fr) minmax(0, 3fr) minmax(13rem, 1fr);
|
|
width: min(100%, 90rem);
|
|
margin-inline: auto;
|
|
padding: clamp(2rem, 5vw, 4rem) clamp(1rem, 4vw, 3rem);
|
|
gap: clamp(1.5rem, 4vw, 3.5rem);
|
|
}
|
|
|
|
.wrn-footer--width-full .wrn-footer__grid {
|
|
width: 100%;
|
|
max-width: none;
|
|
}
|
|
|
|
.wrn-footer__pre:empty,
|
|
.wrn-footer__post:empty,
|
|
.wrn-footer__pre:not(:has(*)),
|
|
.wrn-footer__post:not(:has(*)) {
|
|
display: none;
|
|
}
|
|
|
|
.wrn-footer__pre,
|
|
.wrn-footer__post {
|
|
min-width: 0;
|
|
}
|
|
|
|
.wrn-footer__pre strong,
|
|
.wrn-footer__post strong {
|
|
font-size: 0.875rem;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.wrn-footer__pre p,
|
|
.wrn-footer__post p {
|
|
font-size: 0.8125rem;
|
|
font-weight: 400;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.wrn-footer__links {
|
|
display: grid;
|
|
grid-template-columns: repeat(var(--wrn-footer-columns, 3), minmax(0, 1fr));
|
|
align-content: start;
|
|
gap: 1.5rem;
|
|
}
|
|
|
|
.wrn-footer__column {
|
|
display: grid;
|
|
min-width: 0;
|
|
align-content: start;
|
|
gap: 0.8rem;
|
|
}
|
|
|
|
.wrn-footer__column-links {
|
|
display: grid;
|
|
align-content: start;
|
|
gap: 0.55rem;
|
|
}
|
|
|
|
.wrn-footer__heading {
|
|
display: block;
|
|
color: var(--wrn-color-text);
|
|
font-size: 0.8125rem;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.wrn-footer__link {
|
|
display: flex;
|
|
width: fit-content;
|
|
min-height: 2rem;
|
|
align-items: center;
|
|
gap: 0.4rem;
|
|
color: var(--wrn-color-muted);
|
|
font-size: 0.8125rem;
|
|
font-weight: 400;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.wrn-footer__link:hover {
|
|
color: var(--wrn-component-color, var(--wrn-color-primary));
|
|
text-decoration: underline;
|
|
text-underline-offset: 0.2em;
|
|
}
|
|
|
|
.wrn-footer__bottom {
|
|
display: grid;
|
|
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
|
|
width: min(100%, 90rem);
|
|
margin-inline: auto;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
padding: 1rem clamp(1rem, 4vw, 3rem);
|
|
color: var(--wrn-color-muted);
|
|
font-size: 0.75rem;
|
|
font-weight: 400;
|
|
}
|
|
|
|
.wrn-footer__bottom--slots {
|
|
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
|
|
border-top: 1px solid var(--wrn-color-border);
|
|
}
|
|
|
|
.wrn-footer__bottom--slots:not(
|
|
:has(.wrn-footer__copyright-left > *, .wrn-footer__copyright-right > *)
|
|
) {
|
|
display: none;
|
|
}
|
|
|
|
.wrn-footer__copyright {
|
|
margin: 0;
|
|
text-align: center;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.wrn-footer__copyright-left,
|
|
.wrn-footer__copyright-right {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
min-width: 0;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
.wrn-footer__copyright-left a,
|
|
.wrn-footer__copyright-right a {
|
|
color: inherit;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.wrn-footer__copyright-left a:hover,
|
|
.wrn-footer__copyright-right a:hover {
|
|
color: var(--wrn-component-color, var(--wrn-color-primary));
|
|
text-decoration: underline;
|
|
text-underline-offset: 0.2em;
|
|
}
|
|
|
|
.wrn-footer__copyright-right {
|
|
justify-content: flex-end;
|
|
text-align: right;
|
|
}
|
|
|
|
.wrn-footer__copyright-left:empty,
|
|
.wrn-footer__copyright-right:empty,
|
|
.wrn-footer__copyright-left:not(:has(*)),
|
|
.wrn-footer__copyright-right:not(:has(*)) {
|
|
min-height: 0;
|
|
}
|
|
|
|
@media (max-width: 900px) {
|
|
.wrn-footer__grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
.wrn-footer__links {
|
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
}
|
|
.wrn-footer__bottom--slots {
|
|
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
|
}
|
|
.wrn-footer__copyright {
|
|
grid-column: 1 / -1;
|
|
grid-row: 1;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 600px) {
|
|
.wrn-footer__links {
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
}
|
|
.wrn-footer__bottom,
|
|
.wrn-footer__bottom--slots {
|
|
grid-template-columns: 1fr;
|
|
justify-items: center;
|
|
text-align: center;
|
|
}
|
|
.wrn-footer__copyright-left,
|
|
.wrn-footer__copyright-right {
|
|
justify-content: center;
|
|
text-align: center;
|
|
}
|
|
.wrn-footer__copyright {
|
|
white-space: normal;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 420px) {
|
|
.wrn-footer__links {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
}
|
|
}
|