@nysds/nys-globalfooter 1.19.4 → 1.20.1

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.
@@ -0,0 +1 @@
1
+ export * from "./nys-globalfooter";
@@ -0,0 +1,135 @@
1
+ import { NysElement } from "@nysds/internals";
2
+ import "@nysds/nys-divider";
3
+ /**
4
+ * Agency-branded footer with agency name and slotted content sections. Auto-layouts based on content structure.
5
+ *
6
+ * Place above `nys-unavfooter`. Slot contact info, links, or other content. Use `<h4>` elements
7
+ * to create multi-column layouts; without `<h4>`, renders as compact single section.
8
+ *
9
+ * @summary Agency footer with auto-layout for contact info and link sections.
10
+ * @element nys-globalfooter
11
+ *
12
+ * @slot - Footer content (links, contact info). Use `<h4>` for column headings.
13
+ *
14
+ * @example Basic
15
+ * ```html
16
+ * <nys-globalfooter agencyName="Office of Information Technology Services"></nys-globalfooter>
17
+ * ```
18
+ *
19
+ * @example Homepage Link
20
+ * ```html
21
+ * <nys-globalfooter
22
+ * agencyName="Office of Information Technology Services"
23
+ * homepageLink="https://its.ny.gov"
24
+ * ></nys-globalfooter>
25
+ * ```
26
+ *
27
+ * @example Menu Links
28
+ * ```html
29
+ * <nys-globalfooter agencyName="Office of Information Technology Services">
30
+ * <ul>
31
+ * <li><a href="https://its.ny.gov">ITS Home</a></li>
32
+ * <li><a href="https://its.ny.gov/about-us">About ITS</a></li>
33
+ * </ul>
34
+ * </nys-globalfooter>
35
+ * ```
36
+ *
37
+ * @example Column Links
38
+ * ```html
39
+ * <nys-globalfooter agencyName="Office of Information Technology Services">
40
+ * <ul>
41
+ * <li>
42
+ * <span>About</span>
43
+ * <ul>
44
+ * <li><a href="https://its.ny.gov/about-us">About ITS</a></li>
45
+ * <li><a href="https://its.ny.gov/contact-us">Contact</a></li>
46
+ * <li><a href="https://its.ny.gov/policies">Policies</a></li>
47
+ * </ul>
48
+ * </li>
49
+ * <li>
50
+ * <span>Resources</span>
51
+ * <ul>
52
+ * <li><a href="https://its.ny.gov/resources">Developer Tools</a></li>
53
+ * <li><a href="https://its.ny.gov/accessibility">Accessibility</a></li>
54
+ * <li><a href="https://its.ny.gov/privacy">Privacy</a></li>
55
+ * </ul>
56
+ * </li>
57
+ * </ul>
58
+ * </nys-globalfooter>
59
+ * ```
60
+ *
61
+ * @example Subheading
62
+ * ```html
63
+ * <nys-globalfooter
64
+ * agencyName="Office of Information Technology Services"
65
+ * agencySubheading="Innovating Technology for a Better New York"
66
+ * ></nys-globalfooter>
67
+ * ```
68
+ *
69
+ * @example Custom landmark label
70
+ * ```html
71
+ * <!-- Names the contentinfo landmark directly instead of from the visible
72
+ * heading. Keep it distinct from nys-unavfooter's ("New York State"). -->
73
+ * <nys-globalfooter
74
+ * agencyName="Office of Information Technology Services"
75
+ * landmarkLabel="ITS"
76
+ * ></nys-globalfooter>
77
+ * ```
78
+ */
79
+ export declare class NysGlobalFooter extends NysElement {
80
+ static styles: import("lit").CSSResult;
81
+ /** Unique identifier. Auto-generated if not provided. */
82
+ id: string;
83
+ /** Agency name displayed as the footer heading. */
84
+ agencyName: string;
85
+ /** Optional subheading displayed below the agency name. */
86
+ agencySubheading: string;
87
+ /** URL for the agency name link. If empty, name is not clickable. */
88
+ homepageLink: string;
89
+ /**
90
+ * Accessible name for the `contentinfo` landmark this footer renders.
91
+ *
92
+ * Leave it unset and the landmark is named after the visible `agencyName`
93
+ * heading, which cannot drift out of sync and is translated with the rest of
94
+ * the page. Falls back to `"Site"` when there is no agency name to reference.
95
+ *
96
+ * Set this only when the agency name is not right for your audience. A page
97
+ * pairing this with `nys-unavfooter` carries two `contentinfo` landmarks, so
98
+ * the name must stay distinct from that footer's (`"New York State"` by
99
+ * default) or landmark navigation stops distinguishing them.
100
+ *
101
+ * An explicit name replaces the reference to the visible heading.
102
+ */
103
+ landmarkLabel: string;
104
+ private slotHasContent;
105
+ /**
106
+ * Lifecycle Methods
107
+ * --------------------------------------------------------------------------
108
+ */
109
+ firstUpdated(): void;
110
+ /**
111
+ * Functions
112
+ * --------------------------------------------------------------------------
113
+ */
114
+ private _handleSlotChange;
115
+ /**
116
+ * Id of the heading that names the contentinfo landmark, or undefined when no
117
+ * agency name was given.
118
+ *
119
+ * The documented pairing puts this footer above `nys-unavfooter`, which leaves a
120
+ * page with two `contentinfo` landmarks. Pointing at the visible heading rather
121
+ * than repeating the agency name in an `aria-label` keeps the two in sync and lets
122
+ * the name be translated along with the rest of the page.
123
+ */
124
+ private get _contentinfoLabelledBy();
125
+ /** The author's landmark name, or undefined when they gave none. */
126
+ private get _landmarkLabelOverride();
127
+ /**
128
+ * Literal name for the contentinfo: the author's override, or the "Site"
129
+ * default when there is no agency heading to reference. Undefined whenever
130
+ * `_contentinfoLabelledBy` has something to point at, so the landmark never
131
+ * carries both.
132
+ */
133
+ private get _contentinfoLabel();
134
+ render(): import("lit-html").TemplateResult<1>;
135
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,29 +1,60 @@
1
- import { LitElement as h, unsafeCSS as b, nothing as _, html as i } from "lit";
2
- import { property as f, state as m } from "lit/decorators.js";
1
+ import { LitElement as b, unsafeCSS as _, html as f, nothing as m } from "lit";
2
+ import { property as y, state as v } from "lit/decorators.js";
3
+ import { ifDefined as c } from "lit/directives/if-defined.js";
4
+ import "@nysds/nys-divider";
3
5
  /*!
4
6
  * █▄ █ █ █ █▀▀▀█ █▀▀▄ █▀▀▀█
5
7
  * █ █ █ █▄▄▄█ ▀▀▀▄▄ █ █ ▀▀▀▄▄
6
8
  * █ ▀█ █ █▄▄▄█ █▄▄▀ █▄▄▄█
7
9
  *
8
- * Global Footer Component v1.19.4
10
+ * Global Footer Component v1.20.1
9
11
  * Part of the New York State Design System
10
12
  * Repository: https://github.com/its-hcd/nysds
11
13
  * License: MIT
12
14
  */
13
- const v = ':host{--_nys-globalfooter-color: var( --nys-color-text, var(--nys-color-neutral-900, #1b1b1b) );--_nys-globalfooter-background-color: var( --nys-color-theme-weaker, var(--nys-color-state-blue-50, #eff6fb) );--_nys-globalfooter-gap: var(--nys-space-300, 24px);--_nys-globalfooter-gap--heading-container: var(--nys-space-150, 12px);--_nys-globalfooter-padding--y: var(--nys-space-400, 32px);--_nys-globalfooter-padding--gutter: var(--nys-gutter-sm, 20px);--_nys-globalfooter-font-size--agency: var( --nys-font-size-agency-xl, var(--nys-font-size-2xl, 22px) );--_nys-globalfooter-font-size--link: var( --nys-font-size-body-md, var(--nys-font-size-md, 16px) );--_nys-globalfooter-line-height--agency: normal;--_nys-globalfooter-font-weight--regular: var(--nys-font-weight-regular, 400);--_nys-globalfooter-font-weight--semibold: var( --nys-font-weight-semibold, 600 );--_nys-globalfooter-max-width: var(--nys-max-content-width, 1280px);--_nys-globalfooter-font-family--agency: var( --nys-font-family-agency, "D Sari", Arial, sans-serif );--_nys-globalfooter-column-gap: var(--nys-space-400, 32px);--_nys-globalfooter-row-gap: var(--nys-space-400, 32px);--_nys-globalfooter-line-height--link: var(--nys-font-lineheight-ui-md, 24px);--_nys-globalfooter-letter-spacing: var( --nys-font-letterspacing-ui-md, var(--nys-font-letterspacing-400, .044px) );--_nys-globalfooter-font-family--link: var( --nys-font-family-ui, var( --nys-font-family-sans, "Proxima Nova", "Helvetica Neue", "Helvetica", "Arial", sans-serif ) );--_nys-globalfooter-text-decoration-thickness: var(--nys-size-2px, 2px);--_nys-globalfooter-background--divider: var(--nys-color-theme, #154973);--_nys-globalfooter-margin--divider: var(--nys-space-50, 4px)}.nys-globalfooter{display:flex;padding:var(--_nys-globalfooter-padding--y) var(--_nys-globalfooter-padding--gutter);justify-content:center;background-color:var(--_nys-globalfooter-background-color);color:var(--_nys-globalfooter-color);width:100%;box-sizing:border-box}.nys-globalfooter .nys-globalfooter__main-container{display:flex;flex-direction:column;gap:var(--_nys-globalfooter-gap);width:100%;max-width:var(--_nys-globalfooter-max-width)}.nys-globalfooter .nys-globalfooter__main-container .nys-globalfooter__heading-container{display:flex;flex-direction:column;gap:var(--_nys-globalfooter-gap--heading-container)}.nys-globalfooter .nys-globalfooter__main-container .nys-globalfooter__heading-container .nys-globalfooter__name{text-align:left;margin:0;color:var(--_nys-globalfooter-color);font-family:var(--_nys-globalfooter-font-family--agency);font-size:var(--_nys-globalfooter-font-size--agency);font-style:normal;font-weight:var(--_nys-globalfooter-font-weight--semibold);line-height:var(--_nys-globalfooter-line-height--agency);letter-spacing:normal}.nys-globalfooter .nys-globalfooter__main-container .nys-globalfooter__heading-container .nys-globalfooter__subheading{margin:0}ul{list-style-type:none;padding:0;margin:0}li{margin:0;padding:0}a,span{color:var(--_nys-globalfooter-color);text-decoration:none;font-family:var(--_nys-globalfooter-font-family--link);font-size:var(--_nys-globalfooter-font-size--link);font-style:normal;font-weight:var(--_nys-globalfooter-font-weight--semibold);line-height:var(--_nys-globalfooter-line-height--link);letter-spacing:var(--_nys-globalfooter-letter-spacing)}ul li>span+ul li a{font-weight:var(--_nys-globalfooter-font-weight--regular)}a:hover{text-decoration:underline}a:active{text-decoration-thickness:var(--_nys-globalfooter-text-decoration-thickness)}.nys-globalfooter__content{width:100%}.nys-globalfooter__content ul{display:flex;flex-flow:column wrap;gap:var(--_nys-globalfooter-row-gap) var(--_nys-globalfooter-column-gap)}.nys-globalfooter__content ul li:has(span~ul){flex:1;display:flex;flex-direction:column}.nys-globalfooter__content ul:has(li>span~ul){--_nys-globalfooter-column-gap: var(--nys-space-500, 40px)}.nys-globalfooter__content ul li>span~ul{display:flex;flex-direction:column;gap:var(--nys-space-200, 16px)}.divider{margin-top:var(--_nys-globalfooter-margin--divider);margin-bottom:var(--nys-space-300, 24px)}@media(min-width:768px){.nys-globalfooter__content ul{flex-direction:row}.nys-globalfooter__content ul li:has(span~ul){flex:1 0 205px}:host{--_nys-globalfooter-padding--gutter: var(--nys-gutter-lg, 32px);--_nys-globalfooter-row-gap: var(--nys-space-600, 48px)}}@media(min-width:1280px){:host{--_nys-globalfooter-padding--gutter: var(--nys-gutter-xl, 64px)}}';
14
- var u = Object.defineProperty, g = (c, l, s, t) => {
15
- for (var o = void 0, e = c.length - 1, n; e >= 0; e--)
16
- (n = c[e]) && (o = n(l, s, o) || o);
17
- return o && u(l, s, o), o;
15
+ /*!
16
+ * New York State Design System v1.20.1
17
+ * Description: A design system for New York State's digital products.
18
+ * Repository: https://github.com/its-hcd/nysds
19
+ * License: MIT
20
+ */
21
+ let u = 0;
22
+ function x(s) {
23
+ return `${s}-${Date.now()}-${u++}`;
24
+ }
25
+ const w = (s) => {
26
+ class o extends s {
27
+ get idPrefix() {
28
+ return this.localName || "nys-element";
29
+ }
30
+ ensureId() {
31
+ this.id || (this.id = x(this.idPrefix));
32
+ }
33
+ connectedCallback() {
34
+ super.connectedCallback(), this.ensureId();
35
+ }
36
+ }
37
+ return o;
38
+ }, L = /* @__PURE__ */ w(b), k = ':host{--_nys-globalfooter-color: var( --nys-color-text, var(--nys-color-neutral-900, #1b1b1b) );--_nys-globalfooter-background-color: var( --nys-color-theme-weaker, var(--nys-color-state-blue-50, #eff6fb) );--_nys-globalfooter-gap: var(--nys-space-300, 24px);--_nys-globalfooter-gap--heading-container: var(--nys-space-150, 12px);--_nys-globalfooter-padding--y: var(--nys-space-400, 32px);--_nys-globalfooter-padding--gutter: var(--nys-gutter-sm, 20px);--_nys-globalfooter-font-size--agency: var( --nys-font-size-agency-xl, var(--nys-font-size-2xl, 22px) );--_nys-globalfooter-font-size--link: var( --nys-font-size-body-md, var(--nys-font-size-md, 16px) );--_nys-globalfooter-line-height--agency: normal;--_nys-globalfooter-font-weight--regular: var(--nys-font-weight-regular, 400);--_nys-globalfooter-font-weight--semibold: var( --nys-font-weight-semibold, 600 );--_nys-globalfooter-max-width--content: var( --nys-globalfooter-max-width--content, 1280px );--_nys-globalfooter-font-family--agency: var( --nys-font-family-agency, "D Sari", Arial, sans-serif );--_nys-globalfooter-column-gap: var(--nys-space-400, 32px);--_nys-globalfooter-row-gap: var(--nys-space-400, 32px);--_nys-globalfooter-line-height--link: var(--nys-font-lineheight-ui-md, 24px);--_nys-globalfooter-letter-spacing: var( --nys-font-letterspacing-ui-md, var(--nys-font-letterspacing-400, .044px) );--_nys-globalfooter-font-family--link: var( --nys-font-family-ui, var( --nys-font-family-sans, "Proxima Nova", "Helvetica Neue", "Helvetica", "Arial", sans-serif ) );--_nys-globalfooter-text-decoration-thickness: var(--nys-size-2px, 2px);--_nys-globalfooter-background--divider: var(--nys-color-theme, #154973);--_nys-globalfooter-margin--divider: var(--nys-space-50, 4px)}.nys-globalfooter{display:flex;padding:var(--_nys-globalfooter-padding--y) var(--_nys-globalfooter-padding--gutter);justify-content:center;background-color:var(--_nys-globalfooter-background-color);color:var(--_nys-globalfooter-color);width:100%;box-sizing:border-box}.nys-globalfooter .nys-globalfooter__main-container{display:flex;flex-direction:column;gap:var(--_nys-globalfooter-gap);width:100%;max-width:var(--_nys-globalfooter-max-width--content)}.nys-globalfooter .nys-globalfooter__main-container .nys-globalfooter__heading-container{display:flex;flex-direction:column;gap:var(--_nys-globalfooter-gap--heading-container)}.nys-globalfooter .nys-globalfooter__main-container .nys-globalfooter__heading-container .nys-globalfooter__name{text-align:left;margin:0;color:var(--_nys-globalfooter-color);font-family:var(--_nys-globalfooter-font-family--agency);font-size:var(--_nys-globalfooter-font-size--agency);font-style:normal;font-weight:var(--_nys-globalfooter-font-weight--semibold);line-height:var(--_nys-globalfooter-line-height--agency);letter-spacing:normal}.nys-globalfooter .nys-globalfooter__main-container .nys-globalfooter__heading-container .nys-globalfooter__subheading{margin:0}ul{list-style-type:none;padding:0;margin:0}li{margin:0;padding:0}a,span{color:var(--_nys-globalfooter-color);text-decoration:none;font-family:var(--_nys-globalfooter-font-family--link);font-size:var(--_nys-globalfooter-font-size--link);font-style:normal;font-weight:var(--_nys-globalfooter-font-weight--semibold);line-height:var(--_nys-globalfooter-line-height--link);letter-spacing:var(--_nys-globalfooter-letter-spacing)}ul li>span+ul li a{font-weight:var(--_nys-globalfooter-font-weight--regular)}a:hover{text-decoration:underline}a:active{text-decoration-thickness:var(--_nys-globalfooter-text-decoration-thickness)}.nys-globalfooter__content{width:100%}.nys-globalfooter__content ul{display:flex;flex-flow:column wrap;gap:var(--_nys-globalfooter-row-gap) var(--_nys-globalfooter-column-gap)}.nys-globalfooter__content ul li:has(span~ul){flex:1;display:flex;flex-direction:column}.nys-globalfooter__content ul:has(li>span~ul){--_nys-globalfooter-column-gap: var(--nys-space-500, 40px)}.nys-globalfooter__content ul li>span~ul{display:flex;flex-direction:column;gap:var(--nys-space-200, 16px)}.divider{margin-top:var(--_nys-globalfooter-margin--divider);margin-bottom:var(--nys-space-300, 24px)}@media(min-width:768px){.nys-globalfooter__content ul{flex-direction:row}.nys-globalfooter__content ul li:has(span~ul){flex:1 0 205px}:host{--_nys-globalfooter-padding--gutter: var(--nys-gutter-lg, 32px);--_nys-globalfooter-row-gap: var(--nys-space-600, 48px)}}@media(min-width:1280px){:host{--_nys-globalfooter-padding--gutter: var(--nys-gutter-xl, 64px)}}';
39
+ var S = Object.defineProperty, i = (s, o, l, r) => {
40
+ for (var e = void 0, n = s.length - 1, a; n >= 0; n--)
41
+ (a = s[n]) && (e = a(o, l, e) || e);
42
+ return e && S(o, l, e), e;
18
43
  };
19
- const y = class y extends h {
44
+ const E = "Site", d = class d extends L {
20
45
  constructor() {
21
- super(...arguments), this.agencyName = "", this.agencySubheading = "", this.homepageLink = "", this.slotHasContent = !0;
46
+ super(...arguments), this.id = "", this.agencyName = "", this.agencySubheading = "", this.homepageLink = "", this.landmarkLabel = "", this.slotHasContent = !0;
22
47
  }
23
48
  /**
24
49
  * Lifecycle Methods
25
50
  * --------------------------------------------------------------------------
26
51
  */
52
+ // No connectedCallback override is needed here: NysElement's own
53
+ // connectedCallback already assigns an id when one is not provided, and it runs
54
+ // automatically because this class does not override it. The contentinfo landmark
55
+ // intentionally stays on the inner <footer> element (and is given an accessible
56
+ // name from the agency name so multiple footers on a page are distinguishable),
57
+ // so this component keeps defaultRole = null and does not move a role onto the host.
27
58
  firstUpdated() {
28
59
  this.shadowRoot?.querySelector("slot")?.addEventListener("slotchange", () => this._handleSlotChange()), this._handleSlotChange();
29
60
  }
@@ -33,40 +64,74 @@ const y = class y extends h {
33
64
  */
34
65
  // Gets called when the slot content changes and directly appends the slotted elements into the shadow DOM
35
66
  async _handleSlotChange() {
36
- const l = this.shadowRoot?.querySelector("slot");
37
- if (!l) return;
38
- const s = l?.assignedNodes({ flatten: !0 }).filter((e) => e.nodeType === Node.ELEMENT_NODE);
39
- await Promise.resolve(), this.slotHasContent = s.length > 0;
40
- const t = this.shadowRoot?.querySelector(
67
+ const o = this.shadowRoot?.querySelector("slot");
68
+ if (!o) return;
69
+ const l = o?.assignedNodes({ flatten: !0 }).filter((n) => n.nodeType === Node.ELEMENT_NODE);
70
+ await Promise.resolve(), this.slotHasContent = l.length > 0;
71
+ const r = this.shadowRoot?.querySelector(
41
72
  ".nys-globalfooter__content"
42
- ), o = s?.some(
43
- (e) => e.tagName === "H4"
73
+ ), e = l?.some(
74
+ (n) => n.tagName === "H4"
44
75
  );
45
- t && (t.classList.toggle("columns", o), t.classList.toggle("small", !o), t.innerHTML = "", s.forEach((n) => {
46
- if (n.nodeType === Node.ELEMENT_NODE) {
47
- const r = n.cloneNode(!0);
48
- ["script", "iframe", "object", "embed", "img"].forEach((d) => {
49
- r.querySelectorAll(d).forEach((p) => p.remove());
50
- }), t.appendChild(r), n.remove();
76
+ r && (r.classList.toggle("columns", e), r.classList.toggle("small", !e), r.innerHTML = "", l.forEach((a) => {
77
+ if (a.nodeType === Node.ELEMENT_NODE) {
78
+ const g = a.cloneNode(!0);
79
+ ["script", "iframe", "object", "embed", "img"].forEach((h) => {
80
+ g.querySelectorAll(h).forEach((p) => p.remove());
81
+ }), r.appendChild(g), a.remove();
51
82
  }
52
- }), t.querySelectorAll("span").forEach((n) => {
53
- const r = document.createElement("nys-divider");
54
- r.classList.add("divider"), n.insertAdjacentElement("afterend", r);
83
+ }), r.querySelectorAll("span").forEach((a) => {
84
+ const g = document.createElement("nys-divider");
85
+ g.classList.add("divider"), a.insertAdjacentElement("afterend", g);
55
86
  }));
56
87
  }
88
+ /**
89
+ * Id of the heading that names the contentinfo landmark, or undefined when no
90
+ * agency name was given.
91
+ *
92
+ * The documented pairing puts this footer above `nys-unavfooter`, which leaves a
93
+ * page with two `contentinfo` landmarks. Pointing at the visible heading rather
94
+ * than repeating the agency name in an `aria-label` keeps the two in sync and lets
95
+ * the name be translated along with the rest of the page.
96
+ */
97
+ get _contentinfoLabelledBy() {
98
+ if (!this._landmarkLabelOverride)
99
+ return this.agencyName?.trim() ? `${this.id}-name` : void 0;
100
+ }
101
+ /** The author's landmark name, or undefined when they gave none. */
102
+ get _landmarkLabelOverride() {
103
+ return this.landmarkLabel?.trim() || void 0;
104
+ }
105
+ /**
106
+ * Literal name for the contentinfo: the author's override, or the "Site"
107
+ * default when there is no agency heading to reference. Undefined whenever
108
+ * `_contentinfoLabelledBy` has something to point at, so the landmark never
109
+ * carries both.
110
+ */
111
+ get _contentinfoLabel() {
112
+ return this._landmarkLabelOverride ? this._landmarkLabelOverride : this._contentinfoLabelledBy ? void 0 : E;
113
+ }
57
114
  render() {
58
- return i`
59
- <footer class="nys-globalfooter">
115
+ const o = f`<h2
116
+ id="${this.id}-name"
117
+ class="nys-globalfooter__name"
118
+ >
119
+ ${this.agencyName}
120
+ </h2>`;
121
+ return f`
122
+ <footer
123
+ class="nys-globalfooter"
124
+ aria-labelledby=${c(this._contentinfoLabelledBy)}
125
+ aria-label=${c(this._contentinfoLabel)}
126
+ >
60
127
  <div class="nys-globalfooter__main-container">
61
128
  <div class="nys-globalfooter__heading-container">
62
- ${this.homepageLink?.trim() ? i`<a href=${this.homepageLink?.trim()}>
63
- <h2 class="nys-globalfooter__name">${this.agencyName}</h2>
64
- </a>` : i`<h2 class="nys-globalfooter__name">${this.agencyName}</h2>`}
65
- ${this.agencySubheading ? i`<p class="nys-globalfooter__subheading">
129
+ ${this.homepageLink?.trim() ? f`<a href=${this.homepageLink?.trim()}>${o}</a>` : o}
130
+ ${this.agencySubheading ? f`<p class="nys-globalfooter__subheading">
66
131
  ${this.agencySubheading}
67
- </p>` : _}
132
+ </p>` : m}
68
133
  </div>
69
- ${this.slotHasContent ? i`<div class="nys-globalfooter__content">
134
+ ${this.slotHasContent ? f`<div class="nys-globalfooter__content">
70
135
  <slot
71
136
  style="display: hidden"
72
137
  @slotchange="${this._handleSlotChange}"
@@ -77,22 +142,28 @@ const y = class y extends h {
77
142
  `;
78
143
  }
79
144
  };
80
- y.styles = b(v);
81
- let a = y;
82
- g([
83
- f({ type: String })
84
- ], a.prototype, "agencyName");
85
- g([
86
- f({ type: String })
87
- ], a.prototype, "agencySubheading");
88
- g([
89
- f({ type: String })
90
- ], a.prototype, "homepageLink");
91
- g([
92
- m()
93
- ], a.prototype, "slotHasContent");
94
- customElements.get("nys-globalfooter") || customElements.define("nys-globalfooter", a);
145
+ d.styles = _(k);
146
+ let t = d;
147
+ i([
148
+ y({ type: String, reflect: !0 })
149
+ ], t.prototype, "id");
150
+ i([
151
+ y({ type: String })
152
+ ], t.prototype, "agencyName");
153
+ i([
154
+ y({ type: String })
155
+ ], t.prototype, "agencySubheading");
156
+ i([
157
+ y({ type: String })
158
+ ], t.prototype, "homepageLink");
159
+ i([
160
+ y({ type: String })
161
+ ], t.prototype, "landmarkLabel");
162
+ i([
163
+ v()
164
+ ], t.prototype, "slotHasContent");
165
+ customElements.get("nys-globalfooter") || customElements.define("nys-globalfooter", t);
95
166
  export {
96
- a as NysGlobalFooter
167
+ t as NysGlobalFooter
97
168
  };
98
169
  //# sourceMappingURL=nys-globalfooter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"nys-globalfooter.js","sources":["../src/nys-globalfooter.ts"],"sourcesContent":["import { LitElement, html, unsafeCSS, nothing } from \"lit\";\nimport { property, state } from \"lit/decorators.js\";\n// @ts-ignore: SCSS module imported via bundler as inline\nimport styles from \"./nys-globalfooter.scss?inline\";\n\n/**\n * Agency-branded footer with agency name and slotted content sections. Auto-layouts based on content structure.\n *\n * Place above `nys-unavfooter`. Slot contact info, links, or other content. Use `<h4>` elements\n * to create multi-column layouts; without `<h4>`, renders as compact single section.\n *\n * @summary Agency footer with auto-layout for contact info and link sections.\n * @element nys-globalfooter\n *\n * @slot - Footer content (links, contact info). Use `<h4>` for column headings.\n *\n * @example Basic\n * ```html\n * <nys-globalfooter agencyName=\"Office of Information Technology Services\"></nys-globalfooter>\n * ```\n *\n * @example Homepage Link\n * ```html\n * <nys-globalfooter\n * agencyName=\"Office of Information Technology Services\"\n * homepageLink=\"https://its.ny.gov\"\n * ></nys-globalfooter>\n * ```\n *\n * @example Menu Links\n * ```html\n * <nys-globalfooter agencyName=\"Office of Information Technology Services\">\n * <ul>\n * <li><a href=\"https://its.ny.gov\">ITS Home</a></li>\n * <li><a href=\"https://its.ny.gov/about-us\">About ITS</a></li>\n * </ul>\n * </nys-globalfooter>\n * ```\n *\n * @example Column Links\n * ```html\n * <nys-globalfooter agencyName=\"Office of Information Technology Services\">\n * <ul>\n * <li>\n * <span>About</span>\n * <ul>\n * <li><a href=\"https://its.ny.gov/about-us\">About ITS</a></li>\n * <li><a href=\"https://its.ny.gov/contact-us\">Contact</a></li>\n * <li><a href=\"https://its.ny.gov/policies\">Policies</a></li>\n * </ul>\n * </li>\n * <li>\n * <span>Resources</span>\n * <ul>\n * <li><a href=\"https://its.ny.gov/resources\">Developer Tools</a></li>\n * <li><a href=\"https://its.ny.gov/accessibility\">Accessibility</a></li>\n * <li><a href=\"https://its.ny.gov/privacy\">Privacy</a></li>\n * </ul>\n * </li>\n * </ul>\n * </nys-globalfooter>\n * ```\n *\n * @example Subheading\n * ```html\n * <nys-globalfooter\n * agencyName=\"Office of Information Technology Services\"\n * agencySubheading=\"Innovating Technology for a Better New York\"\n * ></nys-globalfooter>\n * ```\n */\n\nexport class NysGlobalFooter extends LitElement {\n static styles = unsafeCSS(styles);\n\n /** Agency name displayed as the footer heading. */\n @property({ type: String }) agencyName = \"\";\n\n /** Optional subheading displayed below the agency name. */\n @property({ type: String }) agencySubheading = \"\";\n\n /** URL for the agency name link. If empty, name is not clickable. */\n @property({ type: String }) homepageLink = \"\";\n @state() private slotHasContent = true;\n\n /**\n * Lifecycle Methods\n * --------------------------------------------------------------------------\n */\n\n firstUpdated() {\n // Check for slot content after rendering\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>(\"slot\");\n slot?.addEventListener(\"slotchange\", () => this._handleSlotChange());\n this._handleSlotChange(); // Initial check\n }\n\n /**\n * Functions\n * --------------------------------------------------------------------------\n */\n\n // Gets called when the slot content changes and directly appends the slotted elements into the shadow DOM\n private async _handleSlotChange() {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>(\"slot\");\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 await Promise.resolve(); // Wait for current update cycle to complete before modifying reactive state (solves the lit issue \"scheduled an update\")\n\n this.slotHasContent = assignedNodes.length > 0;\n\n // Determine layout based on content structure\n const container = this.shadowRoot?.querySelector(\n \".nys-globalfooter__content\",\n );\n const hasMultipleGroups = assignedNodes?.some(\n (node) => node.tagName === \"H4\",\n );\n\n // Toggle layout classes\n if (container) {\n container.classList.toggle(\"columns\", hasMultipleGroups);\n container.classList.toggle(\"small\", !hasMultipleGroups);\n\n container.innerHTML = \"\";\n\n // Clone and append slotted elements into the shadow DOM container\n assignedNodes.forEach((node) => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const cleanNode = node.cloneNode(true);\n\n // Remove <script>, <iframe>, <object>, and any potentially dangerous elements XSS\n const dangerousTags = [\"script\", \"iframe\", \"object\", \"embed\", \"img\"];\n dangerousTags.forEach((tag) => {\n (cleanNode as Element)\n .querySelectorAll(tag)\n .forEach((element) => element.remove());\n });\n container.appendChild(cleanNode);\n node.remove(); // Remove from light DOM to avoid duplication\n }\n });\n\n const spans = container.querySelectorAll(\"span\");\n spans.forEach((span) => {\n const divider = document.createElement(\"nys-divider\");\n divider.classList.add(\"divider\");\n span.insertAdjacentElement(\"afterend\", divider);\n });\n }\n }\n\n render() {\n return html`\n <footer class=\"nys-globalfooter\">\n <div class=\"nys-globalfooter__main-container\">\n <div class=\"nys-globalfooter__heading-container\">\n ${!this.homepageLink?.trim()\n ? html`<h2 class=\"nys-globalfooter__name\">${this.agencyName}</h2>`\n : html`<a href=${this.homepageLink?.trim()}>\n <h2 class=\"nys-globalfooter__name\">${this.agencyName}</h2>\n </a>`}\n ${this.agencySubheading\n ? html`<p class=\"nys-globalfooter__subheading\">\n ${this.agencySubheading}\n </p>`\n : nothing}\n </div>\n ${this.slotHasContent\n ? html`<div class=\"nys-globalfooter__content\">\n <slot\n style=\"display: hidden\"\n @slotchange=\"${this._handleSlotChange}\"\n ></slot>\n </div>`\n : \"\"}\n </div>\n </footer>\n `;\n }\n}\n\nif (!customElements.get(\"nys-globalfooter\")) {\n customElements.define(\"nys-globalfooter\", NysGlobalFooter);\n}\n"],"names":["_NysGlobalFooter","LitElement","slot","assignedNodes","node","container","hasMultipleGroups","cleanNode","tag","element","span","divider","html","nothing","unsafeCSS","styles","NysGlobalFooter","__decorateClass","property","state"],"mappings":";;;;;;;;;;;;;;;;;;AAwEO,MAAMA,IAAN,MAAMA,UAAwBC,EAAW;AAAA,EAAzC,cAAA;AAAA,UAAA,GAAA,SAAA,GAIuB,KAAA,aAAa,IAGb,KAAA,mBAAmB,IAGnB,KAAA,eAAe,IAClC,KAAQ,iBAAiB;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOlC,eAAe;AAGb,IADa,KAAK,YAAY,cAA+B,MAAM,GAC7D,iBAAiB,cAAc,MAAM,KAAK,mBAAmB,GACnE,KAAK,kBAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,oBAAoB;AAChC,UAAMC,IAAO,KAAK,YAAY,cAA+B,MAAM;AACnE,QAAI,CAACA,EAAM;AAEX,UAAMC,IAAgBD,GAClB,cAAc,EAAE,SAAS,GAAA,CAAM,EAChC,OAAO,CAACE,MAASA,EAAK,aAAa,KAAK,YAAY;AAEvD,UAAM,QAAQ,QAAA,GAEd,KAAK,iBAAiBD,EAAc,SAAS;AAG7C,UAAME,IAAY,KAAK,YAAY;AAAA,MACjC;AAAA,IAAA,GAEIC,IAAoBH,GAAe;AAAA,MACvC,CAACC,MAASA,EAAK,YAAY;AAAA,IAAA;AAI7B,IAAIC,MACFA,EAAU,UAAU,OAAO,WAAWC,CAAiB,GACvDD,EAAU,UAAU,OAAO,SAAS,CAACC,CAAiB,GAEtDD,EAAU,YAAY,IAGtBF,EAAc,QAAQ,CAACC,MAAS;AAC9B,UAAIA,EAAK,aAAa,KAAK,cAAc;AACvC,cAAMG,IAAYH,EAAK,UAAU,EAAI;AAIrC,QADsB,CAAC,UAAU,UAAU,UAAU,SAAS,KAAK,EACrD,QAAQ,CAACI,MAAQ;AAC5B,UAAAD,EACE,iBAAiBC,CAAG,EACpB,QAAQ,CAACC,MAAYA,EAAQ,QAAQ;AAAA,QAC1C,CAAC,GACDJ,EAAU,YAAYE,CAAS,GAC/BH,EAAK,OAAA;AAAA,MACP;AAAA,IACF,CAAC,GAEaC,EAAU,iBAAiB,MAAM,EACzC,QAAQ,CAACK,MAAS;AACtB,YAAMC,IAAU,SAAS,cAAc,aAAa;AACpD,MAAAA,EAAQ,UAAU,IAAI,SAAS,GAC/BD,EAAK,sBAAsB,YAAYC,CAAO;AAAA,IAChD,CAAC;AAAA,EAEL;AAAA,EAEA,SAAS;AACP,WAAOC;AAAA;AAAA;AAAA;AAAA,cAII,KAAK,cAAc,KAAA,IAElBA,YAAe,KAAK,cAAc,MAAM;AAAA,uDACD,KAAK,UAAU;AAAA,wBAFtDA,uCAA0C,KAAK,UAAU,OAGpD;AAAA,cACP,KAAK,mBACHA;AAAA,oBACI,KAAK,gBAAgB;AAAA,wBAEzBC,CAAO;AAAA;AAAA,YAEX,KAAK,iBACHD;AAAA;AAAA;AAAA,iCAGmB,KAAK,iBAAiB;AAAA;AAAA,wBAGzC,EAAE;AAAA;AAAA;AAAA;AAAA,EAId;AACF;AA/GEZ,EAAO,SAASc,EAAUC,CAAM;AAD3B,IAAMC,IAANhB;AAIuBiB,EAAA;AAAA,EAA3BC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GAJfF,EAIiB,WAAA,YAAA;AAGAC,EAAA;AAAA,EAA3BC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GAPfF,EAOiB,WAAA,kBAAA;AAGAC,EAAA;AAAA,EAA3BC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GAVfF,EAUiB,WAAA,cAAA;AACXC,EAAA;AAAA,EAAhBE,EAAA;AAAM,GAXIH,EAWM,WAAA,gBAAA;AAuGd,eAAe,IAAI,kBAAkB,KACxC,eAAe,OAAO,oBAAoBA,CAAe;"}
1
+ {"version":3,"file":"nys-globalfooter.js","sources":["../../internals/dist/index.js","../src/nys-globalfooter.ts"],"sourcesContent":["import { LitElement as u } from \"lit\";\n/*!\n * New York State Design System v1.20.1\n * Description: A design system for New York State's digital products.\n * Repository: https://github.com/its-hcd/nysds\n * License: MIT\n */\nlet y = 0;\nfunction f(e) {\n return `${e}-${Date.now()}-${y++}`;\n}\nconst p = {\n labelledby: \"ariaLabelledByElements\",\n describedby: \"ariaDescribedByElements\"\n}, A = {\n labelledby: \"ariaLabel\",\n describedby: \"ariaDescription\"\n};\nfunction R(e) {\n return \"ariaDescribedByElements\" in e;\n}\nfunction _(e) {\n return e === \"errormessage\" ? \"aria-errormessage\" : `aria-${e}`;\n}\nfunction d(e) {\n return e.filter((t) => !!t);\n}\nfunction E(e) {\n return e.map((t) => (t.id || (t.id = f(\"nys-ref\")), t.id));\n}\nfunction V(e, t, n) {\n const i = d(n), r = _(t);\n i.length ? e.setAttribute(r, E(i).join(\" \")) : e.removeAttribute(r);\n}\nfunction x(e, t, n, i, r) {\n const s = d(i), a = p[n], l = t;\n if (a && R(t)) {\n l[a] = s.length ? s : null;\n return;\n }\n const o = A[n];\n if (!o) return;\n const c = r ?? (s.length ? s.map((m) => m.textContent?.trim() ?? \"\").filter(Boolean).join(\" \") : \"\");\n o in l ? l[o] = c || null : c ? e.setAttribute(\n n === \"labelledby\" ? \"aria-label\" : \"aria-description\",\n c\n ) : e.removeAttribute(\n n === \"labelledby\" ? \"aria-label\" : \"aria-description\"\n );\n}\nconst g = {\n labelledby: \"ariaLabelledByElements\",\n describedby: \"ariaDescribedByElements\"\n}, C = {\n labelledby: \"aria-label\",\n describedby: \"aria-description\"\n};\nfunction L(e, t, n) {\n const i = d(n), r = g[t], s = C[t], a = e;\n if (r && r in e && (a[r] = i.length ? i : null), !s) return;\n const l = i.map((o) => o.textContent?.trim() ?? \"\").filter(Boolean).join(\" \");\n l ? e.setAttribute(s, l) : e.removeAttribute(s);\n}\nconst b = (e) => {\n class t extends e {\n get idPrefix() {\n return this.localName || \"nys-element\";\n }\n ensureId() {\n this.id || (this.id = f(this.idPrefix));\n }\n connectedCallback() {\n super.connectedCallback(), this.ensureId();\n }\n }\n return t;\n}, P = /* @__PURE__ */ b(u), h = (e) => {\n class t extends b(e) {\n constructor() {\n super(...arguments), this.__internals = null;\n }\n get internals() {\n if (this.__internals) return this.__internals;\n if (typeof this.attachInternals == \"function\")\n try {\n this.__internals = this.attachInternals();\n } catch {\n this.__internals = null;\n }\n return this.__internals;\n }\n get defaultRole() {\n return null;\n }\n setHostAria(i, r) {\n const s = this.internals;\n if (s && i in s) {\n s[i] = r;\n return;\n }\n const a = v(i);\n r === null ? this.removeAttribute(a) : this.setAttribute(a, r);\n }\n reflectDefaultSemantics() {\n const i = this.defaultRole;\n i && this.setHostAria(\"role\", i);\n }\n connectedCallback() {\n super.connectedCallback(), this.reflectDefaultSemantics();\n }\n }\n return t;\n};\nfunction v(e) {\n if (e === \"role\") return \"role\";\n const t = e.replace(/^aria/, \"\");\n return \"aria-\" + t.charAt(0).toLowerCase() + t.slice(1);\n}\nconst T = /* @__PURE__ */ h(u), I = (e) => {\n const n = class n extends h(e) {\n setFormValue(r) {\n this.internals?.setFormValue(r ?? null);\n }\n setValidityFromState(r, s, a) {\n const l = this.internals;\n if (!l) return;\n const o = Object.values(r).some(Boolean);\n o ? l.setValidity(r, s ?? \"Invalid value\", a) : l.setValidity({}), this.setHostAria(\"ariaInvalid\", o ? \"true\" : \"false\");\n }\n clearValidity() {\n this.internals?.setValidity({}), this.setHostAria(\"ariaInvalid\", \"false\");\n }\n checkValidity() {\n return this.internals?.checkValidity() ?? !0;\n }\n reportValidity() {\n return this.internals?.reportValidity() ?? !0;\n }\n formResetCallback() {\n this.clearValidity();\n }\n };\n n.formAssociated = !0;\n let t = n;\n return t;\n}, w = /* @__PURE__ */ I(u);\nfunction O(e) {\n const t = e.tagName.toLowerCase(), n = /* @__PURE__ */ new Set(), i = (r) => {\n r.querySelectorAll(\"*\").forEach((s) => {\n const a = s.tagName.toLowerCase();\n a.startsWith(\"nys-\") && a !== t && !customElements.get(a) && n.add(a), s.shadowRoot && i(s.shadowRoot);\n });\n };\n return e.shadowRoot && i(e.shadowRoot), i(e), [...n].sort();\n}\nexport {\n I as FormControlMixin,\n b as IdentifiedMixin,\n P as NysElement,\n w as NysFormControlElement,\n T as NysReflectsAriaElement,\n h as ReflectsAriaMixin,\n V as associateControl,\n L as associateControlRefs,\n x as associateHost,\n O as findUnregisteredChildren,\n f as generateId,\n R as supportsElementRefs\n};\n//# sourceMappingURL=index.js.map\n","import { html, unsafeCSS, nothing } from \"lit\";\nimport { property, state } from \"lit/decorators.js\";\nimport { ifDefined } from \"lit/directives/if-defined.js\";\nimport { NysElement } from \"@nysds/internals\";\n// This element is created imperatively (document.createElement) after each\n// slotted <span> section heading, so it must be registered whenever\n// nys-globalfooter is used. Importing it here (intentional side effect)\n// guarantees it always renders.\nimport \"@nysds/nys-divider\";\n// @ts-ignore: SCSS module imported via bundler as inline\nimport styles from \"./nys-globalfooter.scss?inline\";\n\n/**\n * Accessible name for the `contentinfo` landmark when the footer carries no\n * agency name to reference and the consumer supplied no override.\n */\nconst DEFAULT_LANDMARK_LABEL = \"Site\";\n\n/**\n * Agency-branded footer with agency name and slotted content sections. Auto-layouts based on content structure.\n *\n * Place above `nys-unavfooter`. Slot contact info, links, or other content. Use `<h4>` elements\n * to create multi-column layouts; without `<h4>`, renders as compact single section.\n *\n * @summary Agency footer with auto-layout for contact info and link sections.\n * @element nys-globalfooter\n *\n * @slot - Footer content (links, contact info). Use `<h4>` for column headings.\n *\n * @example Basic\n * ```html\n * <nys-globalfooter agencyName=\"Office of Information Technology Services\"></nys-globalfooter>\n * ```\n *\n * @example Homepage Link\n * ```html\n * <nys-globalfooter\n * agencyName=\"Office of Information Technology Services\"\n * homepageLink=\"https://its.ny.gov\"\n * ></nys-globalfooter>\n * ```\n *\n * @example Menu Links\n * ```html\n * <nys-globalfooter agencyName=\"Office of Information Technology Services\">\n * <ul>\n * <li><a href=\"https://its.ny.gov\">ITS Home</a></li>\n * <li><a href=\"https://its.ny.gov/about-us\">About ITS</a></li>\n * </ul>\n * </nys-globalfooter>\n * ```\n *\n * @example Column Links\n * ```html\n * <nys-globalfooter agencyName=\"Office of Information Technology Services\">\n * <ul>\n * <li>\n * <span>About</span>\n * <ul>\n * <li><a href=\"https://its.ny.gov/about-us\">About ITS</a></li>\n * <li><a href=\"https://its.ny.gov/contact-us\">Contact</a></li>\n * <li><a href=\"https://its.ny.gov/policies\">Policies</a></li>\n * </ul>\n * </li>\n * <li>\n * <span>Resources</span>\n * <ul>\n * <li><a href=\"https://its.ny.gov/resources\">Developer Tools</a></li>\n * <li><a href=\"https://its.ny.gov/accessibility\">Accessibility</a></li>\n * <li><a href=\"https://its.ny.gov/privacy\">Privacy</a></li>\n * </ul>\n * </li>\n * </ul>\n * </nys-globalfooter>\n * ```\n *\n * @example Subheading\n * ```html\n * <nys-globalfooter\n * agencyName=\"Office of Information Technology Services\"\n * agencySubheading=\"Innovating Technology for a Better New York\"\n * ></nys-globalfooter>\n * ```\n *\n * @example Custom landmark label\n * ```html\n * <!-- Names the contentinfo landmark directly instead of from the visible\n * heading. Keep it distinct from nys-unavfooter's (\"New York State\"). -->\n * <nys-globalfooter\n * agencyName=\"Office of Information Technology Services\"\n * landmarkLabel=\"ITS\"\n * ></nys-globalfooter>\n * ```\n */\n\nexport class NysGlobalFooter extends NysElement {\n static styles = unsafeCSS(styles);\n\n /** Unique identifier. Auto-generated if not provided. */\n @property({ type: String, reflect: true }) id = \"\";\n\n /** Agency name displayed as the footer heading. */\n @property({ type: String }) agencyName = \"\";\n\n /** Optional subheading displayed below the agency name. */\n @property({ type: String }) agencySubheading = \"\";\n\n /** URL for the agency name link. If empty, name is not clickable. */\n @property({ type: String }) homepageLink = \"\";\n\n /**\n * Accessible name for the `contentinfo` landmark this footer renders.\n *\n * Leave it unset and the landmark is named after the visible `agencyName`\n * heading, which cannot drift out of sync and is translated with the rest of\n * the page. Falls back to `\"Site\"` when there is no agency name to reference.\n *\n * Set this only when the agency name is not right for your audience. A page\n * pairing this with `nys-unavfooter` carries two `contentinfo` landmarks, so\n * the name must stay distinct from that footer's (`\"New York State\"` by\n * default) or landmark navigation stops distinguishing them.\n *\n * An explicit name replaces the reference to the visible heading.\n */\n @property({ type: String }) landmarkLabel = \"\";\n\n @state() private slotHasContent = true;\n\n /**\n * Lifecycle Methods\n * --------------------------------------------------------------------------\n */\n\n // No connectedCallback override is needed here: NysElement's own\n // connectedCallback already assigns an id when one is not provided, and it runs\n // automatically because this class does not override it. The contentinfo landmark\n // intentionally stays on the inner <footer> element (and is given an accessible\n // name from the agency name so multiple footers on a page are distinguishable),\n // so this component keeps defaultRole = null and does not move a role onto the host.\n\n firstUpdated() {\n // Check for slot content after rendering\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>(\"slot\");\n slot?.addEventListener(\"slotchange\", () => this._handleSlotChange());\n this._handleSlotChange(); // Initial check\n }\n\n /**\n * Functions\n * --------------------------------------------------------------------------\n */\n\n // Gets called when the slot content changes and directly appends the slotted elements into the shadow DOM\n private async _handleSlotChange() {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>(\"slot\");\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 await Promise.resolve(); // Wait for current update cycle to complete before modifying reactive state (solves the lit issue \"scheduled an update\")\n\n this.slotHasContent = assignedNodes.length > 0;\n\n // Determine layout based on content structure\n const container = this.shadowRoot?.querySelector(\n \".nys-globalfooter__content\",\n );\n const hasMultipleGroups = assignedNodes?.some(\n (node) => node.tagName === \"H4\",\n );\n\n // Toggle layout classes\n if (container) {\n container.classList.toggle(\"columns\", hasMultipleGroups);\n container.classList.toggle(\"small\", !hasMultipleGroups);\n\n container.innerHTML = \"\";\n\n // Clone and append slotted elements into the shadow DOM container\n assignedNodes.forEach((node) => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const cleanNode = node.cloneNode(true);\n\n // Remove <script>, <iframe>, <object>, and any potentially dangerous elements XSS\n const dangerousTags = [\"script\", \"iframe\", \"object\", \"embed\", \"img\"];\n dangerousTags.forEach((tag) => {\n (cleanNode as Element)\n .querySelectorAll(tag)\n .forEach((element) => element.remove());\n });\n container.appendChild(cleanNode);\n node.remove(); // Remove from light DOM to avoid duplication\n }\n });\n\n const spans = container.querySelectorAll(\"span\");\n spans.forEach((span) => {\n const divider = document.createElement(\"nys-divider\");\n divider.classList.add(\"divider\");\n span.insertAdjacentElement(\"afterend\", divider);\n });\n }\n }\n\n /**\n * Id of the heading that names the contentinfo landmark, or undefined when no\n * agency name was given.\n *\n * The documented pairing puts this footer above `nys-unavfooter`, which leaves a\n * page with two `contentinfo` landmarks. Pointing at the visible heading rather\n * than repeating the agency name in an `aria-label` keeps the two in sync and lets\n * the name be translated along with the rest of the page.\n */\n private get _contentinfoLabelledBy(): string | undefined {\n // An explicit name is the author saying the visible heading is not the right\n // one; pointing at it anyway would put both on the landmark.\n if (this._landmarkLabelOverride) return undefined;\n return this.agencyName?.trim() ? `${this.id}-name` : undefined;\n }\n\n /** The author's landmark name, or undefined when they gave none. */\n private get _landmarkLabelOverride(): string | undefined {\n return this.landmarkLabel?.trim() || undefined;\n }\n\n /**\n * Literal name for the contentinfo: the author's override, or the \"Site\"\n * default when there is no agency heading to reference. Undefined whenever\n * `_contentinfoLabelledBy` has something to point at, so the landmark never\n * carries both.\n */\n private get _contentinfoLabel(): string | undefined {\n if (this._landmarkLabelOverride) return this._landmarkLabelOverride;\n return this._contentinfoLabelledBy ? undefined : DEFAULT_LANDMARK_LABEL;\n }\n\n render() {\n const heading = html`<h2\n id=\"${this.id}-name\"\n class=\"nys-globalfooter__name\"\n >\n ${this.agencyName}\n </h2>`;\n\n return html`\n <footer\n class=\"nys-globalfooter\"\n aria-labelledby=${ifDefined(this._contentinfoLabelledBy)}\n aria-label=${ifDefined(this._contentinfoLabel)}\n >\n <div class=\"nys-globalfooter__main-container\">\n <div class=\"nys-globalfooter__heading-container\">\n ${!this.homepageLink?.trim()\n ? heading\n : html`<a href=${this.homepageLink?.trim()}>${heading}</a>`}\n ${this.agencySubheading\n ? html`<p class=\"nys-globalfooter__subheading\">\n ${this.agencySubheading}\n </p>`\n : nothing}\n </div>\n ${this.slotHasContent\n ? html`<div class=\"nys-globalfooter__content\">\n <slot\n style=\"display: hidden\"\n @slotchange=\"${this._handleSlotChange}\"\n ></slot>\n </div>`\n : \"\"}\n </div>\n </footer>\n `;\n }\n}\n\nif (!customElements.get(\"nys-globalfooter\")) {\n customElements.define(\"nys-globalfooter\", NysGlobalFooter);\n}\n"],"names":["LitElement","unsafeCSS","html","nothing","property","state","ifDefined","y","f","e","b","t","P","u","DEFAULT_LANDMARK_LABEL","_NysGlobalFooter","NysElement","slot","assignedNodes","node","container","hasMultipleGroups","cleanNode","tag","element","span","divider","heading","styles","NysGlobalFooter","__decorateClass"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nysds/nys-globalfooter",
3
- "version": "1.19.4",
3
+ "version": "1.20.1",
4
4
  "description": "The Globalfooter component from the NYS Design System.",
5
5
  "module": "dist/nys-globalfooter.js",
6
6
  "types": "dist/index.d.ts",
@@ -23,9 +23,10 @@
23
23
  "lit-analyze": "lit-analyzer '**/*.ts'"
24
24
  },
25
25
  "dependencies": {
26
- "@nysds/nys-divider": "^1.19.4"
26
+ "@nysds/nys-divider": "^1.20.1"
27
27
  },
28
28
  "devDependencies": {
29
+ "@nysds/internals": "^1.20.1",
29
30
  "lit": "^3.3.1",
30
31
  "typescript": "^5.9.3",
31
32
  "vite": "^7.3.1"