@nysds/nys-dropdownmenu 1.21.0 → 1.22.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.
@@ -8,6 +8,20 @@ type Position = "bottom-start" | "bottom-end" | "top-start" | "top-end";
8
8
  * @summary Action menu with auto-positioning, keyboard support, and screen reader integration.
9
9
  * @element nys-dropdownmenu
10
10
  *
11
+ * @usagedos
12
+ * - Use to display a list of 5–15 actions or links under a single trigger, such as navigation, commands, or user profile access.
13
+ * - Keep to 3–7 actions when possible.
14
+ * - Order actions by frequency or importance.
15
+ * - Separate destructive actions with a divider.
16
+ * - Use clear, specific labels (e.g., "Profile", "Account Settings", "Sign out").
17
+ *
18
+ * @usagedonts
19
+ * - Use for form inputs or selectable values. Use `<nys-select>` for standard selection or `<nys-combobox>` when users need to filter or search through many options.
20
+ * - Use when the action is always visible and frequently used. Use `<nys-button>` instead.
21
+ * - Use without a label or accessible description on the trigger.
22
+ * - Nest dropdown menus inside other dropdown menus.
23
+ * - Use vague labels like "Manage" or "Options".
24
+ *
11
25
  * @example Basic
12
26
  * ```html
13
27
  * <nys-button id="my-trigger-id" label="Open Menu"></nys-button>
@@ -55,6 +69,7 @@ export declare class NysDropdownMenu extends NysElement {
55
69
  private _lastFocusedIndex;
56
70
  private readonly GAP;
57
71
  private _resizeObserver;
72
+ private _wiredFor;
58
73
  /**
59
74
  * Lifecycle Methods
60
75
  * --------------------------------------------------------------------------
@@ -63,12 +78,14 @@ export declare class NysDropdownMenu extends NysElement {
63
78
  connectedCallback(): void;
64
79
  disconnectedCallback(): void;
65
80
  firstUpdated(): Promise<void>;
81
+ updated(changedProperties: Map<string | number | symbol, unknown>): void;
66
82
  /**
67
83
  * Functions
68
84
  * --------------------------------------------------------------------------
69
85
  */
70
86
  private _findTrigger;
71
87
  private _connectTrigger;
88
+ private _disconnectTrigger;
72
89
  private _toggleDropdown;
73
90
  private _closeDropdown;
74
91
  private _getMenuItems;
@@ -1,4 +1,4 @@
1
- import { LitElement as y, unsafeCSS as b, html as c } from "lit";
1
+ import { LitElement as y, unsafeCSS as g, html as u } from "lit";
2
2
  import { property as d } from "lit/decorators.js";
3
3
  import "@nysds/nys-icon";
4
4
  /*!
@@ -6,22 +6,22 @@ import "@nysds/nys-icon";
6
6
  * █ █ █ █▄▄▄█ ▀▀▀▄▄ █ █ ▀▀▀▄▄
7
7
  * █ ▀█ █ █▄▄▄█ █▄▄▀ █▄▄▄█
8
8
  *
9
- * Dropdownmenu Component v1.21.0
9
+ * Dropdownmenu Component v1.22.0
10
10
  * Part of the New York State Design System
11
11
  * Repository: https://github.com/its-hcd/nysds
12
12
  * License: MIT
13
13
  */
14
14
  /*!
15
- * New York State Design System v1.21.0
15
+ * New York State Design System v1.22.0
16
16
  * Description: A design system for New York State's digital products.
17
17
  * Repository: https://github.com/its-hcd/nysds
18
18
  * License: MIT
19
19
  */
20
- var x = 0;
20
+ let x = 0;
21
21
  function k(a) {
22
22
  return `${a}-${Date.now()}-${x++}`;
23
23
  }
24
- var E = (a) => {
24
+ const E = (a) => {
25
25
  class e extends a {
26
26
  get idPrefix() {
27
27
  return this.localName || "nys-element";
@@ -34,11 +34,10 @@ var E = (a) => {
34
34
  }
35
35
  }
36
36
  return e;
37
- }, _ = /* @__PURE__ */ E(y);
38
- const g = ':host{--_nys-dropdownmenu-width: 180px;--_nys-dropdownmenu-height: 100%;--_nys-dropdownmenu-radius: var(--nys-radius-md, 4px);--_nys-dropdownmenu-border-width: var(--nys-border-width-sm, 1px);--_nys-dropdownmenu-border-color: var(--nys-color-neutral-100, #d0d0ce);--_nys-dropdownmenu-background-color: var(--nys-color-ink-reverse, #ffffff);--_nys-dropdownmenu-boxshadow-color-100: var( --nys-color-black-transparent-100, rgba(27, 27, 27, .1) );--_nys-dropdownmenu-boxshadow-color-50: var( --nys-color-black-transparent-50, rgba(27, 27, 27, .01) );--_nys-dropdownmenu-gap: var(--nys-space-2px, 2px);--_nys-dropdownmenu-padding: var(--nys-space-100, 8px);--_nys-dropdownmenu-font-size: var(--nys-font-size-ui-md, 16px);--_nys-dropdownmenu-font-weight: var(font-weight: 400);--_nys-dropdownmenu-line-height: var(--nys-font-lineheight-ui-md, 24px);--_nys-dropdownmenu-font-family: var( --nys-font-family-ui, var( --nys-font-family-sans, "Proxima Nova", "Helvetica Neue", "Helvetica", "Arial", sans-serif ) );--_nys-dropdownmenuitem-color: var(--nys-color-text, #1b1b1b);--_nys-dropdownmenuitem-gap: var(--nys-space-100, 8px);--_nys-dropdownmenuitem-padding: var(--nys-space-200, 16px) var(--nys-space-100, 8px);--_nys-dropdownmenuitem-border-radius: var(--nys-radius-md, 4px);--_nys-dropdownmenuitem-background-color: var(--nys-color-white, #ffffff);--_nys-dropdownmenuitem-background-color--hover: var( --nys-color-neutral-10, #f6f6f6 );--_nys-dropdownmenuitem-background-color--active: var( --nys-color-neutral-50, #ededed );--_nys-dropdownmenuitem-color--disabled: var( --nys-color-text-disabled, #bec0c1 );--_nys-dropdownmenuitem-outline-width: var(--nys-border-width-md, 2px);--_nys-dropdownmenuitem-outline-color: var(--nys-color-focus, #004dd1)}.nys-dropdownmenu{position:fixed;top:0;left:0;z-index:1776}.nys-dropdownmenu.active ul{display:flex}.nys-dropdownmenu ul{display:none;align-items:flex-start;flex-direction:column;gap:var(--_nys-dropdownmenu-gap);width:var(--_nys-dropdownmenu-width);height:var(--_nys-dropdownmenu-height);border-radius:var(--_nys-dropdownmenu-radius);border:var(--_nys-dropdownmenu-border-width) solid var(--_nys-dropdownmenu-border-color);background-color:var(--_nys-dropdownmenu-background-color);box-shadow:0 4px 6px -1px var(--_nys-dropdownmenu-boxshadow-color-100),0 4px 6px -1px var(--_nys-dropdownmenu-boxshadow-color-50);padding:var(--_nys-dropdownmenu-padding);font-family:var(--_nys-dropdownmenu-font-family);font-size:var(--_nys-dropdownmenu-font-size);font-weight:var(--_nys-dropdownmenu-font-weight);line-height:var(--_nys-dropdownmenu-line-height);overflow:hidden;margin:0}.nys-dropdownmenuitem{list-style:none;width:var(--_nys-dropdownmenu-width);padding:0;margin:0}.nys-dropdownmenuitem a,.nys-dropdownmenuitem button{display:flex;gap:var(--_nys-dropdownmenuitem-gap);padding:var(--_nys-dropdownmenuitem-padding);border-radius:var(--_nys-dropdownmenuitem-border-radius);background-color:var(--_nys-dropdownmenuitem-background-color);width:100%;box-sizing:border-box;text-decoration:none;text-wrap:wrap;color:var(--_nys-dropdownmenuitem-color);transition:.05s ease-in-out all;border:none;font-family:var(--_nys-dropdownmenu-font-family);font-size:var(--_nys-dropdownmenu-font-size);font-weight:var(--_nys-dropdownmenu-font-weight);line-height:var(--_nys-dropdownmenu-line-height);cursor:pointer}.nys-dropdownmenuitem a nys-icon,.nys-dropdownmenuitem button nys-icon{margin-top:2.5px}.nys-dropdownmenuitem a:hover:not(.disabled):not([aria-disabled=true]),.nys-dropdownmenuitem button:hover:not(.disabled):not([aria-disabled=true]){background-color:var(--_nys-dropdownmenuitem-background-color--hover)}.nys-dropdownmenuitem a:active:not(.disabled):not([aria-disabled=true]),.nys-dropdownmenuitem button:active:not(.disabled):not([aria-disabled=true]){background-color:var(--_nys-dropdownmenuitem-background-color--active)}.nys-dropdownmenuitem a:focus-visible:not(.disabled):not([aria-disabled=true]):not([focus-ring-false]),.nys-dropdownmenuitem button:focus-visible:not(.disabled):not([aria-disabled=true]):not([focus-ring-false]){position:relative;outline:var(--_nys-dropdownmenuitem-outline-width) solid var(--_nys-dropdownmenuitem-outline-color)}.nys-dropdownmenuitem a.disabled,.nys-dropdownmenuitem a[aria-disabled=true],.nys-dropdownmenuitem button.disabled,.nys-dropdownmenuitem button[aria-disabled=true]{color:var(--_nys-dropdownmenuitem-color--disabled);pointer-events:none;cursor:default}';
39
- var $ = Object.defineProperty, h = (a, e, t, n) => {
40
- for (var o = void 0, i = a.length - 1, s; i >= 0; i--)
41
- (s = a[i]) && (o = s(e, t, o) || o);
37
+ }, _ = /* @__PURE__ */ E(y), b = ':host{--_nys-dropdownmenu-width: 180px;--_nys-dropdownmenu-height: 100%;--_nys-dropdownmenu-radius: var(--nys-radius-md, 4px);--_nys-dropdownmenu-border-width: var(--nys-border-width-sm, 1px);--_nys-dropdownmenu-border-color: var(--nys-color-neutral-100, #d0d0ce);--_nys-dropdownmenu-background-color: var(--nys-color-ink-reverse, #ffffff);--_nys-dropdownmenu-boxshadow-color-100: var( --nys-color-black-transparent-100, rgba(27, 27, 27, .1) );--_nys-dropdownmenu-boxshadow-color-50: var( --nys-color-black-transparent-50, rgba(27, 27, 27, .01) );--_nys-dropdownmenu-gap: var(--nys-space-2px, 2px);--_nys-dropdownmenu-padding: var(--nys-space-100, 8px);--_nys-dropdownmenu-font-size: var(--nys-font-size-ui-md, 16px);--_nys-dropdownmenu-font-weight: var(font-weight: 400);--_nys-dropdownmenu-line-height: var(--nys-font-lineheight-ui-md, 24px);--_nys-dropdownmenu-font-family: var( --nys-font-family-ui, var( --nys-font-family-sans, "Proxima Nova", "Helvetica Neue", "Helvetica", "Arial", sans-serif ) );--_nys-dropdownmenuitem-color: var(--nys-color-text, #1b1b1b);--_nys-dropdownmenuitem-gap: var(--nys-space-100, 8px);--_nys-dropdownmenuitem-padding: var(--nys-space-200, 16px) var(--nys-space-100, 8px);--_nys-dropdownmenuitem-border-radius: var(--nys-radius-md, 4px);--_nys-dropdownmenuitem-background-color: var(--nys-color-white, #ffffff);--_nys-dropdownmenuitem-background-color--hover: var( --nys-color-neutral-10, #f6f6f6 );--_nys-dropdownmenuitem-background-color--active: var( --nys-color-neutral-50, #ededed );--_nys-dropdownmenuitem-color--disabled: var( --nys-color-text-disabled, #bec0c1 );--_nys-dropdownmenuitem-outline-width: var(--nys-border-width-md, 2px);--_nys-dropdownmenuitem-outline-color: var(--nys-color-focus, #004dd1)}.nys-dropdownmenu{position:fixed;top:0;left:0;z-index:1776}.nys-dropdownmenu.active ul{display:flex}.nys-dropdownmenu ul{display:none;align-items:flex-start;flex-direction:column;gap:var(--_nys-dropdownmenu-gap);width:var(--_nys-dropdownmenu-width);height:var(--_nys-dropdownmenu-height);border-radius:var(--_nys-dropdownmenu-radius);border:var(--_nys-dropdownmenu-border-width) solid var(--_nys-dropdownmenu-border-color);background-color:var(--_nys-dropdownmenu-background-color);box-shadow:0 4px 6px -1px var(--_nys-dropdownmenu-boxshadow-color-100),0 4px 6px -1px var(--_nys-dropdownmenu-boxshadow-color-50);padding:var(--_nys-dropdownmenu-padding);font-family:var(--_nys-dropdownmenu-font-family);font-size:var(--_nys-dropdownmenu-font-size);font-weight:var(--_nys-dropdownmenu-font-weight);line-height:var(--_nys-dropdownmenu-line-height);overflow:hidden;margin:0}.nys-dropdownmenuitem{list-style:none;width:var(--_nys-dropdownmenu-width);padding:0;margin:0}.nys-dropdownmenuitem a,.nys-dropdownmenuitem button{display:flex;gap:var(--_nys-dropdownmenuitem-gap);padding:var(--_nys-dropdownmenuitem-padding);border-radius:var(--_nys-dropdownmenuitem-border-radius);background-color:var(--_nys-dropdownmenuitem-background-color);width:100%;box-sizing:border-box;text-decoration:none;text-wrap:wrap;color:var(--_nys-dropdownmenuitem-color);transition:.05s ease-in-out all;border:none;font-family:var(--_nys-dropdownmenu-font-family);font-size:var(--_nys-dropdownmenu-font-size);font-weight:var(--_nys-dropdownmenu-font-weight);line-height:var(--_nys-dropdownmenu-line-height);cursor:pointer}.nys-dropdownmenuitem a nys-icon,.nys-dropdownmenuitem button nys-icon{margin-top:2.5px}.nys-dropdownmenuitem a:hover:not(.disabled):not([aria-disabled=true]),.nys-dropdownmenuitem button:hover:not(.disabled):not([aria-disabled=true]){background-color:var(--_nys-dropdownmenuitem-background-color--hover)}.nys-dropdownmenuitem a:active:not(.disabled):not([aria-disabled=true]),.nys-dropdownmenuitem button:active:not(.disabled):not([aria-disabled=true]){background-color:var(--_nys-dropdownmenuitem-background-color--active)}.nys-dropdownmenuitem a:focus-visible:not(.disabled):not([aria-disabled=true]):not([focus-ring-false]),.nys-dropdownmenuitem button:focus-visible:not(.disabled):not([aria-disabled=true]):not([focus-ring-false]){position:relative;outline:var(--_nys-dropdownmenuitem-outline-width) solid var(--_nys-dropdownmenuitem-outline-color)}.nys-dropdownmenuitem a.disabled,.nys-dropdownmenuitem a[aria-disabled=true],.nys-dropdownmenuitem button.disabled,.nys-dropdownmenuitem button[aria-disabled=true]{color:var(--_nys-dropdownmenuitem-color--disabled);pointer-events:none;cursor:default}';
38
+ var $ = Object.defineProperty, c = (a, e, t, n) => {
39
+ for (var o = void 0, i = a.length - 1, r; i >= 0; i--)
40
+ (r = a[i]) && (o = r(e, t, o) || o);
42
41
  return o && $(e, t, o), o;
43
42
  };
44
43
  const m = class m extends _ {
@@ -71,8 +70,8 @@ const m = class m extends _ {
71
70
  }
72
71
  render() {
73
72
  const e = !!this.href;
74
- return c`<li class="nys-dropdownmenuitem" role="presentation">
75
- ${e ? c` <a
73
+ return u`<li class="nys-dropdownmenuitem" role="presentation">
74
+ ${e ? u` <a
76
75
  class=${this.disabled ? "disabled" : ""}
77
76
  href=${this.disabled ? "" : this.href}
78
77
  role="menuitem"
@@ -82,9 +81,9 @@ const m = class m extends _ {
82
81
  @click="${this._handleClick}"
83
82
  target="${this.target}"
84
83
  >
85
- ${this.prefixIcon ? c`<nys-icon size="16" name=${this.prefixIcon}></nys-icon>` : ""}
84
+ ${this.prefixIcon ? u`<nys-icon size="16" name=${this.prefixIcon}></nys-icon>` : ""}
86
85
  ${this.label}</a
87
- >` : c`
86
+ >` : u`
88
87
  <button
89
88
  class=${this.disabled ? "disabled" : ""}
90
89
  type="button"
@@ -95,49 +94,49 @@ const m = class m extends _ {
95
94
  ?disabled=${this.disabled}
96
95
  @click="${this._handleClick}"
97
96
  >
98
- ${this.prefixIcon ? c`<nys-icon size="16" name=${this.prefixIcon}></nys-icon>` : ""}
97
+ ${this.prefixIcon ? u`<nys-icon size="16" name=${this.prefixIcon}></nys-icon>` : ""}
99
98
  ${this.label}
100
99
  </button>
101
100
  `}
102
101
  </li>`;
103
102
  }
104
103
  };
105
- m.styles = b(g), m.shadowRootOptions = {
104
+ m.styles = g(b), m.shadowRootOptions = {
106
105
  ...y.shadowRootOptions,
107
106
  delegatesFocus: !0
108
107
  };
109
108
  let l = m;
110
- h([
109
+ c([
111
110
  d({ type: String })
112
111
  ], l.prototype, "label");
113
- h([
112
+ c([
114
113
  d({ type: String })
115
114
  ], l.prototype, "href");
116
- h([
115
+ c([
117
116
  d({ type: Boolean, reflect: !0 })
118
117
  ], l.prototype, "disabled");
119
- h([
118
+ c([
120
119
  d({ type: String })
121
120
  ], l.prototype, "target");
122
- h([
121
+ c([
123
122
  d({ type: String })
124
123
  ], l.prototype, "prefixIcon");
125
- h([
124
+ c([
126
125
  d({ type: String })
127
126
  ], l.prototype, "divider");
128
127
  customElements.get("nys-dropdownmenuitem") || customElements.define("nys-dropdownmenuitem", l);
129
- var C = Object.defineProperty, f = (a, e, t, n) => {
130
- for (var o = void 0, i = a.length - 1, s; i >= 0; i--)
131
- (s = a[i]) && (o = s(e, t, o) || o);
128
+ var C = Object.defineProperty, w = (a, e, t, n) => {
129
+ for (var o = void 0, i = a.length - 1, r; i >= 0; i--)
130
+ (r = a[i]) && (o = r(e, t, o) || o);
132
131
  return o && C(e, t, o), o;
133
132
  };
134
- const w = class w extends _ {
133
+ const f = class f extends _ {
135
134
  /**
136
135
  * Lifecycle Methods
137
136
  * --------------------------------------------------------------------------
138
137
  */
139
138
  constructor() {
140
- super(), this.for = "", this.showDropdown = !1, this.label = "", this.position = null, this._trigger = null, this._menuElement = null, this._ariaTarget = null, this._lastFocusedIndex = 0, this.GAP = 4, this._resizeObserver = null, this._toggleDropdown = async () => {
139
+ super(), this.for = "", this.showDropdown = !1, this.label = "", this.position = null, this._trigger = null, this._menuElement = null, this._ariaTarget = null, this._lastFocusedIndex = 0, this.GAP = 4, this._resizeObserver = null, this._wiredFor = null, this._toggleDropdown = async () => {
141
140
  this.showDropdown = !this.showDropdown, this._ariaTarget?.setAttribute("aria-expanded", String(this.showDropdown)), this.showDropdown ? (window.addEventListener("scroll", this._handleWindowScroll, !0), this._resizeObserver = new ResizeObserver(() => {
142
141
  this.showDropdown && this._positionMenu();
143
142
  }), this._resizeObserver.observe(document.documentElement), document.addEventListener("click", this._handleDocumentClick), this._menuElement = this.shadowRoot?.querySelector(
@@ -189,7 +188,18 @@ const w = class w extends _ {
189
188
  super.disconnectedCallback();
190
189
  }
191
190
  async firstUpdated() {
192
- await this.updateComplete, this.applyInverseTransform(), this._connectTrigger(), this._handleMenuClick();
191
+ await this.updateComplete, this.applyInverseTransform(), this._handleMenuClick();
192
+ }
193
+ // `for` is wired here rather than once in firstUpdated: under SSR (e.g.
194
+ // Next.js) the wrapper element upgrades with `for` still at its default
195
+ // ("") — the real value only arrives afterward as a property set by the
196
+ // framework wrapper — so wiring once in firstUpdated would silently wire
197
+ // nothing and the menu would never open. Re-wiring on every `for` change
198
+ // (including the first, since `updated()` also runs after the initial
199
+ // render) picks up that later-arriving value; the id-comparison guard
200
+ // keeps unrelated updates from re-wiring the same trigger.
201
+ updated(e) {
202
+ e.has("for") && this.for !== this._wiredFor && (this._disconnectTrigger(), this.for && this.updateComplete.then(() => this._connectTrigger()));
193
203
  }
194
204
  /**
195
205
  * Functions
@@ -202,11 +212,11 @@ const w = class w extends _ {
202
212
  if (t) return t;
203
213
  const n = (o) => {
204
214
  for (const i of Array.from(o.querySelectorAll("*"))) {
205
- const s = i.shadowRoot;
206
- if (s) {
207
- const r = s.getElementById(e);
208
- if (r) return r;
209
- const p = n(s);
215
+ const r = i.shadowRoot;
216
+ if (r) {
217
+ const s = r.getElementById(e);
218
+ if (s) return s;
219
+ const p = n(r);
210
220
  if (p) return p;
211
221
  }
212
222
  }
@@ -217,10 +227,17 @@ const w = class w extends _ {
217
227
  _connectTrigger() {
218
228
  const e = this._findTrigger();
219
229
  if (!e) return;
220
- this._trigger = e;
230
+ this._trigger = e, this._wiredFor = this.for;
221
231
  const t = e.tagName.toLowerCase() === "nys-button" ? e.shadowRoot?.querySelector("button") ?? e : e;
222
232
  t.setAttribute("aria-haspopup", "menu"), t.setAttribute("aria-expanded", "false"), this._ariaTarget = t, this._trigger.addEventListener("click", this._toggleDropdown), this._trigger.addEventListener("keydown", this._handleTriggerKeydown);
223
233
  }
234
+ // Undoes _connectTrigger: removes the listeners and aria wiring from
235
+ // whatever trigger is currently connected, and closes the menu if it was
236
+ // open against that trigger. Called before wiring a new trigger (`for`
237
+ // changed) so a stale trigger never keeps listening.
238
+ _disconnectTrigger() {
239
+ this.showDropdown && this._closeDropdown(), this._trigger && (this._trigger.removeEventListener("click", this._toggleDropdown), this._trigger.removeEventListener("keydown", this._handleTriggerKeydown)), this._ariaTarget && (this._ariaTarget.removeAttribute("aria-haspopup"), this._ariaTarget.removeAttribute("aria-expanded")), this._trigger = null, this._ariaTarget = null, this._wiredFor = null;
240
+ }
224
241
  _closeDropdown() {
225
242
  this.showDropdown = !1, this._ariaTarget?.setAttribute("aria-expanded", "false"), this._trigger?.focus();
226
243
  }
@@ -288,7 +305,7 @@ const w = class w extends _ {
288
305
  };
289
306
  }
290
307
  _checkPositionFits(e, t, n) {
291
- const o = n.width, i = n.height, [s, r] = e.split("-"), p = s === "bottom" ? t.bottom >= i + this.GAP : t.top >= i + this.GAP, v = r === "start" ? t.end >= o : t.start >= o;
308
+ const o = n.width, i = n.height, [r, s] = e.split("-"), p = r === "bottom" ? t.bottom >= i + this.GAP : t.top >= i + this.GAP, v = s === "start" ? t.end >= o : t.start >= o;
292
309
  return p && v;
293
310
  }
294
311
  /**
@@ -299,7 +316,7 @@ const w = class w extends _ {
299
316
  * @param menuRect
300
317
  */
301
318
  _findBestAlternative(e, t, n) {
302
- const [o, i] = e.split("-"), s = [
319
+ const [o, i] = e.split("-"), r = [
303
320
  `${o === "bottom" ? "top" : "bottom"}-${i}`,
304
321
  // Flip vertical
305
322
  `${o}-${i === "start" ? "end" : "start"}`,
@@ -307,9 +324,9 @@ const w = class w extends _ {
307
324
  `${o === "bottom" ? "top" : "bottom"}-${i === "start" ? "end" : "start"}`
308
325
  // Flip both
309
326
  ];
310
- for (const r of s)
311
- if (this._checkPositionFits(r, t, n))
312
- return r;
327
+ for (const s of r)
328
+ if (this._checkPositionFits(s, t, n))
329
+ return s;
313
330
  return this._findMostAvailableSpace(t);
314
331
  }
315
332
  _findMostAvailableSpace(e) {
@@ -326,8 +343,8 @@ const w = class w extends _ {
326
343
  if (!this._trigger || !this._menuElement)
327
344
  return { top: 0, left: 0 };
328
345
  const t = this._trigger.getBoundingClientRect(), n = this._menuElement.getBoundingClientRect(), [o, i] = e.split("-");
329
- let s = 0, r = 0;
330
- return o === "bottom" ? s = t.bottom + this.GAP : s = t.top - n.height - this.GAP, i === "start" ? r = t.left : r = t.right - n.width, { top: s, left: r };
346
+ let r = 0, s = 0;
347
+ return o === "bottom" ? r = t.bottom + this.GAP : r = t.top - n.height - this.GAP, i === "start" ? s = t.left : s = t.right - n.width, { top: r, left: s };
331
348
  }
332
349
  _applyPosition(e) {
333
350
  this._menuElement && (this._menuElement.style.top = `${e.top}px`, this._menuElement.style.left = `${e.left}px`);
@@ -343,7 +360,7 @@ const w = class w extends _ {
343
360
  });
344
361
  }
345
362
  render() {
346
- return c`<div
363
+ return u`<div
347
364
  class="nys-dropdownmenu ${this.showDropdown ? "active" : ""}"
348
365
  for=${this.for}
349
366
  ?hidden=${!this.showDropdown}
@@ -354,23 +371,23 @@ const w = class w extends _ {
354
371
  </div>`;
355
372
  }
