@nysds/nys-globalheader 1.14.0 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/nys-globalheader.js
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { LitElement as b, unsafeCSS as _, html as s } from "lit";
|
|
2
2
|
import { property as c, state as y } from "lit/decorators.js";
|
|
3
|
+
/*!
|
|
4
|
+
* █▄ █ █ █ █▀▀▀█ █▀▀▄ █▀▀▀█
|
|
5
|
+
* █ █ █ █▄▄▄█ ▀▀▀▄▄ █ █ ▀▀▀▄▄
|
|
6
|
+
* █ ▀█ █ █▄▄▄█ █▄▄▀ █▄▄▄█
|
|
7
|
+
*
|
|
8
|
+
* Global Header Component
|
|
9
|
+
* Part of the New York State Design System
|
|
10
|
+
* Repository: https://github.com/its-hcd/nysds
|
|
11
|
+
* License: MIT
|
|
12
|
+
*/
|
|
3
13
|
const m = ':host{--_nys-globalheader-color: var( --nys-color-text-reverse, var(--nys-color-white, #ffffff) );--_nys-globalheader-link-color: var( --nys-color-link-reverse-neutral, var(--nys-color-white, #ffffff) );--_nys-globalheader-background-color: var( --nys-color-theme, var(--nys-color-state-blue-700, #154973) );--_nys-globalheader-gap: var(--nys-space-300, 24px);--_nys-globalheader-padding: var(--nys-space-250, 20px);--_nys-globalheader-font-family--menu: var( --nys-font-family-ui, var( --nys-font-family-sans, "Proxima Nova", "Helvetica Neue", "Helvetica", "Arial", sans-serif ) );--_nys-globalheader-line-height: normal;--_nys-globalheader-letter-spacing: normal;--_nys-globalheader-font-weight: var(--nys-font-weight-semibold, 600);--_nys-globalheader-max-width: var(--nys-max-content-width, 1280px);--_nys-globalheader-gap--text: var(--nys-space-100, 8px);--_nys-globalheader-font-size--heading: var( --nys-font-size-agency-xl, var(--nys-font-size-2xl, 22px) );--_nys-globalheader-font-size--subheading: var( --nys-font-size-agency-md, var(--nys-font-size-md, 16px) );--_nys-globalheader-font-family--headings: var( --nys-font-family-agency, "D Sari", Arial, sans-serif );--_nys-globalheader-line-height--menu: var(--nys-font-lineheight-ui-md, 24px);--_nys-globalheader-letter-spacing--menu: var( --nys-font-letterspacing-ui-md, var(--nys-font-letterspacing-400, .044px) );--_nys-globalheader-text-decoration-thickness--menu: var(--nys-size-2px, 2px);--_nys-globalheader-link-padding: var(--nys-space-300, 24px) var(--nys-space-200, 16px);--_nys-globalheader-font-size--menu-btn: var(--nys-type-size-ui-xs, 12px);--_nys-globalheader-line-height--menu-btn: var( --nys-font-lineheight-ui-xs, 20px );--_nys-globalheader-letter-spacing--menu-btn: var( --nys-font-letterspacing-ui-xs, .057px );--_nys-globalheader-border-color--menu: var(--nys-color-theme-mid, #457aa5);--_nys-globalheader-background-color--menu--hover: var( --nys-color-theme-strong, #0e324f );--_nys-globalheader-background-color--menu--active: var( --nys-color-theme-stronger, #081b2b )}ul{list-style-type:none;padding:0;margin:0}li{display:block;margin:0;padding:0;box-sizing:border-box}a{color:var(--_nys-globalheader-color);text-decoration:none;font-family:var(--_nys-globalheader-font-family--menu);font-style:normal;font-weight:400;line-height:var(--_nys-globalheader-line-height--menu);letter-spacing:var(--_nys-globalheader-letter-spacing--menu)}::slotted([slot=user-actions]){display:flex;align-items:center;margin-inline-start:auto;--_nys-button-outline-color: var( --nys-color-ink-reverse, var(--nys-color-white, #ffffff) )}.nys-globalheader{display:flex;justify-content:center;padding:var(--_nys-globalheader-padding);background-color:var(--_nys-globalheader-background-color);color:var(--_nys-globalheader-color);width:100%;min-height:76px;box-sizing:border-box}a#nys-globalheader__logolink{outline-offset:var(--nys-space-2px, 2px);outline-color:var(--nys-color-ink-reverse, #ffffff);margin:auto 0}.nys-globalheader__logo svg{vertical-align:top;width:auto}.nys-globalheader__main-container{display:flex;gap:var(--_nys-globalheader-gap);max-width:var(--_nys-globalheader-max-width);width:100%}.nys-globalheader__name-container{display:flex;flex-direction:column;justify-content:center;align-items:flex-start;gap:var(--_nys-globalheader-gap--text)}.nys-globalheader__name{margin:0;color:var(--_nys-globalheader-color);font-family:var(--_nys-globalheader-font-family--headings);font-size:var(--_nys-globalheader-font-size--heading);font-style:normal;font-weight:var(--_nys-globalheader-font-weight);line-height:var(--_nys-globalheader-line-height);letter-spacing:var(--_nys-globalheader-letter-spacing);overflow-wrap:break-word}.nys-globalheader__agencyName{font-size:var(--_nys-globalheader-font-size--subheading)}.nys-globalheader__agencyName.main{font-size:var(--_nys-globalheader-font-size--heading)}.nys-globalheader__content{display:none;font-family:var(--_nys-globalheader-font-family--menu)}.nys-globalheader__content ul{display:flex;flex-flow:column wrap;align-items:center}.nys-globalheader__content ul a:hover{text-decoration:underline;text-decoration-style:solid;text-decoration-skip-ink:auto;text-decoration-thickness:7%;text-underline-offset:auto;text-underline-position:from-font}.nys-globalheader__content ul a:active{text-decoration-thickness:var(--_nys-globalheader-text-decoration-thickness--menu)}.nys-globalheader__content li.active a,.nys-globalheader__content-mobile li.active a{font-weight:700}.nys-globalheader__content li.active{border-bottom:8px solid var(--nys-color-theme-weak, #cddde9)}.nys-globalheader__content li.active a{margin-bottom:calc(-1 * var(--nys-space-100, 8px))}.nys-globalheader__content-mobile li.active a{border-left:8px solid var(--nys-color-theme-weak, #cddde9);border-bottom:1px solid var(--_nys-globalheader-border-color--menu)}.nys-globalheader__content ul li.active a:hover{text-decoration:none}.nys-globalheader__content-mobile{position:absolute;z-index:10;display:flex;flex-direction:column;justify-content:center;background-color:var(--_nys-globalheader-background-color);width:fit-content}.nys-globalheader__content-mobile.close{display:none}.nys-globalheader__content-mobile ul{display:flex;flex-direction:column}.nys-globalheader__content-mobile ul li:first-child a{border-top:1px solid var(--_nys-globalheader-color)}.nys-globalheader__content-mobile ul li a{display:flex;padding:24px;align-items:center;gap:8px;align-self:stretch;border-bottom:1px solid var(--_nys-globalheader-border-color--menu);background-color:var(--_nys-globalheader-background-color)}.nys-globalheader__content-mobile ul li a:hover{background-color:var(--_nys-globalheader-background-color--menu--hover)}.nys-globalheader__content-mobile ul li a:active{background-color:var(--_nys-globalheader-background-color--menu--active)}.nys-globalheader__name-container-link{display:flex;flex-direction:column;justify-content:center}.nys-globalheader__button-container{display:flex;justify-content:center;align-items:center}.nys-globalheader__mobile-menu-button{flex-direction:column;gap:3px;width:50px;height:50px;background-color:var(--_nys-globalheader-background-color);border:none;cursor:pointer;display:flex;justify-content:center;align-items:center;padding:0;color:var(--_nys-globalheader-color)}.nys-globalheader__mobile-menu-button-text{font-size:var(--_nys-globalheader-font-size--menu-btn);line-height:var(--_nys-globalheader-line-height--menu-btn);letter-spacing:var(--_nys-globalheader-letter-spacing--menu-btn)}@media(min-width:1024px){.nys-globalheader__content{display:flex}.nys-globalheader__content ul{flex-direction:row}.nys-globalheader__content-mobile,.nys-globalheader__button-container{display:none}li{display:flex;align-items:center;padding:var(--_nys-globalheader-link-padding)}:host{--_nys-globalheader-gap: var(--nys-space-500, 40px);--_nys-globalheader-padding: var(--nys-space-50, 4px) var(--nys-size-400, 32px) 0}}@media(min-width:1280px){:host{--_nys-globalheader-padding: var(--nys-space-50, 4px) var(--nys-space-800, 64px) 0}}';
|
|
4
14
|
var p = Object.defineProperty, r = (g, l, o, t) => {
|
|
5
15
|
for (var n = void 0, e = g.length - 1, a; e >= 0; e--)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nys-globalheader.js","sources":["../src/nys-globalheader.ts"],"sourcesContent":["import { LitElement, html, unsafeCSS } from \"lit\";\nimport { property, state } from \"lit/decorators.js\";\n// @ts-ignore: SCSS module imported via bundler as inline\nimport styles from \"./nys-globalheader.scss?inline\";\n\n/**\n * Agency-branded header with app/agency name, navigation, and responsive mobile menu.\n *\n * Place below `nys-unavheader`. Slot navigation links as `<ul><li><a>` elements; active links\n * are auto-highlighted based on current URL. Mobile menu toggles automatically on narrow screens.\n *\n * @summary Agency header with navigation, mobile menu, and active link highlighting.\n * @element nys-globalheader\n *\n * @slot - Navigation content (typically `<ul>` with `<li><a>` links). Auto-sanitized.\n *\n * @example Basic header\n * ```html\n * <nys-globalheader appName=\"My App\" agencyName=\"Department of Health\" homepageLink=\"/\">\n * <ul><li><a href=\"/about\">About</a></li><li><a href=\"/contact\">Contact</a></li></ul>\n * </nys-globalheader>\n * ```\n */\n\nexport class NysGlobalHeader extends LitElement {\n static styles = unsafeCSS(styles);\n\n /** Application name displayed prominently. */\n @property({ type: String }) appName = \"\";\n\n /** Agency name displayed below app name (or as main title if no appName). */\n @property({ type: String }) agencyName = \"\";\n\n /** URL for the header title link. If empty, title is not clickable. */\n @property({ type: String }) homepageLink = \"\";\n @state() private isMobileMenuOpen = false;\n @state() private hasLinkContent = false;\n\n /**\n * Lifecycle Methods\n * --------------------------------------------------------------------------\n */\n\n firstUpdated() {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>(\"slot\");\n slot?.addEventListener(\"slotchange\", () => this._handleListSlotChange());\n this._handleListSlotChange(); // run once at startup\n\n this._listenLinkClicks();\n }\n\n /**\n * Functions\n * --------------------------------------------------------------------------\n */\n\n private _highlightActiveLink(container: HTMLElement) {\n const links = Array.from(container.querySelectorAll(\"a\"));\n const currentUrl = window.location.pathname.replace(/\\/+$/, \"\") || \"/\";\n\n let bestMatch: { li: HTMLElement | null; length: number } = {\n li: null,\n length: 0,\n };\n\n links.forEach((a) => {\n const linkPath = this._normalizePath(a.getAttribute(\"href\"));\n if (!linkPath) return;\n\n if (linkPath === \"/\" && currentUrl === \"/\") {\n bestMatch = { li: a.closest(\"li\"), length: 1 };\n } else if (\n currentUrl.startsWith(linkPath) &&\n linkPath.length > bestMatch.length\n ) {\n bestMatch = {\n li: a.closest(\"li\"),\n length: linkPath.length,\n };\n }\n });\n\n // Clear all previous actives\n links.forEach((a) => a.closest(\"li\")?.classList.remove(\"active\"));\n\n // Apply best match\n bestMatch.li?.classList.add(\"active\");\n }\n\n // Gets called when the slot content changes and directly appends the slotted elements into the shadow DOM\n private async _handleListSlotChange() {\n const slot = this.shadowRoot?.querySelector(\n 'slot:not([name=\"user-actions\"])',\n ) as HTMLSlotElement | null;\n\n if (!slot) return;\n\n const assignedNodes = slot\n .assignedNodes({ flatten: true })\n .filter((node) => node.nodeType === Node.ELEMENT_NODE) as Element[]; // Filter to elements only\n\n this.hasLinkContent = assignedNodes.length > 0;\n\n await Promise.resolve(); // Wait for current update cycle to complete before modifying reactive state (solves the lit issue \"scheduled an update\")\n\n // Get the containers to append the slotted elements\n const container = this.shadowRoot?.querySelector(\n \".nys-globalheader__content\",\n ) as HTMLElement | null;\n\n const containerMobile = this.shadowRoot?.querySelector(\n \".nys-globalheader__content-mobile\",\n ) as HTMLElement | null;\n\n if (!container || !containerMobile) return;\n\n // Clear existing children in the container\n container.innerHTML = \"\";\n containerMobile.innerHTML = \"\";\n\n // Clone and append slotted elements into the shadow DOM container\n assignedNodes.forEach((node) => {\n if (node instanceof HTMLElement) {\n // need to clone the node because cannot have same node in two places in DOM\n const nodeInline = node.cloneNode(true) as HTMLElement;\n const nodeMobile = node.cloneNode(true) as HTMLElement;\n\n container.appendChild(nodeInline);\n containerMobile.appendChild(nodeMobile);\n }\n });\n\n // Highlight active links AFTER DOM is finalized\n this._highlightActiveLink(container);\n this._highlightActiveLink(containerMobile);\n }\n\n // Normalize paths so that links like \"name\", \"/name/\", and \"/\" match window.location.pathname.\n // This ensures consistent active-link behavior regardless of how hrefs are written.\n private _normalizePath(href: string | null): string | null {\n if (!href) return null;\n\n try {\n const url = new URL(href, window.location.origin);\n return url.pathname.replace(/\\/+$/, \"\") || \"/\";\n } catch {\n return null;\n }\n }\n\n private _toggleMobileMenu() {\n this.isMobileMenuOpen = !this.isMobileMenuOpen;\n }\n\n // Listens for click events on links to mark them active\n private _listenLinkClicks() {\n const containers = this.shadowRoot?.querySelectorAll(\n \".nys-globalheader__content, .nys-globalheader__content-mobile\",\n );\n\n containers?.forEach((container) => {\n container?.addEventListener(\"click\", (event) => {\n const target = event.target as HTMLElement;\n const ahref = target.closest(\"a\");\n\n if (!ahref) return;\n\n // Clear all existing active <li>\n container\n .querySelectorAll(\"li.active\")\n .forEach((li) => li.classList.remove(\"active\"));\n\n // Set active on the clicked link's <li>\n const li = ahref.closest(\"li\");\n if (li) li.classList.add(\"active\");\n });\n });\n }\n\n render() {\n return html`\n <header class=\"nys-globalheader\">\n <div class=\"nys-globalheader__main-container\">\n ${this.hasLinkContent\n ? html` <div class=\"nys-globalheader__button-container\">\n <button\n class=\"nys-globalheader__mobile-menu-button\"\n @click=\"${this._toggleMobileMenu}\"\n >\n <nys-icon\n name=\"${this.isMobileMenuOpen ? \"close\" : \"menu\"}\"\n size=\"32\"\n label=\"${this.isMobileMenuOpen ? \"close\" : \"menu\"} icon\"\n ></nys-icon>\n <span class=\"nys-globalheader__mobile-menu-button-text\"\n >${this.isMobileMenuOpen ? \"CLOSE\" : \"MENU\"}</span\n >\n </button>\n </div>`\n : \"\"}\n ${!this.homepageLink?.trim()\n ? html`\n <div class=\"nys-globalheader__name-container\">\n ${this.appName?.trim().length > 0\n ? html`<div\n class=\"nys-globalheader__appName nys-globalheader__name\"\n >\n ${this.appName}\n </div> `\n : \"\"}\n ${this.agencyName?.trim().length > 0\n ? html`<div\n class=\"nys-globalheader__agencyName nys-globalheader__name ${this.appName?.trim()\n .length > 0\n ? \"\"\n : \"main\"}\"\n >\n ${this.agencyName}\n </div> `\n : \"\"}\n </div>\n `\n : html`<a\n class=\"nys-globalheader__name-container-link\"\n href=${this.homepageLink?.trim()}\n >\n <div class=\"nys-globalheader__name-container\">\n ${this.appName?.trim().length > 0\n ? html`<div\n class=\"nys-globalheader__appName nys-globalheader__name\"\n >\n ${this.appName}\n </div> `\n : \"\"}\n ${this.agencyName?.trim().length > 0\n ? html`<div\n class=\"nys-globalheader__agencyName nys-globalheader__name ${this.appName?.trim()\n .length > 0\n ? \"\"\n : \"main\"}\"\n >\n ${this.agencyName}\n </div> `\n : \"\"}\n </div>\n </a>`}\n <div class=\"nys-globalheader__content\"></div>\n <slot\n style=\"display: none;\"\n @slotchange=\"${this._handleListSlotChange}\"\n ></slot>\n <slot name=\"user-actions\"></slot>\n </div>\n </header>\n <div\n class=\"nys-globalheader__content-mobile ${this.isMobileMenuOpen\n ? \"\"\n : \"close\"}\"\n ></div>\n `;\n }\n}\n\nif (!customElements.get(\"nys-globalheader\")) {\n customElements.define(\"nys-globalheader\", NysGlobalHeader);\n}\n"],"names":["_NysGlobalHeader","LitElement","container","links","currentUrl","bestMatch","a","linkPath","slot","assignedNodes","node","containerMobile","nodeInline","nodeMobile","href","event","ahref","li","html","unsafeCSS","styles","NysGlobalHeader","__decorateClass","property","state"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"nys-globalheader.js","sources":["../src/nys-globalheader.ts"],"sourcesContent":["import { LitElement, html, unsafeCSS } from \"lit\";\nimport { property, state } from \"lit/decorators.js\";\n// @ts-ignore: SCSS module imported via bundler as inline\nimport styles from \"./nys-globalheader.scss?inline\";\n\n/**\n * Agency-branded header with app/agency name, navigation, and responsive mobile menu.\n *\n * Place below `nys-unavheader`. Slot navigation links as `<ul><li><a>` elements; active links\n * are auto-highlighted based on current URL. Mobile menu toggles automatically on narrow screens.\n *\n * @summary Agency header with navigation, mobile menu, and active link highlighting.\n * @element nys-globalheader\n *\n * @slot - Navigation content (typically `<ul>` with `<li><a>` links). Auto-sanitized.\n *\n * @example Basic header\n * ```html\n * <nys-globalheader appName=\"My App\" agencyName=\"Department of Health\" homepageLink=\"/\">\n * <ul><li><a href=\"/about\">About</a></li><li><a href=\"/contact\">Contact</a></li></ul>\n * </nys-globalheader>\n * ```\n */\n\nexport class NysGlobalHeader extends LitElement {\n static styles = unsafeCSS(styles);\n\n /** Application name displayed prominently. */\n @property({ type: String }) appName = \"\";\n\n /** Agency name displayed below app name (or as main title if no appName). */\n @property({ type: String }) agencyName = \"\";\n\n /** URL for the header title link. If empty, title is not clickable. */\n @property({ type: String }) homepageLink = \"\";\n @state() private isMobileMenuOpen = false;\n @state() private hasLinkContent = false;\n\n /**\n * Lifecycle Methods\n * --------------------------------------------------------------------------\n */\n\n firstUpdated() {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>(\"slot\");\n slot?.addEventListener(\"slotchange\", () => this._handleListSlotChange());\n this._handleListSlotChange(); // run once at startup\n\n this._listenLinkClicks();\n }\n\n /**\n * Functions\n * --------------------------------------------------------------------------\n */\n\n private _highlightActiveLink(container: HTMLElement) {\n const links = Array.from(container.querySelectorAll(\"a\"));\n const currentUrl = window.location.pathname.replace(/\\/+$/, \"\") || \"/\";\n\n let bestMatch: { li: HTMLElement | null; length: number } = {\n li: null,\n length: 0,\n };\n\n links.forEach((a) => {\n const linkPath = this._normalizePath(a.getAttribute(\"href\"));\n if (!linkPath) return;\n\n if (linkPath === \"/\" && currentUrl === \"/\") {\n bestMatch = { li: a.closest(\"li\"), length: 1 };\n } else if (\n currentUrl.startsWith(linkPath) &&\n linkPath.length > bestMatch.length\n ) {\n bestMatch = {\n li: a.closest(\"li\"),\n length: linkPath.length,\n };\n }\n });\n\n // Clear all previous actives\n links.forEach((a) => a.closest(\"li\")?.classList.remove(\"active\"));\n\n // Apply best match\n bestMatch.li?.classList.add(\"active\");\n }\n\n // Gets called when the slot content changes and directly appends the slotted elements into the shadow DOM\n private async _handleListSlotChange() {\n const slot = this.shadowRoot?.querySelector(\n 'slot:not([name=\"user-actions\"])',\n ) as HTMLSlotElement | null;\n\n if (!slot) return;\n\n const assignedNodes = slot\n .assignedNodes({ flatten: true })\n .filter((node) => node.nodeType === Node.ELEMENT_NODE) as Element[]; // Filter to elements only\n\n this.hasLinkContent = assignedNodes.length > 0;\n\n await Promise.resolve(); // Wait for current update cycle to complete before modifying reactive state (solves the lit issue \"scheduled an update\")\n\n // Get the containers to append the slotted elements\n const container = this.shadowRoot?.querySelector(\n \".nys-globalheader__content\",\n ) as HTMLElement | null;\n\n const containerMobile = this.shadowRoot?.querySelector(\n \".nys-globalheader__content-mobile\",\n ) as HTMLElement | null;\n\n if (!container || !containerMobile) return;\n\n // Clear existing children in the container\n container.innerHTML = \"\";\n containerMobile.innerHTML = \"\";\n\n // Clone and append slotted elements into the shadow DOM container\n assignedNodes.forEach((node) => {\n if (node instanceof HTMLElement) {\n // need to clone the node because cannot have same node in two places in DOM\n const nodeInline = node.cloneNode(true) as HTMLElement;\n const nodeMobile = node.cloneNode(true) as HTMLElement;\n\n container.appendChild(nodeInline);\n containerMobile.appendChild(nodeMobile);\n }\n });\n\n // Highlight active links AFTER DOM is finalized\n this._highlightActiveLink(container);\n this._highlightActiveLink(containerMobile);\n }\n\n // Normalize paths so that links like \"name\", \"/name/\", and \"/\" match window.location.pathname.\n // This ensures consistent active-link behavior regardless of how hrefs are written.\n private _normalizePath(href: string | null): string | null {\n if (!href) return null;\n\n try {\n const url = new URL(href, window.location.origin);\n return url.pathname.replace(/\\/+$/, \"\") || \"/\";\n } catch {\n return null;\n }\n }\n\n private _toggleMobileMenu() {\n this.isMobileMenuOpen = !this.isMobileMenuOpen;\n }\n\n // Listens for click events on links to mark them active\n private _listenLinkClicks() {\n const containers = this.shadowRoot?.querySelectorAll(\n \".nys-globalheader__content, .nys-globalheader__content-mobile\",\n );\n\n containers?.forEach((container) => {\n container?.addEventListener(\"click\", (event) => {\n const target = event.target as HTMLElement;\n const ahref = target.closest(\"a\");\n\n if (!ahref) return;\n\n // Clear all existing active <li>\n container\n .querySelectorAll(\"li.active\")\n .forEach((li) => li.classList.remove(\"active\"));\n\n // Set active on the clicked link's <li>\n const li = ahref.closest(\"li\");\n if (li) li.classList.add(\"active\");\n });\n });\n }\n\n render() {\n return html`\n <header class=\"nys-globalheader\">\n <div class=\"nys-globalheader__main-container\">\n ${this.hasLinkContent\n ? html` <div class=\"nys-globalheader__button-container\">\n <button\n class=\"nys-globalheader__mobile-menu-button\"\n @click=\"${this._toggleMobileMenu}\"\n >\n <nys-icon\n name=\"${this.isMobileMenuOpen ? \"close\" : \"menu\"}\"\n size=\"32\"\n label=\"${this.isMobileMenuOpen ? \"close\" : \"menu\"} icon\"\n ></nys-icon>\n <span class=\"nys-globalheader__mobile-menu-button-text\"\n >${this.isMobileMenuOpen ? \"CLOSE\" : \"MENU\"}</span\n >\n </button>\n </div>`\n : \"\"}\n ${!this.homepageLink?.trim()\n ? html`\n <div class=\"nys-globalheader__name-container\">\n ${this.appName?.trim().length > 0\n ? html`<div\n class=\"nys-globalheader__appName nys-globalheader__name\"\n >\n ${this.appName}\n </div> `\n : \"\"}\n ${this.agencyName?.trim().length > 0\n ? html`<div\n class=\"nys-globalheader__agencyName nys-globalheader__name ${this.appName?.trim()\n .length > 0\n ? \"\"\n : \"main\"}\"\n >\n ${this.agencyName}\n </div> `\n : \"\"}\n </div>\n `\n : html`<a\n class=\"nys-globalheader__name-container-link\"\n href=${this.homepageLink?.trim()}\n >\n <div class=\"nys-globalheader__name-container\">\n ${this.appName?.trim().length > 0\n ? html`<div\n class=\"nys-globalheader__appName nys-globalheader__name\"\n >\n ${this.appName}\n </div> `\n : \"\"}\n ${this.agencyName?.trim().length > 0\n ? html`<div\n class=\"nys-globalheader__agencyName nys-globalheader__name ${this.appName?.trim()\n .length > 0\n ? \"\"\n : \"main\"}\"\n >\n ${this.agencyName}\n </div> `\n : \"\"}\n </div>\n </a>`}\n <div class=\"nys-globalheader__content\"></div>\n <slot\n style=\"display: none;\"\n @slotchange=\"${this._handleListSlotChange}\"\n ></slot>\n <slot name=\"user-actions\"></slot>\n </div>\n </header>\n <div\n class=\"nys-globalheader__content-mobile ${this.isMobileMenuOpen\n ? \"\"\n : \"close\"}\"\n ></div>\n `;\n }\n}\n\nif (!customElements.get(\"nys-globalheader\")) {\n customElements.define(\"nys-globalheader\", NysGlobalHeader);\n}\n"],"names":["_NysGlobalHeader","LitElement","container","links","currentUrl","bestMatch","a","linkPath","slot","assignedNodes","node","containerMobile","nodeInline","nodeMobile","href","event","ahref","li","html","unsafeCSS","styles","NysGlobalHeader","__decorateClass","property","state"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nysds/nys-globalheader",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.0",
|
|
4
4
|
"description": "The Globalheader component from the NYS Design System.",
|
|
5
5
|
"module": "dist/nys-globalheader.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"lit-analyze": "lit-analyzer '**/*.ts'"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@nysds/nys-icon": "^1.
|
|
26
|
+
"@nysds/nys-icon": "^1.15.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"lit": "^3.3.1",
|