/* eslint-disable lit-a11y/anchor-is-valid */ import { css, html, nothing } from 'lit'; import '@lion/ui/define/lion-icon.js'; import { UIBaseElement } from './shared/UIBaseElement.js'; import { addIconResolverForPortal } from './iconset-portal/addIconResolverForPortal.js'; import uiPortalMainNavBurgerCss from './ui-portal-main-nav-burger.css.js'; try { addIconResolverForPortal(); } catch (e) { // do nothing // icons can be registered by somebody else? } // TODO: apply https://web.dev/website-navigation/ (aria-current="page" etc.) /** * @typedef {{name: string; url: string; active?:boolean; iconId?: string; children?: NavItemData[]}} NavItem */ export class UIPortalMainNav extends UIBaseElement { static properties = { navData: { type: Array, attribute: 'nav-data' }, layoutWide: { type: Boolean, attribute: 'layout-wide' }, // true or false }; constructor() { super(); /** * @type {NavItem[]} */ this.navData = []; this.layoutWide = false; this.getLink = item => html`${item.name}`; } connectedCallback() { super.connectedCallback(); if (window) { // only on the client window.setTimeout(() => { // remove the second navigation // its rendered twice due to lack of lit/ssr // https://github.com/lit/lit/issues/4472 const $navs = this.renderRoot.querySelectorAll('[data-part="nav"]'); if ($navs.length > 1) { $navs[1].remove(); } }); } } get templateContext() { return { ...super.templateContext, data: { navData: this.navData }, }; } static templates = { main(context) { const { data, templates } = context; return html` `; }, navLevel(context, { children }) { const { templates } = context; return html``; }, navItem(context, { item }) { return this.getLink(item); }, }; attributeChangedCallback(attrName, oldVal, newVal) { super.attributeChangedCallback(attrName, oldVal, newVal); if (attrName === 'layout-wide') { if (newVal === true || newVal === 'true') { this.setAttribute('data-wide', 'true'); } else { this.removeAttribute('data-wide'); } } } } export const tagName = 'ui-portal-main-nav'; const sharedGlobalStyles = css` * { box-sizing: border-box; } `; /** * Base UI Nav templates contains an accessible base html that can be used for all kinds of navigations, * regardless of presentation: horizontally stacked, vertically stacked or a combination of both. * With or without collapsible levels, with or without overlays. * with any amount of nested levels. * @returns */ const baseUINavMarkup = { templates: () => ({ main(context) { const { data, templates } = context; return html` `; }, navLevel(context, { children, level, hasActiveChild = false }) { const { templates } = context; return html`
${level === 1 ? html` ` : nothing}
`; }, navLevel3(context, { children, level, item }) { const { templates } = context; return html`
${this.getLink(item)}
`; // accordion with all the links // return html` //

//
// //
// `; }, navItem(context, { item, level }) { return html`${level === 1 ? html`` : nothing}${item.name}`; }, }), // this is not working // you need to use global elements definitions scopedElements: () => ({}), }; UIPortalMainNav.provideStylesAndMarkup({ markup: baseUINavMarkup, /** 2 columns */ styles: () => [ sharedGlobalStyles, uiPortalMainNavBurgerCss, css` :host { --_width-l0: var(--size-12); --_width-l1: var(--size-13); height: 100vh; /** Make this the positioning parent of l0 and l1 */ position: relative; width: var(--_width-l0); display: block; position: sticky; top: 0; } :host([data-layout='inline-columns'][data-wide='true']) { width: calc(var(--_width-l0) + var(--_width-l1)); } :host [data-part='nav'] { height: 100%; } :host [data-part='level'][data-level='1'], :host [data-part='level'][data-level='2'] { padding-block-start: var(--size-6); padding-inline: var(--size-2); overflow-y: scroll; } :host [data-part='level'][data-level='1'] { width: var(--_width-l0); height: 100vh; border-right: 1px solid #ccc; overflow: hidden; display: flex; flex-direction: column; justify-content: space-between; } /** * When a l0 child is active, or a l1 child => open correct l1 */ :host [data-part='listitem']:not([data-\:active]) [data-part='level'][data-level='2']:not([data-has-active-child]) { /** TODO: sr-only, because we want to present all links to the screen reader */ display: none; } :host [data-part='level'][data-level='2'] { width: var(--_width-l1); position: absolute; left: var(--_width-l0); top: 0; /* padding-inline: var(--size-6); */ border-right: 1px solid #ccc; height: 100%; } :host [data-part='list'] { list-style-type: none; margin: 4px; padding: 0; } :host [data-part='anchor'][data-level='1'] { display: block; padding-block: var(--size-6); padding-inline: var(--size-6); } :host [data-part='anchor'][data-level='2'] { display: block; padding-block: var(--size-3); padding-inline: var(--size-6); } :host [data-part='anchor'][data-level='2'][aria-current='page']:not(:last-child) { padding-block: var(--size-2); } :host [data-part='icon'][data-level='1'] { display: block; width: var(--size-7); height: var(--size-7); margin-bottom: var(--size-1); } :host [data-part='anchor'][data-level='1'] { display: flex; flex-direction: column; align-items: center; margin-block-end: 6px; } :host [data-part='anchor'] { display: block; color: var(--text-color); text-decoration: inherit; font-size: 1rem; fill: var(--primary-icon-color); margin-inline: var(--size-1); border-radius: var(--radius-4); } :host [data-part='anchor'][aria-current='page'][data-level='1'], :host [data-part='anchor'][aria-current='page'][data-level='3']:last-child, :host [data-part='anchor'][aria-current='page'][data-level='4'] { font-weight: bold; background-color: var(--secondary-color); } :host [data-part='anchor']:hover { text-decoration: underline; text-underline-offset: 0.3em; background-color: var(--secondary-color-lighter); } :host [data-part='anchor']:focus { outline: 2px solid var(--contrast-color-dark); } :host [data-part='anchor'][data-level='2']:focus, :host [data-part='anchor'][data-level='2']:focus { outline-offset: 2px; } :host [data-part='level'][data-level='2'] { display: none; } :host [data-\\:active] [data-part='level'][data-level='2'] { display: block; } :host [data-part='level'][data-level='2'] { color: var(--text-color, #333); /* 14px/Regular */ font-family: 'ING Me'; font-size: 0.875rem; font-style: normal; font-weight: 400; line-height: 20px; /* 142.857% */ text-decoration: none; } :host [data-part='listitem'][data-level='2'][data-\\:active] { border-radius: var(--radius-4); background: var(--neutral-color-lightest); margin-block: 6px; } :host [data-part='level'][data-level='3'] { overflow: hidden; padding-block-end: 12px; } :host [data-part='anchor'][data-level='3'], :host [data-part='anchor'][data-level='4'] { /* 14px/Regular */ font-family: 'ING Me'; font-size: 0.875rem; font-style: normal; font-weight: 400; line-height: 20px; /* 142.857% */ text-decoration: none; margin-left: var(--size-7); padding-inline: var(--size-3); } :host [data-part='anchor'][data-level='3'][aria-current='page'], :host [data-part='anchor'][data-level='4'][aria-current='page'] { font-weight: bold; } :host [data-level='2'] > [aria-current='page'] { background: transparent; font-weight: bold; } :host [data-part='list'][data-level='4'] { margin-left: var(--size-3); } `, ], layouts: () => ({ 'floating-toggle': 0, 'inline-columns': 900, }), layoutsContainer: () => globalThis, }); customElements.define(tagName, UIPortalMainNav);