356
373
  };
357
- w.styles = b(g);
358
- let u = w;
359
- f([
374
+ f.styles = g(b);
375
+ let h = f;
376
+ w([
360
377
  d({ type: String, reflect: !0 })
361
- ], u.prototype, "for");
362
- f([
378
+ ], h.prototype, "for");
379
+ w([
363
380
  d({ type: Boolean })
364
- ], u.prototype, "showDropdown");
365
- f([
381
+ ], h.prototype, "showDropdown");
382
+ w([
366
383
  d({ type: String })
367
- ], u.prototype, "label");
368
- f([
384
+ ], h.prototype, "label");
385
+ w([
369
386
  d({ type: String, reflect: !0 })
370
- ], u.prototype, "position");
371
- customElements.get("nys-dropdownmenu") || customElements.define("nys-dropdownmenu", u);
387
+ ], h.prototype, "position");
388
+ customElements.get("nys-dropdownmenu") || customElements.define("nys-dropdownmenu", h);
372
389
  export {
373
- u as NysDropdownMenu,
390
+ h as NysDropdownMenu,
374
391
  l as NysDropdownMenuItem
375
392
  };
376
393
  //# sourceMappingURL=nys-dropdownmenu.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"nys-dropdownmenu.js","sources":["../../internals/dist/index.js","../src/nys-dropdownmenuitem.ts","../src/nys-dropdownmenu.ts"],"sourcesContent":["import { LitElement as u } from \"lit\";\n/*!\n * New York State Design System v1.21.0\n * Description: A design system for New York State's digital products.\n * Repository: https://github.com/its-hcd/nysds\n * License: MIT\n */\nvar y = 0;\nfunction f(e) {\n return `${e}-${Date.now()}-${y++}`;\n}\nvar p = {\n labelledby: \"ariaLabelledByElements\",\n describedby: \"ariaDescribedByElements\"\n}, v = {\n labelledby: \"ariaLabel\",\n describedby: \"ariaDescription\"\n};\nfunction A(e) {\n return \"ariaDescribedByElements\" in e;\n}\nfunction R(e) {\n return e === \"errormessage\" ? \"aria-errormessage\" : `aria-${e}`;\n}\nfunction d(e) {\n return e.filter((r) => !!r);\n}\nfunction _(e) {\n return e.map((r) => (r.id || (r.id = f(\"nys-ref\")), r.id));\n}\nfunction V(e, r, a) {\n const t = d(a), n = R(r);\n t.length ? e.setAttribute(n, _(t).join(\" \")) : e.removeAttribute(n);\n}\nfunction x(e, r, a, t, n) {\n const i = d(t), s = p[a], l = r;\n if (s && A(r)) {\n l[s] = i.length ? i : null;\n return;\n }\n const o = v[a];\n if (!o) return;\n const c = n ?? (i.length ? i.map((m) => m.textContent?.trim() ?? \"\").filter(Boolean).join(\" \") : \"\");\n o in l ? l[o] = c || null : c ? e.setAttribute(a === \"labelledby\" ? \"aria-label\" : \"aria-description\", c) : e.removeAttribute(a === \"labelledby\" ? \"aria-label\" : \"aria-description\");\n}\nvar E = {\n labelledby: \"ariaLabelledByElements\",\n describedby: \"ariaDescribedByElements\"\n}, C = {\n labelledby: \"aria-label\",\n describedby: \"aria-description\"\n};\nfunction L(e, r, a) {\n const t = d(a), n = E[r], i = C[r], s = e;\n if (n && n in e && (s[n] = t.length ? t : null), !i) return;\n const l = t.map((o) => o.textContent?.trim() ?? \"\").filter(Boolean).join(\" \");\n l ? e.setAttribute(i, l) : e.removeAttribute(i);\n}\nvar b = (e) => {\n class r 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 r;\n}, P = /* @__PURE__ */ b(u), h = (e) => {\n class r extends b(e) {\n constructor(...t) {\n super(...t), this.__internals = null;\n }\n get internals() {\n if (this.__internals) return this.__internals;\n if (typeof this.attachInternals == \"function\") 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(t, n) {\n const i = this.internals;\n if (i && t in i) {\n i[t] = n;\n return;\n }\n const s = g(t);\n n === null ? this.removeAttribute(s) : this.setAttribute(s, n);\n }\n reflectDefaultSemantics() {\n const t = this.defaultRole;\n t && this.setHostAria(\"role\", t);\n }\n connectedCallback() {\n super.connectedCallback(), this.reflectDefaultSemantics();\n }\n }\n return r;\n};\nfunction g(e) {\n if (e === \"role\") return \"role\";\n const r = e.replace(/^aria/, \"\");\n return \"aria-\" + r.charAt(0).toLowerCase() + r.slice(1);\n}\nvar T = /* @__PURE__ */ h(u), I = (e) => {\n class r extends h(e) {\n static {\n this.formAssociated = !0;\n }\n setFormValue(t) {\n this.internals?.setFormValue(t ?? null);\n }\n setValidityFromState(t, n, i) {\n const s = this.internals;\n if (!s) return;\n const l = Object.values(t).some(Boolean);\n l ? s.setValidity(t, n ?? \"Invalid value\", i) : s.setValidity({}), this.setHostAria(\"ariaInvalid\", l ? \"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 return r;\n}, w = /* @__PURE__ */ I(u);\nfunction O(e) {\n const r = e.tagName.toLowerCase(), a = /* @__PURE__ */ new Set(), t = (n) => {\n n.querySelectorAll(\"*\").forEach((i) => {\n const s = i.tagName.toLowerCase();\n s.startsWith(\"nys-\") && s !== r && !customElements.get(s) && a.add(s), i.shadowRoot && t(i.shadowRoot);\n });\n };\n return e.shadowRoot && t(e.shadowRoot), t(e), [...a].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 A as supportsElementRefs\n};\n\n//# sourceMappingURL=index.js.map","import { LitElement, html, unsafeCSS } from \"lit\";\nimport { property } from \"lit/decorators.js\";\nimport { NysElement } from \"@nysds/internals\";\n// This element is rendered inside this component's shadow DOM (the prefix\n// icon), so it must be registered whenever nys-dropdownmenuitem is used.\n// Importing it here (intentional side effect) guarantees it always renders.\nimport \"@nysds/nys-icon\";\n// @ts-ignore: SCSS module imported via bundler as inline\nimport styles from \"./nys-dropdownmenu.scss?inline\";\n\n/**\n * **Slotted component.** Displays an individual dropdown item within `nys-dropdown` with label.\n *\n * The `nys-dropdownitem` is used as customizable item within the dropdown so users don't have to raw code <ul>, <li>, <a href>\n * and have the benefit of default customization.\n *\n * @summary Dropdown item to display label and provide href link.\n * @element nys-dropdownmenuitem\n */\nexport class NysDropdownMenuItem extends NysElement {\n static styles = unsafeCSS(styles);\n static shadowRootOptions = {\n ...LitElement.shadowRootOptions,\n delegatesFocus: true,\n };\n\n @property({ type: String }) label = \"\";\n @property({ type: String }) href = \"\";\n @property({ type: Boolean, reflect: true }) disabled = false;\n @property({ type: String }) target = \"_self\";\n @property({ type: String }) prefixIcon = \"\";\n @property({ type: String }) divider = \"\";\n\n // super.connectedCallback() (NysElement) auto-assigns this.id when\n // one is not provided (prefix = localName, i.e. \"nys-dropdownmenuitem-<ts>-<n>\").\n // role=\"menuitem\" intentionally stays on the inner <a>/<button>, so this\n // component keeps defaultRole = null and does not move a role onto the host.\n connectedCallback() {\n super.connectedCallback();\n }\n\n private _handleClick(e: Event) {\n if (this.disabled) {\n e.preventDefault();\n return;\n }\n\n this.dispatchEvent(\n new CustomEvent(\"nys-click\", {\n bubbles: true,\n composed: true,\n detail: {\n id: this.id,\n label: this.label,\n ...(this.href && { href: this.href }),\n },\n }),\n );\n }\n\n render() {\n const isLink = !!this.href;\n\n return html`<li class=\"nys-dropdownmenuitem\" role=\"presentation\">\n ${isLink\n ? html` <a\n class=${this.disabled ? \"disabled\" : \"\"}\n href=${this.disabled ? \"\" : this.href}\n role=\"menuitem\"\n aria-disabled=\"${this.disabled ? \"true\" : \"false\"}\"\n aria-label=${this.label}\n tabindex=${this.disabled ? \"-1\" : \"0\"}\n @click=\"${this._handleClick}\"\n target=\"${this.target}\"\n >\n ${this.prefixIcon\n ? html`<nys-icon size=\"16\" name=${this.prefixIcon}></nys-icon>`\n : \"\"}\n ${this.label}</a\n >`\n : html`\n <button\n class=${this.disabled ? \"disabled\" : \"\"}\n type=\"button\"\n role=\"menuitem\"\n aria-disabled=\"${this.disabled ? \"true\" : \"false\"}\"\n aria-label=${this.label}\n tabindex=${this.disabled ? \"-1\" : \"0\"}\n ?disabled=${this.disabled}\n @click=\"${this._handleClick}\"\n >\n ${this.prefixIcon\n ? html`<nys-icon size=\"16\" name=${this.prefixIcon}></nys-icon>`\n : \"\"}\n ${this.label}\n </button>\n `}\n </li>`;\n }\n}\n\nif (!customElements.get(\"nys-dropdownmenuitem\")) {\n customElements.define(\"nys-dropdownmenuitem\", NysDropdownMenuItem);\n}\n","import { html, unsafeCSS } from \"lit\";\nimport { property } from \"lit/decorators.js\";\nimport { NysElement } from \"@nysds/internals\";\nimport \"./nys-dropdownmenuitem\";\n// @ts-ignore: SCSS module imported via bundler as inline\nimport styles from \"./nys-dropdownmenu.scss?inline\";\n\ntype Position = \"bottom-start\" | \"bottom-end\" | \"top-start\" | \"top-end\";\n\ninterface PositionCoordinates {\n top: number;\n left: number;\n}\n\ninterface SpaceAvailable {\n top: number;\n bottom: number;\n start: number;\n end: number;\n}\n\n/**\n * Dropdown menus enable users to select an action from a list of options.\n * They’re commonly used to save space by grouping related actions, or to provide actions in a confined space.\n *\n * @summary Action menu with auto-positioning, keyboard support, and screen reader integration.\n * @element nys-dropdownmenu\n *\n * @example Basic\n * ```html\n * <nys-button id=\"my-trigger-id\" label=\"Open Menu\"></nys-button>\n * <nys-dropdownmenu id=\"my-dropdownmenu\" for=\"my-trigger-id\">\n * <nys-dropdownmenuitem label=\"Profile\" href=\"/profile\"></nys-dropdownmenuitem>\n * <nys-dropdownmenuitem label=\"Repositories & Github Pages\" href=\"/repos\"></nys-dropdownmenuitem>\n * <nys-dropdownmenuitem label=\"Organizations\" href=\"/organizations\"></nys-dropdownmenuitem>\n * <nys-dropdownmenuitem label=\"Sign out\" href=\"/logout\"></nys-dropdownmenuitem>\n * </nys-dropdownmenu>\n * </div>\n * ```\n *\n * @render Basic\n * ```html\n * <div style=\"margin: 200px; display: flex; justify-content: center; align-items: center;\">\n * <nys-button id=\"my-trigger-id\" label=\"Open Menu\"></nys-button>\n * <nys-dropdownmenu id=\"my-dropdownmenu\" for=\"my-trigger-id\">\n * <nys-dropdownmenuitem label=\"Profile\" href=\"/profile\"></nys-dropdownmenuitem>\n * <nys-dropdownmenuitem label=\"Repositories & Github Pages\" href=\"/repos\"></nys-dropdownmenuitem>\n * <nys-dropdownmenuitem label=\"Organizations\" href=\"/organizations\"></nys-dropdownmenuitem>\n * <nys-dropdownmenuitem label=\"Sign out\" href=\"/logout\"></nys-dropdownmenuitem>\n * </nys-dropdownmenu>\n * </div>\n * ```\n */\n\nexport class NysDropdownMenu extends NysElement {\n static styles = unsafeCSS(styles);\n\n @property({ type: String, reflect: true }) for = \"\";\n @property({ type: Boolean }) showDropdown = false;\n\n /**\n * Accessible label for the menu (`role=\"menu\"`) container. Used as `aria-label`\n * on the `<ul role=\"menu\">` element. When empty, falls back to \"Menu\" so the\n * menu always has an accessible name for screen readers.\n * @default \"\"\n */\n @property({ type: String }) label = \"\";\n\n /**\n * Preferred position relative to trigger.\n * @default \"bottom-end\"\n */\n @property({ type: String, reflect: true }) position: Position | null = null;\n\n private _trigger: HTMLElement | null = null;\n private _menuElement: HTMLElement | null = null;\n private _ariaTarget: HTMLElement | null = null;\n private _lastFocusedIndex: number = 0;\n private readonly GAP = 4;\n private _resizeObserver: ResizeObserver | null = null;\n\n /**\n * Lifecycle Methods\n * --------------------------------------------------------------------------\n */\n\n constructor() {\n super();\n }\n\n // super.connectedCallback() (NysElement) auto-assigns this.id when\n // one is not provided (prefix = localName, i.e. \"nys-dropdownmenu-<ts>-<n>\").\n // The menu/menuitem roles intentionally stay on the inner <ul>/items, so this\n // component keeps defaultRole = null and does not move a role onto the host.\n connectedCallback() {\n super.connectedCallback();\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n }\n\n async firstUpdated() {\n await this.updateComplete;\n this.applyInverseTransform();\n this._connectTrigger();\n this._handleMenuClick();\n }\n /**\n * Functions\n * --------------------------------------------------------------------------\n */\n\n private _findTrigger() {\n const targetId = this.for;\n if (!targetId) return null;\n\n // lightDOM search\n let htmlElement = document.getElementById(targetId);\n if (htmlElement) return htmlElement;\n\n // Search recursively through shadow DOMs (e.g. for nys-label within other component's shadowDOM)\n const findInShadows = (root: ParentNode): HTMLElement | null => {\n for (const el of Array.from(root.querySelectorAll(\"*\"))) {\n const shadowElement = el.shadowRoot;\n if (shadowElement) {\n const found = shadowElement.getElementById(targetId);\n if (found) return found;\n\n const deeper = findInShadows(shadowElement);\n if (deeper) return deeper;\n }\n }\n return null;\n };\n\n return findInShadows(document);\n }\n\n private _connectTrigger() {\n const trigger = this._findTrigger();\n if (!trigger) return;\n\n this._trigger = trigger;\n\n const ariaTarget =\n trigger.tagName.toLowerCase() === \"nys-button\"\n ? (trigger.shadowRoot?.querySelector(\"button\") ?? trigger)\n : trigger;\n\n ariaTarget.setAttribute(\"aria-haspopup\", \"menu\");\n ariaTarget.setAttribute(\"aria-expanded\", \"false\");\n\n this._ariaTarget = ariaTarget;\n\n this._trigger.addEventListener(\"click\", this._toggleDropdown);\n this._trigger.addEventListener(\"keydown\", this._handleTriggerKeydown);\n }\n\n private _toggleDropdown = async () => {\n this.showDropdown = !this.showDropdown;\n\n this._ariaTarget?.setAttribute(\"aria-expanded\", String(this.showDropdown));\n\n if (this.showDropdown) {\n window.addEventListener(\"scroll\", this._handleWindowScroll, true);\n this._resizeObserver = new ResizeObserver(() => {\n if (this.showDropdown) {\n this._positionMenu();\n }\n });\n this._resizeObserver.observe(document.documentElement);\n\n document.addEventListener(\"click\", this._handleDocumentClick);\n\n this._menuElement = this.shadowRoot?.querySelector(\n \".nys-dropdownmenu\",\n ) as HTMLElement | null;\n this._menuElement!.addEventListener(\"keydown\", this._handleMenuKeydown);\n\n await this.updateComplete;\n this._positionMenu();\n this._focusOnItem(this._lastFocusedIndex);\n } else {\n window.removeEventListener(\"scroll\", this._handleWindowScroll, true);\n document.removeEventListener(\"click\", this._handleDocumentClick);\n this._menuElement!.removeEventListener(\n \"keydown\",\n this._handleMenuKeydown,\n );\n\n this._resizeObserver?.disconnect();\n this._resizeObserver = null;\n }\n };\n\n private _closeDropdown() {\n this.showDropdown = false;\n this._ariaTarget?.setAttribute(\"aria-expanded\", \"false\");\n this._trigger?.focus();\n }\n\n private _getMenuItems(): HTMLElement[] {\n const slot = this.shadowRoot?.querySelector(\"slot\");\n const assigned = slot?.assignedElements({ flatten: true }) || [];\n\n return assigned.filter(\n (el) => el && !el.hasAttribute(\"disabled\"),\n ) as HTMLElement[];\n }\n\n private _handleDocumentClick = (event: MouseEvent) => {\n if (!this.showDropdown) return;\n\n const path = event?.composedPath();\n\n const clickedInsideMenu = path.includes(this);\n const clickedTrigger = this._trigger && path.includes(this._trigger);\n\n if (!clickedInsideMenu && !clickedTrigger) {\n this._closeDropdown();\n }\n };\n\n private async _focusOnItem(index: number = 0) {\n await new Promise((resolve) => requestAnimationFrame(resolve));\n const items = this._getMenuItems();\n const target = items[Math.min(index, items.length - 1)];\n if (!target) return;\n\n target.focus();\n }\n\n // In some iframes (like Storybook's) or embedded containers , parent elements may have CSS transforms applied, creating a new coordinate context.\n // This function removes such transforms to prevent them from affecting tooltip positioning calculations.\n private applyInverseTransform() {\n document.querySelectorAll('div[scale=\"1\"]').forEach((el) => {\n (el as HTMLElement).style.transform = \"none\";\n });\n }\n\n /**\n * Position Logic\n * --------------------------------------------------------------------------\n */\n\n /**\n * The controller function for positioning the dropdown menu.\n * The logic diverts to if user sets position or we auto position the dropdown menu\n */\n private _positionMenu() {\n if (!this._trigger) return;\n\n this._menuElement = this.shadowRoot?.querySelector(\n \".nys-dropdownmenu\",\n ) as HTMLElement | null;\n if (!this._menuElement) return;\n\n // Decide position based on user preference OR auto position if no position chosen\n const finalPosition = this.position\n ? this._setUserPosition(this.position)\n : this._autoPosition();\n\n // Apply the calculated position\n const coords = this._calculateCoordinates(finalPosition);\n this._applyPosition(coords);\n }\n\n private _setUserPosition(userPosition: Position): Position {\n const space = this._checkSpaceAvailable();\n const menuRect = this._menuElement!.getBoundingClientRect();\n\n const userPositionFits = this._checkPositionFits(\n userPosition,\n space,\n menuRect,\n );\n\n if (userPositionFits) {\n return userPosition;\n }\n\n // User position doesn't fit, find best alternative\n return this._findBestAlternative(userPosition, space, menuRect);\n }\n\n /**\n * Auto Positioning of the dropdown menu relies on the best surrounding space available\n * to select the desirable position.\n */\n private _autoPosition(): Position {\n const space = this._checkSpaceAvailable();\n const menuRect = this._menuElement!.getBoundingClientRect();\n\n const defaultPosition: Position = \"bottom-end\";\n\n if (this._checkPositionFits(defaultPosition, space, menuRect)) {\n return defaultPosition;\n }\n\n return this._findBestAlternative(defaultPosition, space, menuRect);\n }\n\n /**\n * Checks if the dropdown menu fits inside the viewport on the given side of the trigger.\n * Overrides user set position for auto-positioning if user's desire space is not available\n */\n private _checkSpaceAvailable(): SpaceAvailable {\n if (!this._trigger) {\n return { top: 0, bottom: 0, start: 0, end: 0 };\n }\n\n const triggerRect = this._trigger.getBoundingClientRect();\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n\n return {\n top: triggerRect.top,\n bottom: viewportHeight - triggerRect.bottom,\n start: triggerRect.left,\n end: viewportWidth - triggerRect.right,\n };\n }\n\n private _checkPositionFits(\n position: Position,\n space: SpaceAvailable,\n menuRect: DOMRect,\n ): boolean {\n const menuWidth = menuRect.width;\n const menuHeight = menuRect.height;\n\n const [vertical, horizontal] = position.split(\"-\") as [\n \"top\" | \"bottom\",\n \"start\" | \"end\",\n ];\n\n const verticalFits =\n vertical === \"bottom\"\n ? space.bottom >= menuHeight + this.GAP\n : space.top >= menuHeight + this.GAP;\n\n const horizontalFits =\n horizontal === \"start\"\n ? space.end >= menuWidth\n : space.start >= menuWidth;\n return verticalFits && horizontalFits;\n }\n\n /**\n * This position is called for when user's set position didn't fit OR auto positioning when default position doesn't fit\n * We look for the best alternative positions in order of preference base on the set position (e.g. bottom-start => bottom-end).\n * @param userPosition\n * @param space\n * @param menuRect\n */\n private _findBestAlternative(\n userPosition: Position,\n space: SpaceAvailable,\n menuRect: DOMRect,\n ): Position {\n const [vertical, horizontal] = userPosition.split(\"-\") as [\n \"top\" | \"bottom\",\n \"start\" | \"end\",\n ];\n\n const alternatives: Position[] = [\n `${vertical === \"bottom\" ? \"top\" : \"bottom\"}-${horizontal}`, // Flip vertical\n `${vertical}-${horizontal === \"start\" ? \"end\" : \"start\"}`, // Flip horizontal\n `${vertical === \"bottom\" ? \"top\" : \"bottom\"}-${horizontal === \"start\" ? \"end\" : \"start\"}`, // Flip both\n ];\n\n for (const altPosition of alternatives) {\n if (this._checkPositionFits(altPosition, space, menuRect)) {\n return altPosition;\n }\n }\n\n // Fallback: nothing fits, return position with most space\n return this._findMostAvailableSpace(space) as Position;\n }\n\n private _findMostAvailableSpace(space: SpaceAvailable): Position {\n const vertical = space.bottom >= space.top ? \"bottom\" : \"top\";\n const horizontal = space.start >= space.end ? \"start\" : \"end\";\n\n return `${vertical}-${horizontal}`;\n }\n\n /**\n * A valid ideal position has been chosen.\n * This function calculates the coordinate of the trigger to properly position the dropdown menu.\n * @param position\n * @returns\n */\n private _calculateCoordinates(position: Position): PositionCoordinates {\n if (!this._trigger || !this._menuElement) {\n return { top: 0, left: 0 };\n }\n\n const triggerRect = this._trigger.getBoundingClientRect();\n const menuRect = this._menuElement.getBoundingClientRect();\n\n const [vertical, horizontal] = position.split(\"-\") as [\n \"top\" | \"bottom\",\n \"start\" | \"end\",\n ];\n\n let top = 0;\n let left = 0;\n\n // Vertical position\n if (vertical === \"bottom\") {\n top = triggerRect.bottom + this.GAP;\n } else {\n top = triggerRect.top - menuRect.height - this.GAP;\n }\n\n // Horizontal position\n if (horizontal === \"start\") {\n left = triggerRect.left;\n } else {\n left = triggerRect.right - menuRect.width;\n }\n\n return { top, left };\n }\n\n private _applyPosition(coords: PositionCoordinates) {\n if (!this._menuElement) return;\n\n this._menuElement.style.top = `${coords.top}px`;\n this._menuElement.style.left = `${coords.left}px`;\n }\n\n /**\n * Event Handlers\n * --------------------------------------------------------------------------\n */\n\n private _handleMenuClick() {\n this.addEventListener(\"nys-click\", (e: Event) => {\n const items = this._getMenuItems();\n const targetCrumb = (e as CustomEvent).detail?.id;\n const index = items.findIndex((item) => item.id === targetCrumb);\n if (index !== -1) this._lastFocusedIndex = index;\n this._closeDropdown();\n });\n }\n\n private _handleTriggerKeydown = (event: KeyboardEvent) => {\n if (event.defaultPrevented) return;\n\n if (event.key === \"Escape\" && this.showDropdown) {\n event.preventDefault();\n this._closeDropdown();\n }\n };\n\n private _handleMenuKeydown = (event: KeyboardEvent) => {\n const items = this._getMenuItems();\n const currentIndex = items.indexOf(document.activeElement as HTMLElement);\n\n switch (event.key) {\n case \"Escape\":\n event.preventDefault();\n this._closeDropdown();\n break;\n case \"ArrowDown\":\n case \"ArrowRight\":\n event.preventDefault();\n const nextIndex =\n currentIndex < items.length - 1 ? currentIndex + 1 : 0;\n this._lastFocusedIndex = nextIndex;\n items[nextIndex].focus();\n break;\n case \"ArrowUp\":\n case \"ArrowLeft\":\n event.preventDefault();\n const prevIndex =\n currentIndex > 0 ? currentIndex - 1 : items.length - 1;\n this._lastFocusedIndex = prevIndex;\n items[prevIndex].focus();\n break;\n case \"Tab\":\n if (currentIndex >= items.length - 1 && !event.shiftKey) {\n this._closeDropdown();\n }\n break;\n default:\n break;\n }\n };\n\n private _handleWindowScroll = () => {\n if (this.showDropdown) {\n this._positionMenu();\n }\n };\n\n render() {\n return html`<div\n class=\"nys-dropdownmenu ${this.showDropdown ? \"active\" : \"\"}\"\n for=${this.for}\n ?hidden=${!this.showDropdown}\n >\n <ul role=\"menu\" aria-label=${this.label || \"Menu\"}>\n <slot></slot>\n </ul>\n </div>`;\n }\n}\n\nif (!customElements.get(\"nys-dropdownmenu\")) {\n customElements.define(\"nys-dropdownmenu\", NysDropdownMenu);\n}\n"],"names":["LitElement","unsafeCSS","html","property","y","f","e","b","r","P","u","_NysDropdownMenuItem","NysElement","isLink","styles","NysDropdownMenuItem","__decorateClass","_NysDropdownMenu","event","path","clickedInsideMenu","clickedTrigger","items","currentIndex","nextIndex","prevIndex","targetId","htmlElement","findInShadows","root","el","shadowElement","found","deeper","trigger","ariaTarget","index","resolve","target","finalPosition","coords","userPosition","space","menuRect","defaultPosition","triggerRect","viewportWidth","viewportHeight","position","menuWidth","menuHeight","vertical","horizontal","verticalFits","horizontalFits","alternatives","altPosition","top","left","targetCrumb","item","NysDropdownMenu"],"mappings":"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"}
1
+ {"version":3,"file":"nys-dropdownmenu.js","sources":["../../internals/dist/index.js","../src/nys-dropdownmenuitem.ts","../src/nys-dropdownmenu.ts"],"sourcesContent":["import { LitElement as u } from \"lit\";\n/*!\n * New York State Design System v1.22.0\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}\nfunction N(e, t, s) {\n e.dispatchEvent(\n new CustomEvent(t, { detail: s, bubbles: !0, composed: !0 })\n );\n}\nfunction I(e, t) {\n e.dispatchEvent(\n new Event(`nys-${t}`, { bubbles: !0, composed: !0 })\n );\n}\nconst p = {\n labelledby: \"ariaLabelledByElements\",\n describedby: \"ariaDescribedByElements\"\n}, g = {\n labelledby: \"ariaLabel\",\n describedby: \"ariaDescription\"\n};\nfunction _(e) {\n return \"ariaDescribedByElements\" in e;\n}\nfunction E(e) {\n return e === \"errormessage\" ? \"aria-errormessage\" : `aria-${e}`;\n}\nfunction d(e) {\n return e.filter((t) => !!t);\n}\nfunction A(e) {\n return e.map((t) => (t.id || (t.id = f(\"nys-ref\")), t.id));\n}\nfunction w(e, t, s) {\n const n = d(s), i = E(t);\n n.length ? e.setAttribute(i, A(n).join(\" \")) : e.removeAttribute(i);\n}\nfunction x(e, t, s, n, i) {\n const r = d(n), a = p[s], l = t;\n if (a && _(t)) {\n l[a] = r.length ? r : null;\n return;\n }\n const o = g[s];\n if (!o) return;\n const c = i ?? (r.length ? r.map((m) => m.textContent?.trim() ?? \"\").filter(Boolean).join(\" \") : \"\");\n o in l ? l[o] = c || null : c ? e.setAttribute(\n s === \"labelledby\" ? \"aria-label\" : \"aria-description\",\n c\n ) : e.removeAttribute(\n s === \"labelledby\" ? \"aria-label\" : \"aria-description\"\n );\n}\nconst R = {\n labelledby: \"ariaLabelledByElements\",\n describedby: \"ariaDescribedByElements\"\n}, V = {\n labelledby: \"aria-label\",\n describedby: \"aria-description\"\n};\nfunction L(e, t, s) {\n const n = d(s), i = R[t], r = V[t], a = e;\n if (i && i in e && (a[i] = n.length ? n : null), !r) return;\n const l = n.map((o) => o.textContent?.trim() ?? \"\").filter(Boolean).join(\" \");\n l ? e.setAttribute(r, l) : e.removeAttribute(r);\n}\nconst h = (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__ */ h(u), b = (e) => {\n class t extends h(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(n, i) {\n const r = this.internals;\n if (r && n in r) {\n r[n] = i;\n return;\n }\n const a = v(n);\n i === null ? this.removeAttribute(a) : this.setAttribute(a, i);\n }\n reflectDefaultSemantics() {\n const n = this.defaultRole;\n n && this.setHostAria(\"role\", n);\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__ */ b(u), C = (e) => {\n const s = class s extends b(e) {\n constructor() {\n super(...arguments), this.__internalValidationMessage = \"\";\n }\n get internalValidationMessage() {\n return this.__internalValidationMessage;\n }\n set internalValidationMessage(i) {\n i !== this.__internalValidationMessage && (this.__internalValidationMessage = i, this.requestUpdate(\"internalValidationMessage\"));\n }\n resolvedErrorMessage() {\n return this.errorMessage || this.internalValidationMessage;\n }\n setFormValue(i) {\n this.internals?.setFormValue(i ?? null);\n }\n setValidityFromState(i, r, a) {\n const l = this.internals;\n if (!l) return;\n const o = Object.values(i).some(Boolean);\n o ? l.setValidity(i, r ?? \"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.internalValidationMessage = \"\", this.clearValidity();\n }\n };\n s.formAssociated = !0;\n let t = s;\n return t;\n}, O = /* @__PURE__ */ C(u);\nfunction B(e) {\n const t = e.tagName.toLowerCase(), s = /* @__PURE__ */ new Set(), n = (i) => {\n i.querySelectorAll(\"*\").forEach((r) => {\n const a = r.tagName.toLowerCase();\n a.startsWith(\"nys-\") && a !== t && !customElements.get(a) && s.add(a), r.shadowRoot && n(r.shadowRoot);\n });\n };\n return e.shadowRoot && n(e.shadowRoot), n(e), [...s].sort();\n}\nexport {\n C as FormControlMixin,\n h as IdentifiedMixin,\n P as NysElement,\n O as NysFormControlElement,\n T as NysReflectsAriaElement,\n b as ReflectsAriaMixin,\n w as associateControl,\n L as associateControlRefs,\n x as associateHost,\n N as dispatchNysEvent,\n I as dispatchNysFocusBlur,\n B as findUnregisteredChildren,\n f as generateId,\n _ as supportsElementRefs\n};\n//# sourceMappingURL=index.js.map\n","import { LitElement, html, unsafeCSS } from \"lit\";\nimport { property } from \"lit/decorators.js\";\nimport { NysElement } from \"@nysds/internals\";\n// This element is rendered inside this component's shadow DOM (the prefix\n// icon), so it must be registered whenever nys-dropdownmenuitem is used.\n// Importing it here (intentional side effect) guarantees it always renders.\nimport \"@nysds/nys-icon\";\n// @ts-ignore: SCSS module imported via bundler as inline\nimport styles from \"./nys-dropdownmenu.scss?inline\";\n\n/** Detail payload for the `nys-click` event fired by `nys-dropdownmenuitem`. */\nexport interface NysDropdownmenuitemClickDetail {\n id: string;\n label: string;\n href?: string;\n}\n\n/** The `nys-click` event fired by `nys-dropdownmenuitem`. */\nexport type NysDropdownmenuitemClickEvent =\n CustomEvent<NysDropdownmenuitemClickDetail>;\n\n/**\n * **Slotted component.** Displays an individual dropdown item within `nys-dropdown` with label.\n *\n * The `nys-dropdownitem` is used as customizable item within the dropdown so users don't have to raw code <ul>, <li>, <a href>\n * and have the benefit of default customization.\n *\n * @summary Dropdown item to display label and provide href link.\n * @element nys-dropdownmenuitem\n *\n * @fires {NysDropdownmenuitemClickEvent} nys-click - Fired when the item is activated (unless disabled). Detail: `{id, label, href?}` — `href` is present only when set. Bubbles and composed.\n */\nexport class NysDropdownMenuItem extends NysElement {\n static styles = unsafeCSS(styles);\n static shadowRootOptions = {\n ...LitElement.shadowRootOptions,\n delegatesFocus: true,\n };\n\n @property({ type: String }) label = \"\";\n @property({ type: String }) href = \"\";\n @property({ type: Boolean, reflect: true }) disabled = false;\n @property({ type: String }) target = \"_self\";\n @property({ type: String }) prefixIcon = \"\";\n @property({ type: String }) divider = \"\";\n\n // super.connectedCallback() (NysElement) auto-assigns this.id when\n // one is not provided (prefix = localName, i.e. \"nys-dropdownmenuitem-<ts>-<n>\").\n // role=\"menuitem\" intentionally stays on the inner <a>/<button>, so this\n // component keeps defaultRole = null and does not move a role onto the host.\n connectedCallback() {\n super.connectedCallback();\n }\n\n private _handleClick(e: Event) {\n if (this.disabled) {\n e.preventDefault();\n return;\n }\n\n this.dispatchEvent(\n new CustomEvent(\"nys-click\", {\n bubbles: true,\n composed: true,\n detail: {\n id: this.id,\n label: this.label,\n ...(this.href && { href: this.href }),\n },\n }),\n );\n }\n\n render() {\n const isLink = !!this.href;\n\n return html`<li class=\"nys-dropdownmenuitem\" role=\"presentation\">\n ${isLink\n ? html` <a\n class=${this.disabled ? \"disabled\" : \"\"}\n href=${this.disabled ? \"\" : this.href}\n role=\"menuitem\"\n aria-disabled=\"${this.disabled ? \"true\" : \"false\"}\"\n aria-label=${this.label}\n tabindex=${this.disabled ? \"-1\" : \"0\"}\n @click=\"${this._handleClick}\"\n target=\"${this.target}\"\n >\n ${this.prefixIcon\n ? html`<nys-icon size=\"16\" name=${this.prefixIcon}></nys-icon>`\n : \"\"}\n ${this.label}</a\n >`\n : html`\n <button\n class=${this.disabled ? \"disabled\" : \"\"}\n type=\"button\"\n role=\"menuitem\"\n aria-disabled=\"${this.disabled ? \"true\" : \"false\"}\"\n aria-label=${this.label}\n tabindex=${this.disabled ? \"-1\" : \"0\"}\n ?disabled=${this.disabled}\n @click=\"${this._handleClick}\"\n >\n ${this.prefixIcon\n ? html`<nys-icon size=\"16\" name=${this.prefixIcon}></nys-icon>`\n : \"\"}\n ${this.label}\n </button>\n `}\n </li>`;\n }\n}\n\nif (!customElements.get(\"nys-dropdownmenuitem\")) {\n customElements.define(\"nys-dropdownmenuitem\", NysDropdownMenuItem);\n}\n","import { html, unsafeCSS } from \"lit\";\nimport { property } from \"lit/decorators.js\";\nimport { NysElement } from \"@nysds/internals\";\nimport \"./nys-dropdownmenuitem\";\n// @ts-ignore: SCSS module imported via bundler as inline\nimport styles from \"./nys-dropdownmenu.scss?inline\";\n\ntype Position = \"bottom-start\" | \"bottom-end\" | \"top-start\" | \"top-end\";\n\ninterface PositionCoordinates {\n top: number;\n left: number;\n}\n\ninterface SpaceAvailable {\n top: number;\n bottom: number;\n start: number;\n end: number;\n}\n\n/**\n * Dropdown menus enable users to select an action from a list of options.\n * They’re commonly used to save space by grouping related actions, or to provide actions in a confined space.\n *\n * @summary Action menu with auto-positioning, keyboard support, and screen reader integration.\n * @element nys-dropdownmenu\n *\n * @usagedos\n * - Use to display a list of 5–15 actions or links under a single trigger, such as navigation, commands, or user profile access.\n * - Keep to 3–7 actions when possible.\n * - Order actions by frequency or importance.\n * - Separate destructive actions with a divider.\n * - Use clear, specific labels (e.g., \"Profile\", \"Account Settings\", \"Sign out\").\n *\n * @usagedonts\n * - Use for form inputs or selectable values. Use `<nys-select>` for standard selection or `<nys-combobox>` when users need to filter or search through many options.\n * - Use when the action is always visible and frequently used. Use `<nys-button>` instead.\n * - Use without a label or accessible description on the trigger.\n * - Nest dropdown menus inside other dropdown menus.\n * - Use vague labels like \"Manage\" or \"Options\".\n *\n * @example Basic\n * ```html\n * <nys-button id=\"my-trigger-id\" label=\"Open Menu\"></nys-button>\n * <nys-dropdownmenu id=\"my-dropdownmenu\" for=\"my-trigger-id\">\n * <nys-dropdownmenuitem label=\"Profile\" href=\"/profile\"></nys-dropdownmenuitem>\n * <nys-dropdownmenuitem label=\"Repositories & Github Pages\" href=\"/repos\"></nys-dropdownmenuitem>\n * <nys-dropdownmenuitem label=\"Organizations\" href=\"/organizations\"></nys-dropdownmenuitem>\n * <nys-dropdownmenuitem label=\"Sign out\" href=\"/logout\"></nys-dropdownmenuitem>\n * </nys-dropdownmenu>\n * </div>\n * ```\n *\n * @render Basic\n * ```html\n * <div style=\"margin: 200px; display: flex; justify-content: center; align-items: center;\">\n * <nys-button id=\"my-trigger-id\" label=\"Open Menu\"></nys-button>\n * <nys-dropdownmenu id=\"my-dropdownmenu\" for=\"my-trigger-id\">\n * <nys-dropdownmenuitem label=\"Profile\" href=\"/profile\"></nys-dropdownmenuitem>\n * <nys-dropdownmenuitem label=\"Repositories & Github Pages\" href=\"/repos\"></nys-dropdownmenuitem>\n * <nys-dropdownmenuitem label=\"Organizations\" href=\"/organizations\"></nys-dropdownmenuitem>\n * <nys-dropdownmenuitem label=\"Sign out\" href=\"/logout\"></nys-dropdownmenuitem>\n * </nys-dropdownmenu>\n * </div>\n * ```\n */\n\nexport class NysDropdownMenu extends NysElement {\n static styles = unsafeCSS(styles);\n\n @property({ type: String, reflect: true }) for = \"\";\n @property({ type: Boolean }) showDropdown = false;\n\n /**\n * Accessible label for the menu (`role=\"menu\"`) container. Used as `aria-label`\n * on the `<ul role=\"menu\">` element. When empty, falls back to \"Menu\" so the\n * menu always has an accessible name for screen readers.\n * @default \"\"\n */\n @property({ type: String }) label = \"\";\n\n /**\n * Preferred position relative to trigger.\n * @default \"bottom-end\"\n */\n @property({ type: String, reflect: true }) position: Position | null = null;\n\n private _trigger: HTMLElement | null = null;\n private _menuElement: HTMLElement | null = null;\n private _ariaTarget: HTMLElement | null = null;\n private _lastFocusedIndex: number = 0;\n private readonly GAP = 4;\n private _resizeObserver: ResizeObserver | null = null;\n // The `for` value currently wired to a trigger, or null when nothing is\n // wired. Compared against `this.for` in updated() so re-wiring only\n // happens when the id actually changes, not on every unrelated update.\n private _wiredFor: string | null = null;\n\n /**\n * Lifecycle Methods\n * --------------------------------------------------------------------------\n */\n\n constructor() {\n super();\n }\n\n // super.connectedCallback() (NysElement) auto-assigns this.id when\n // one is not provided (prefix = localName, i.e. \"nys-dropdownmenu-<ts>-<n>\").\n // The menu/menuitem roles intentionally stay on the inner <ul>/items, so this\n // component keeps defaultRole = null and does not move a role onto the host.\n connectedCallback() {\n super.connectedCallback();\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n }\n\n async firstUpdated() {\n await this.updateComplete;\n this.applyInverseTransform();\n this._handleMenuClick();\n }\n\n // `for` is wired here rather than once in firstUpdated: under SSR (e.g.\n // Next.js) the wrapper element upgrades with `for` still at its default\n // (\"\") — the real value only arrives afterward as a property set by the\n // framework wrapper — so wiring once in firstUpdated would silently wire\n // nothing and the menu would never open. Re-wiring on every `for` change\n // (including the first, since `updated()` also runs after the initial\n // render) picks up that later-arriving value; the id-comparison guard\n // keeps unrelated updates from re-wiring the same trigger.\n updated(changedProperties: Map<string | number | symbol, unknown>) {\n if (changedProperties.has(\"for\") && this.for !== this._wiredFor) {\n this._disconnectTrigger();\n if (this.for) {\n // Wait for updateComplete: the trigger (found by id, possibly in\n // another shadow root) needs to have finished its own first render\n // before ariaTarget lookups (e.g. nys-button's shadow <button>)\n // succeed.\n this.updateComplete.then(() => this._connectTrigger());\n }\n }\n }\n\n /**\n * Functions\n * --------------------------------------------------------------------------\n */\n\n private _findTrigger() {\n const targetId = this.for;\n if (!targetId) return null;\n\n // lightDOM search\n let htmlElement = document.getElementById(targetId);\n if (htmlElement) return htmlElement;\n\n // Search recursively through shadow DOMs (e.g. for nys-label within other component's shadowDOM)\n const findInShadows = (root: ParentNode): HTMLElement | null => {\n for (const el of Array.from(root.querySelectorAll(\"*\"))) {\n const shadowElement = el.shadowRoot;\n if (shadowElement) {\n const found = shadowElement.getElementById(targetId);\n if (found) return found;\n\n const deeper = findInShadows(shadowElement);\n if (deeper) return deeper;\n }\n }\n return null;\n };\n\n return findInShadows(document);\n }\n\n private _connectTrigger() {\n const trigger = this._findTrigger();\n if (!trigger) return;\n\n this._trigger = trigger;\n this._wiredFor = this.for;\n\n const ariaTarget =\n trigger.tagName.toLowerCase() === \"nys-button\"\n ? (trigger.shadowRoot?.querySelector(\"button\") ?? trigger)\n : trigger;\n\n ariaTarget.setAttribute(\"aria-haspopup\", \"menu\");\n ariaTarget.setAttribute(\"aria-expanded\", \"false\");\n\n this._ariaTarget = ariaTarget;\n\n this._trigger.addEventListener(\"click\", this._toggleDropdown);\n this._trigger.addEventListener(\"keydown\", this._handleTriggerKeydown);\n }\n\n // Undoes _connectTrigger: removes the listeners and aria wiring from\n // whatever trigger is currently connected, and closes the menu if it was\n // open against that trigger. Called before wiring a new trigger (`for`\n // changed) so a stale trigger never keeps listening.\n private _disconnectTrigger() {\n if (this.showDropdown) this._closeDropdown();\n\n if (this._trigger) {\n this._trigger.removeEventListener(\"click\", this._toggleDropdown);\n this._trigger.removeEventListener(\"keydown\", this._handleTriggerKeydown);\n }\n if (this._ariaTarget) {\n this._ariaTarget.removeAttribute(\"aria-haspopup\");\n this._ariaTarget.removeAttribute(\"aria-expanded\");\n }\n\n this._trigger = null;\n this._ariaTarget = null;\n this._wiredFor = null;\n }\n\n private _toggleDropdown = async () => {\n this.showDropdown = !this.showDropdown;\n\n this._ariaTarget?.setAttribute(\"aria-expanded\", String(this.showDropdown));\n\n if (this.showDropdown) {\n window.addEventListener(\"scroll\", this._handleWindowScroll, true);\n this._resizeObserver = new ResizeObserver(() => {\n if (this.showDropdown) {\n this._positionMenu();\n }\n });\n this._resizeObserver.observe(document.documentElement);\n\n document.addEventListener(\"click\", this._handleDocumentClick);\n\n this._menuElement = this.shadowRoot?.querySelector(\n \".nys-dropdownmenu\",\n ) as HTMLElement | null;\n this._menuElement!.addEventListener(\"keydown\", this._handleMenuKeydown);\n\n await this.updateComplete;\n this._positionMenu();\n this._focusOnItem(this._lastFocusedIndex);\n } else {\n window.removeEventListener(\"scroll\", this._handleWindowScroll, true);\n document.removeEventListener(\"click\", this._handleDocumentClick);\n this._menuElement!.removeEventListener(\n \"keydown\",\n this._handleMenuKeydown,\n );\n\n this._resizeObserver?.disconnect();\n this._resizeObserver = null;\n }\n };\n\n private _closeDropdown() {\n this.showDropdown = false;\n this._ariaTarget?.setAttribute(\"aria-expanded\", \"false\");\n this._trigger?.focus();\n }\n\n private _getMenuItems(): HTMLElement[] {\n const slot = this.shadowRoot?.querySelector(\"slot\");\n const assigned = slot?.assignedElements({ flatten: true }) || [];\n\n return assigned.filter(\n (el) => el && !el.hasAttribute(\"disabled\"),\n ) as HTMLElement[];\n }\n\n private _handleDocumentClick = (event: MouseEvent) => {\n if (!this.showDropdown) return;\n\n const path = event?.composedPath();\n\n const clickedInsideMenu = path.includes(this);\n const clickedTrigger = this._trigger && path.includes(this._trigger);\n\n if (!clickedInsideMenu && !clickedTrigger) {\n this._closeDropdown();\n }\n };\n\n private async _focusOnItem(index: number = 0) {\n await new Promise((resolve) => requestAnimationFrame(resolve));\n const items = this._getMenuItems();\n const target = items[Math.min(index, items.length - 1)];\n if (!target) return;\n\n target.focus();\n }\n\n // In some iframes (like Storybook's) or embedded containers , parent elements may have CSS transforms applied, creating a new coordinate context.\n // This function removes such transforms to prevent them from affecting tooltip positioning calculations.\n private applyInverseTransform() {\n document.querySelectorAll('div[scale=\"1\"]').forEach((el) => {\n (el as HTMLElement).style.transform = \"none\";\n });\n }\n\n /**\n * Position Logic\n * --------------------------------------------------------------------------\n */\n\n /**\n * The controller function for positioning the dropdown menu.\n * The logic diverts to if user sets position or we auto position the dropdown menu\n */\n private _positionMenu() {\n if (!this._trigger) return;\n\n this._menuElement = this.shadowRoot?.querySelector(\n \".nys-dropdownmenu\",\n ) as HTMLElement | null;\n if (!this._menuElement) return;\n\n // Decide position based on user preference OR auto position if no position chosen\n const finalPosition = this.position\n ? this._setUserPosition(this.position)\n : this._autoPosition();\n\n // Apply the calculated position\n const coords = this._calculateCoordinates(finalPosition);\n this._applyPosition(coords);\n }\n\n private _setUserPosition(userPosition: Position): Position {\n const space = this._checkSpaceAvailable();\n const menuRect = this._menuElement!.getBoundingClientRect();\n\n const userPositionFits = this._checkPositionFits(\n userPosition,\n space,\n menuRect,\n );\n\n if (userPositionFits) {\n return userPosition;\n }\n\n // User position doesn't fit, find best alternative\n return this._findBestAlternative(userPosition, space, menuRect);\n }\n\n /**\n * Auto Positioning of the dropdown menu relies on the best surrounding space available\n * to select the desirable position.\n */\n private _autoPosition(): Position {\n const space = this._checkSpaceAvailable();\n const menuRect = this._menuElement!.getBoundingClientRect();\n\n const defaultPosition: Position = \"bottom-end\";\n\n if (this._checkPositionFits(defaultPosition, space, menuRect)) {\n return defaultPosition;\n }\n\n return this._findBestAlternative(defaultPosition, space, menuRect);\n }\n\n /**\n * Checks if the dropdown menu fits inside the viewport on the given side of the trigger.\n * Overrides user set position for auto-positioning if user's desire space is not available\n */\n private _checkSpaceAvailable(): SpaceAvailable {\n if (!this._trigger) {\n return { top: 0, bottom: 0, start: 0, end: 0 };\n }\n\n const triggerRect = this._trigger.getBoundingClientRect();\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n\n return {\n top: triggerRect.top,\n bottom: viewportHeight - triggerRect.bottom,\n start: triggerRect.left,\n end: viewportWidth - triggerRect.right,\n };\n }\n\n private _checkPositionFits(\n position: Position,\n space: SpaceAvailable,\n menuRect: DOMRect,\n ): boolean {\n const menuWidth = menuRect.width;\n const menuHeight = menuRect.height;\n\n const [vertical, horizontal] = position.split(\"-\") as [\n \"top\" | \"bottom\",\n \"start\" | \"end\",\n ];\n\n const verticalFits =\n vertical === \"bottom\"\n ? space.bottom >= menuHeight + this.GAP\n : space.top >= menuHeight + this.GAP;\n\n const horizontalFits =\n horizontal === \"start\"\n ? space.end >= menuWidth\n : space.start >= menuWidth;\n return verticalFits && horizontalFits;\n }\n\n /**\n * This position is called for when user's set position didn't fit OR auto positioning when default position doesn't fit\n * We look for the best alternative positions in order of preference base on the set position (e.g. bottom-start => bottom-end).\n * @param userPosition\n * @param space\n * @param menuRect\n */\n private _findBestAlternative(\n userPosition: Position,\n space: SpaceAvailable,\n menuRect: DOMRect,\n ): Position {\n const [vertical, horizontal] = userPosition.split(\"-\") as [\n \"top\" | \"bottom\",\n \"start\" | \"end\",\n ];\n\n const alternatives: Position[] = [\n `${vertical === \"bottom\" ? \"top\" : \"bottom\"}-${horizontal}`, // Flip vertical\n `${vertical}-${horizontal === \"start\" ? \"end\" : \"start\"}`, // Flip horizontal\n `${vertical === \"bottom\" ? \"top\" : \"bottom\"}-${horizontal === \"start\" ? \"end\" : \"start\"}`, // Flip both\n ];\n\n for (const altPosition of alternatives) {\n if (this._checkPositionFits(altPosition, space, menuRect)) {\n return altPosition;\n }\n }\n\n // Fallback: nothing fits, return position with most space\n return this._findMostAvailableSpace(space) as Position;\n }\n\n private _findMostAvailableSpace(space: SpaceAvailable): Position {\n const vertical = space.bottom >= space.top ? \"bottom\" : \"top\";\n const horizontal = space.start >= space.end ? \"start\" : \"end\";\n\n return `${vertical}-${horizontal}`;\n }\n\n /**\n * A valid ideal position has been chosen.\n * This function calculates the coordinate of the trigger to properly position the dropdown menu.\n * @param position\n * @returns\n */\n private _calculateCoordinates(position: Position): PositionCoordinates {\n if (!this._trigger || !this._menuElement) {\n return { top: 0, left: 0 };\n }\n\n const triggerRect = this._trigger.getBoundingClientRect();\n const menuRect = this._menuElement.getBoundingClientRect();\n\n const [vertical, horizontal] = position.split(\"-\") as [\n \"top\" | \"bottom\",\n \"start\" | \"end\",\n ];\n\n let top = 0;\n let left = 0;\n\n // Vertical position\n if (vertical === \"bottom\") {\n top = triggerRect.bottom + this.GAP;\n } else {\n top = triggerRect.top - menuRect.height - this.GAP;\n }\n\n // Horizontal position\n if (horizontal === \"start\") {\n left = triggerRect.left;\n } else {\n left = triggerRect.right - menuRect.width;\n }\n\n return { top, left };\n }\n\n private _applyPosition(coords: PositionCoordinates) {\n if (!this._menuElement) return;\n\n this._menuElement.style.top = `${coords.top}px`;\n this._menuElement.style.left = `${coords.left}px`;\n }\n\n /**\n * Event Handlers\n * --------------------------------------------------------------------------\n */\n\n private _handleMenuClick() {\n this.addEventListener(\"nys-click\", (e: Event) => {\n const items = this._getMenuItems();\n const targetCrumb = (e as CustomEvent).detail?.id;\n const index = items.findIndex((item) => item.id === targetCrumb);\n if (index !== -1) this._lastFocusedIndex = index;\n this._closeDropdown();\n });\n }\n\n private _handleTriggerKeydown = (event: KeyboardEvent) => {\n if (event.defaultPrevented) return;\n\n if (event.key === \"Escape\" && this.showDropdown) {\n event.preventDefault();\n this._closeDropdown();\n }\n };\n\n private _handleMenuKeydown = (event: KeyboardEvent) => {\n const items = this._getMenuItems();\n const currentIndex = items.indexOf(document.activeElement as HTMLElement);\n\n switch (event.key) {\n case \"Escape\":\n event.preventDefault();\n this._closeDropdown();\n break;\n case \"ArrowDown\":\n case \"ArrowRight\":\n event.preventDefault();\n const nextIndex =\n currentIndex < items.length - 1 ? currentIndex + 1 : 0;\n this._lastFocusedIndex = nextIndex;\n items[nextIndex].focus();\n break;\n case \"ArrowUp\":\n case \"ArrowLeft\":\n event.preventDefault();\n const prevIndex =\n currentIndex > 0 ? currentIndex - 1 : items.length - 1;\n this._lastFocusedIndex = prevIndex;\n items[prevIndex].focus();\n break;\n case \"Tab\":\n if (currentIndex >= items.length - 1 && !event.shiftKey) {\n this._closeDropdown();\n }\n break;\n default:\n break;\n }\n };\n\n private _handleWindowScroll = () => {\n if (this.showDropdown) {\n this._positionMenu();\n }\n };\n\n render() {\n return html`<div\n class=\"nys-dropdownmenu ${this.showDropdown ? \"active\" : \"\"}\"\n for=${this.for}\n ?hidden=${!this.showDropdown}\n >\n <ul role=\"menu\" aria-label=${this.label || \"Menu\"}>\n <slot></slot>\n </ul>\n </div>`;\n }\n}\n\nif (!customElements.get(\"nys-dropdownmenu\")) {\n customElements.define(\"nys-dropdownmenu\", NysDropdownMenu);\n}\n"],"names":["LitElement","unsafeCSS","html","property","y","f","e","h","t","P","u","_NysDropdownMenuItem","NysElement","isLink","styles","NysDropdownMenuItem","__decorateClass","_NysDropdownMenu","event","path","clickedInsideMenu","clickedTrigger","items","currentIndex","nextIndex","prevIndex","changedProperties","targetId","htmlElement","findInShadows","root","el","shadowElement","found","deeper","trigger","ariaTarget","index","resolve","target","finalPosition","coords","userPosition","space","menuRect","defaultPosition","triggerRect","viewportWidth","viewportHeight","position","menuWidth","menuHeight","vertical","horizontal","verticalFits","horizontalFits","alternatives","altPosition","top","left","targetCrumb","item","NysDropdownMenu"],"mappings":"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"}
@@ -1,5 +1,13 @@
1
1
  import { NysElement } from "@nysds/internals";
2
2
  import "@nysds/nys-icon";
3
+ /** Detail payload for the `nys-click` event fired by `nys-dropdownmenuitem`. */
4
+ export interface NysDropdownmenuitemClickDetail {
5
+ id: string;
6
+ label: string;
7
+ href?: string;
8
+ }
9
+ /** The `nys-click` event fired by `nys-dropdownmenuitem`. */
10
+ export type NysDropdownmenuitemClickEvent = CustomEvent<NysDropdownmenuitemClickDetail>;
3
11
  /**
4
12
  * **Slotted component.** Displays an individual dropdown item within `nys-dropdown` with label.
5
13
  *
@@ -8,6 +16,8 @@ import "@nysds/nys-icon";
8
16
  *
9
17
  * @summary Dropdown item to display label and provide href link.
10
18
  * @element nys-dropdownmenuitem
19
+ *
20
+ * @fires {NysDropdownmenuitemClickEvent} nys-click - Fired when the item is activated (unless disabled). Detail: `{id, label, href?}` — `href` is present only when set. Bubbles and composed.
11
21
  */
12
22
  export declare class NysDropdownMenuItem extends NysElement {
13
23
  static styles: import("lit").CSSResult;
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@nysds/nys-dropdownmenu",
3
- "version": "1.21.0",
3
+ "version": "1.22.0",
4
4
  "description": "The Dropdownmenu component from the NYS Design System.",
5
5
  "module": "dist/nys-dropdownmenu.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "exports": {
8
8
  ".": {
9
- "import": "./dist/nys-dropdownmenu.js",
10
- "types": "./dist/index.d.ts"
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/nys-dropdownmenu.js"
11
11
  }
12
12
  },
13
13
  "type": "module",
@@ -23,10 +23,10 @@
23
23
  "lit-analyze": "lit-analyzer 'src/**/*.ts'"
24
24
  },
25
25
  "dependencies": {
26
- "@nysds/nys-icon": "^1.21.0"
26
+ "@nysds/nys-icon": "^1.22.0"
27
27
  },
28
28
  "devDependencies": {
29
- "@nysds/internals": "^1.21.0",
29
+ "@nysds/internals": "^1.22.0",
30
30
  "lit": "^3.3.1",
31
31
  "typescript": "^5.9.3",
32
32
  "vite": "^7.3.1"