@dso-toolkit/core 62.11.0 → 62.11.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.
Files changed (61) hide show
  1. package/dist/cjs/dso-alert_7.cjs.entry.js +2 -1
  2. package/dist/cjs/dso-alert_7.cjs.entry.js.map +1 -1
  3. package/dist/cjs/dso-dropdown-menu.cjs.entry.js +55 -72
  4. package/dist/cjs/dso-dropdown-menu.cjs.entry.js.map +1 -1
  5. package/dist/cjs/dso-helpcenter-panel.cjs.entry.js +2 -1
  6. package/dist/cjs/dso-helpcenter-panel.cjs.entry.js.map +1 -1
  7. package/dist/cjs/dso-list-button.cjs.entry.js +2 -1
  8. package/dist/cjs/dso-list-button.cjs.entry.js.map +1 -1
  9. package/dist/cjs/dso-toolkit.cjs.js +1 -1
  10. package/dist/cjs/{focus-trap.esm-1633a28f.js → focus-trap.esm-6cff35a7.js} +16 -584
  11. package/dist/cjs/focus-trap.esm-6cff35a7.js.map +1 -0
  12. package/dist/cjs/index.esm-970bc106.js +578 -0
  13. package/dist/cjs/index.esm-970bc106.js.map +1 -0
  14. package/dist/cjs/loader.cjs.js +1 -1
  15. package/dist/collection/components/dropdown-menu/dropdown-menu.js +68 -71
  16. package/dist/collection/components/dropdown-menu/dropdown-menu.js.map +1 -1
  17. package/dist/components/dropdown-menu.js +55 -72
  18. package/dist/components/dropdown-menu.js.map +1 -1
  19. package/dist/components/focus-trap.esm.js +2 -569
  20. package/dist/components/focus-trap.esm.js.map +1 -1
  21. package/dist/components/index.esm.js +572 -0
  22. package/dist/components/index.esm.js.map +1 -0
  23. package/dist/dso-toolkit/dso-toolkit.esm.js +1 -1
  24. package/dist/dso-toolkit/dso-toolkit.esm.js.map +1 -1
  25. package/dist/dso-toolkit/p-638ad5e5.entry.js +2 -0
  26. package/dist/dso-toolkit/{p-e0a3a1d4.entry.js.map → p-638ad5e5.entry.js.map} +1 -1
  27. package/dist/dso-toolkit/p-67717e41.entry.js +2 -0
  28. package/dist/dso-toolkit/{p-2862c19a.entry.js.map → p-67717e41.entry.js.map} +1 -1
  29. package/dist/dso-toolkit/p-86133aa5.js +6 -0
  30. package/dist/dso-toolkit/p-86133aa5.js.map +1 -0
  31. package/dist/dso-toolkit/p-918ef3d0.entry.js +2 -0
  32. package/dist/dso-toolkit/p-918ef3d0.entry.js.map +1 -0
  33. package/dist/dso-toolkit/p-eb7275e9.entry.js +2 -0
  34. package/dist/dso-toolkit/{p-1faa1497.entry.js.map → p-eb7275e9.entry.js.map} +1 -1
  35. package/dist/dso-toolkit/p-fbb226aa.js +6 -0
  36. package/dist/dso-toolkit/p-fbb226aa.js.map +1 -0
  37. package/dist/esm/dso-alert_7.entry.js +2 -1
  38. package/dist/esm/dso-alert_7.entry.js.map +1 -1
  39. package/dist/esm/dso-dropdown-menu.entry.js +54 -71
  40. package/dist/esm/dso-dropdown-menu.entry.js.map +1 -1
  41. package/dist/esm/dso-helpcenter-panel.entry.js +2 -1
  42. package/dist/esm/dso-helpcenter-panel.entry.js.map +1 -1
  43. package/dist/esm/dso-list-button.entry.js +2 -1
  44. package/dist/esm/dso-list-button.entry.js.map +1 -1
  45. package/dist/esm/dso-toolkit.js +1 -1
  46. package/dist/esm/{focus-trap.esm-22fb5633.js → focus-trap.esm-d8deb1f0.js} +3 -570
  47. package/dist/esm/focus-trap.esm-d8deb1f0.js.map +1 -0
  48. package/dist/esm/index.esm-7e16e884.js +572 -0
  49. package/dist/esm/index.esm-7e16e884.js.map +1 -0
  50. package/dist/esm/loader.js +1 -1
  51. package/dist/types/components/dropdown-menu/dropdown-menu.d.ts +4 -5
  52. package/package.json +2 -2
  53. package/dist/cjs/focus-trap.esm-1633a28f.js.map +0 -1
  54. package/dist/dso-toolkit/p-1faa1497.entry.js +0 -2
  55. package/dist/dso-toolkit/p-2862c19a.entry.js +0 -2
  56. package/dist/dso-toolkit/p-92fd9516.entry.js +0 -2
  57. package/dist/dso-toolkit/p-92fd9516.entry.js.map +0 -1
  58. package/dist/dso-toolkit/p-9c0f67c8.js +0 -10
  59. package/dist/dso-toolkit/p-9c0f67c8.js.map +0 -1
  60. package/dist/dso-toolkit/p-e0a3a1d4.entry.js +0 -2
  61. package/dist/esm/focus-trap.esm-22fb5633.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { proxyCustomElement, HTMLElement, h, Host } from '@stencil/core/internal/client';
2
- import { c as createFocusTrap, t as tabbable } from './focus-trap.esm.js';
2
+ import { t as tabbable } from './index.esm.js';
3
3
  import { h as hasOverflow, c as createPopper } from './has-overflow.js';
4
4
  import { v as v4 } from './v4.js';
5
5
 
@@ -10,29 +10,13 @@ const DropdownMenu = /*@__PURE__*/ proxyCustomElement(class DropdownMenu extends
10
10
  super();
11
11
  this.__registerHost();
12
12
  this.__attachShadow();
13
- this.keyDownListener = (event) => {
14
- if (event.defaultPrevented) {
13
+ this.focusOutListener = (event) => {
14
+ if (!(event.relatedTarget instanceof HTMLElement)) {
15
15
  return;
16
16
  }
17
- switch (event.key) {
18
- case "ArrowDown":
19
- this.tabInPopup(1);
20
- break;
21
- case "ArrowUp":
22
- this.tabInPopup(-1);
23
- break;
24
- case "Escape":
25
- this.escape();
26
- break;
27
- case " ":
28
- if (event.target instanceof HTMLElement) {
29
- event.target.click();
30
- }
31
- break;
32
- default:
33
- return;
17
+ if (!this.tabbables(true).includes(event.relatedTarget)) {
18
+ this.open = false;
34
19
  }
35
- event.preventDefault();
36
20
  };
37
21
  this.escape = () => {
38
22
  this.button.focus();
@@ -70,32 +54,6 @@ const DropdownMenu = /*@__PURE__*/ proxyCustomElement(class DropdownMenu extends
70
54
  watchStrategy() {
71
55
  this.setStrategy();
72
56
  }
73
- createTrap() {
74
- var _a, _b;
75
- this.trap = createFocusTrap(this.host, {
76
- clickOutsideDeactivates: () => {
77
- var _a;
78
- this.open = false;
79
- (_a = this.trap) === null || _a === void 0 ? void 0 : _a.deactivate();
80
- delete this.trap;
81
- return true;
82
- },
83
- escapeDeactivates: (event) => {
84
- var _a;
85
- if (event instanceof KeyboardEvent) {
86
- this.open = false;
87
- (_a = this.trap) === null || _a === void 0 ? void 0 : _a.deactivate();
88
- delete this.trap;
89
- }
90
- return true;
91
- },
92
- tabbableOptions: {
93
- getShadowRoot: true,
94
- },
95
- setReturnFocus: (_a = this.button) !== null && _a !== void 0 ? _a : false,
96
- initialFocus: (_b = this.button) !== null && _b !== void 0 ? _b : false,
97
- }).activate();
98
- }
99
57
  setStrategy() {
100
58
  if (!this.popper) {
101
59
  return;
@@ -128,8 +86,9 @@ const DropdownMenu = /*@__PURE__*/ proxyCustomElement(class DropdownMenu extends
128
86
  }
129
87
  return button;
130
88
  }
131
- get tabbables() {
132
- return tabbable(this.host).filter((e) => e !== this.button);
89
+ tabbables(withButton) {
90
+ const tabbables = tabbable(this.host);
91
+ return withButton ? tabbables : tabbables.filter((element) => element !== this.button);
133
92
  }
134
93
  componentDidLoad() {
135
94
  this.button.setAttribute("aria-haspopup", "menu");
@@ -179,15 +138,10 @@ const DropdownMenu = /*@__PURE__*/ proxyCustomElement(class DropdownMenu extends
179
138
  });
180
139
  }
181
140
  componentDidRender() {
182
- var _a, _b;
141
+ var _a;
183
142
  this.setStrategy();
184
143
  if (this.open) {
185
144
  (_a = this.popper) === null || _a === void 0 ? void 0 : _a.update();
186
- this.createTrap();
187
- }
188
- else {
189
- (_b = this.trap) === null || _b === void 0 ? void 0 : _b.deactivate();
190
- delete this.trap;
191
145
  }
192
146
  for (const li of Array.from(this.host.getElementsByTagName("li"))) {
193
147
  for (const tab of tabbable(li)) {
@@ -197,22 +151,52 @@ const DropdownMenu = /*@__PURE__*/ proxyCustomElement(class DropdownMenu extends
197
151
  }
198
152
  }
199
153
  }
200
- this.host.removeEventListener("keydown", this.keyDownListener);
201
154
  this.button.setAttribute("aria-expanded", this.open ? "true" : "false");
202
- if (this.open) {
203
- this.host.addEventListener("keydown", this.keyDownListener);
155
+ }
156
+ onClick(event) {
157
+ if (!(event.target instanceof HTMLElement)) {
158
+ return;
159
+ }
160
+ if (this.open && (!this.host.contains(event.target) || this.tabbables(false).includes(event.target))) {
161
+ this.open = false;
204
162
  }
205
- this.tabbables.forEach((focusableElement) => {
206
- focusableElement.removeEventListener("click", this.escape);
207
- if (this.open) {
208
- focusableElement.addEventListener("click", this.escape);
209
- }
210
- });
211
163
  }
212
164
  disconnectedCallback() {
213
165
  var _a;
214
166
  (_a = this.popper) === null || _a === void 0 ? void 0 : _a.destroy();
215
167
  }
168
+ keyDownListener(event) {
169
+ if (event.defaultPrevented || !this.open) {
170
+ return;
171
+ }
172
+ switch (event.key) {
173
+ case "Tab":
174
+ if (event.shiftKey) {
175
+ this.tabInPopup(this.tabbables(true), -1);
176
+ }
177
+ else {
178
+ this.tabInPopup(this.tabbables(true), 1);
179
+ }
180
+ break;
181
+ case "ArrowDown":
182
+ this.tabInPopup(this.tabbables(false), 1);
183
+ break;
184
+ case "ArrowUp":
185
+ this.tabInPopup(this.tabbables(false), -1);
186
+ break;
187
+ case "Escape":
188
+ this.escape();
189
+ break;
190
+ case " ":
191
+ if (event.target instanceof HTMLElement) {
192
+ event.target.click();
193
+ }
194
+ break;
195
+ default:
196
+ return;
197
+ }
198
+ event.preventDefault();
199
+ }
216
200
  getActiveElement(root = document) {
217
201
  const activeEl = root.activeElement;
218
202
  if (!activeEl) {
@@ -223,21 +207,20 @@ const DropdownMenu = /*@__PURE__*/ proxyCustomElement(class DropdownMenu extends
223
207
  }
224
208
  return activeEl;
225
209
  }
226
- tabInPopup(direction) {
210
+ tabInPopup(tabbables, direction) {
227
211
  var _a;
228
- const tabs = this.tabbables;
229
- const currentIndex = tabs.findIndex((e) => e === this.getActiveElement());
212
+ const currentIndex = tabbables.findIndex((e) => e === this.getActiveElement());
230
213
  let nextIndex = currentIndex + direction;
231
- if (nextIndex >= tabs.length) {
214
+ if (nextIndex >= tabbables.length) {
232
215
  nextIndex = 0;
233
216
  }
234
217
  else if (nextIndex < 0) {
235
- nextIndex = tabs.length - 1;
218
+ nextIndex = tabbables.length - 1;
236
219
  }
237
- (_a = tabs[nextIndex]) === null || _a === void 0 ? void 0 : _a.focus();
220
+ (_a = tabbables[nextIndex]) === null || _a === void 0 ? void 0 : _a.focus();
238
221
  }
239
222
  render() {
240
- return (h(Host, { tabindex: this.open ? "-1" : undefined }, h("slot", { name: "toggle" }), h("div", { hidden: !this.open }, h("slot", null))));
223
+ return (h(Host, { onFocusout: this.focusOutListener }, h("slot", { name: "toggle" }), h("div", { hidden: !this.open }, h("slot", null))));
241
224
  }
242
225
  get host() { return this; }
243
226
  static get watchers() { return {
@@ -255,7 +238,7 @@ const DropdownMenu = /*@__PURE__*/ proxyCustomElement(class DropdownMenu extends
255
238
  "boundary": [1],
256
239
  "placement": [1],
257
240
  "strategy": [1]
258
- }, undefined, {
241
+ }, [[8, "click", "onClick"], [8, "keydown", "keyDownListener"]], {
259
242
  "placement": ["watchPosition"],
260
243
  "dropdownAlign": ["watchPosition"],
261
244
  "dropdownOptionsOffset": ["watchOptionsOffset"],
@@ -1 +1 @@
1
- {"file":"dropdown-menu.js","mappings":"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","names":["uuidv4"],"sources":["src/components/dropdown-menu/dropdown-menu.scss?tag=dso-dropdown-menu&encapsulation=shadow","src/components/dropdown-menu/dropdown-menu.tsx"],"sourcesContent":["@use \"~dso-toolkit/src/components/dropdown-menu\";\r\n\r\n:host(:focus) {\r\n outline: none;\r\n}\r\n\r\n:host {\r\n @include dropdown-menu.root();\r\n}\r\n","import { createPopper, Placement, Instance as PopperInstance } from \"@popperjs/core\";\r\nimport { h, Component, Element, Host, Prop, Watch } from \"@stencil/core\";\r\nimport { FocusableElement, tabbable } from \"tabbable\";\r\nimport { v4 as uuidv4 } from \"uuid\";\r\n\r\nimport { hasOverflow } from \"../../utils/has-overflow\";\r\nimport { createFocusTrap, FocusTrap } from \"focus-trap\";\r\n\r\n@Component({\r\n tag: \"dso-dropdown-menu\",\r\n styleUrl: \"dropdown-menu.scss\",\r\n shadow: true,\r\n})\r\nexport class DropdownMenu {\r\n private trap?: FocusTrap;\r\n /**\r\n * Whether the menu is open or closed.\r\n * This attribute is reflected and mutable.\r\n */\r\n @Prop({ reflect: true, mutable: true })\r\n open = false;\r\n\r\n /**\r\n * Alignment of the dropdown\r\n */\r\n @Prop()\r\n dropdownAlign: \"left\" | \"right\" = \"left\";\r\n\r\n /**\r\n * Space between button and dropdown options\r\n */\r\n @Prop()\r\n dropdownOptionsOffset = 2;\r\n\r\n /**\r\n * Whether the menu is checkable.\r\n */\r\n @Prop()\r\n checkable = false;\r\n\r\n /**\r\n * Selector for the element the dropdown options should not be overflowing.\r\n */\r\n @Prop()\r\n boundary?: string;\r\n\r\n /**\r\n * Force placement of dropdown.\r\n *\r\n * This property overrides `dropdownAlign`.\r\n */\r\n @Prop()\r\n placement?: Placement;\r\n\r\n /**\r\n * Set position strategy of dropdown options\r\n */\r\n @Prop()\r\n strategy: \"auto\" | \"absolute\" | \"fixed\" = \"auto\";\r\n\r\n @Watch(\"placement\")\r\n @Watch(\"dropdownAlign\")\r\n watchPosition() {\r\n if (!this.popper) {\r\n return;\r\n }\r\n\r\n this.popper.setOptions({\r\n placement: this.placement || (this.dropdownAlign === \"right\" ? \"bottom-end\" : \"bottom-start\"),\r\n });\r\n }\r\n\r\n @Watch(\"dropdownOptionsOffset\")\r\n watchOptionsOffset() {\r\n this.popper?.setOptions({\r\n modifiers: [\r\n {\r\n name: \"offset\",\r\n options: {\r\n offset: [0, this.dropdownOptionsOffset],\r\n },\r\n },\r\n ],\r\n });\r\n }\r\n\r\n @Watch(\"strategy\")\r\n watchStrategy() {\r\n this.setStrategy();\r\n }\r\n\r\n private createTrap() {\r\n this.trap = createFocusTrap(this.host, {\r\n clickOutsideDeactivates: () => {\r\n this.open = false;\r\n this.trap?.deactivate();\r\n delete this.trap;\r\n\r\n return true;\r\n },\r\n escapeDeactivates: (event) => {\r\n if (event instanceof KeyboardEvent) {\r\n this.open = false;\r\n this.trap?.deactivate();\r\n delete this.trap;\r\n }\r\n\r\n return true;\r\n },\r\n tabbableOptions: {\r\n getShadowRoot: true,\r\n },\r\n setReturnFocus: this.button ?? false,\r\n initialFocus: this.button ?? false,\r\n }).activate();\r\n }\r\n\r\n private setStrategy() {\r\n if (!this.popper) {\r\n return;\r\n }\r\n\r\n if (this.strategy === \"absolute\" || this.strategy === \"fixed\") {\r\n this.popper.setOptions({\r\n strategy: this.strategy,\r\n });\r\n\r\n return;\r\n }\r\n\r\n let element: Element | null = this.host;\r\n\r\n const boundary = this.boundary || document;\r\n\r\n while (element && element.parentNode !== boundary) {\r\n element = element.parentNode instanceof ShadowRoot ? element.parentNode.host : element.parentElement;\r\n if (element !== null && hasOverflow(element)) {\r\n this.popper.setOptions({\r\n strategy: \"fixed\",\r\n });\r\n\r\n return;\r\n }\r\n }\r\n\r\n this.popper.setOptions({\r\n strategy: \"absolute\",\r\n });\r\n }\r\n\r\n @Element()\r\n host!: HTMLDsoDropdownMenuElement;\r\n\r\n private popper: PopperInstance | undefined;\r\n\r\n get button(): HTMLButtonElement {\r\n const button = this.host.querySelector('button[slot=\"toggle\"]');\r\n\r\n if (!(button instanceof HTMLButtonElement)) {\r\n throw new ReferenceError(\"Mandatory toggle button not found\");\r\n }\r\n\r\n return button;\r\n }\r\n\r\n get tabbables(): FocusableElement[] {\r\n return tabbable(this.host).filter((e) => e !== this.button);\r\n }\r\n\r\n componentDidLoad() {\r\n this.button.setAttribute(\"aria-haspopup\", \"menu\");\r\n this.button.setAttribute(\"aria-expanded\", \"false\");\r\n if (!this.button.id) {\r\n this.button.id = uuidv4();\r\n }\r\n\r\n this.button.addEventListener(\"click\", () => {\r\n this.open = !this.open;\r\n });\r\n\r\n const options = this.host.querySelector(\".dso-dropdown-options\");\r\n if (!options) {\r\n throw new ReferenceError(\"Dropdown options not found\");\r\n }\r\n\r\n options.setAttribute(\"role\", \"menu\");\r\n options.setAttribute(\"aria-labelledby\", this.button.id);\r\n\r\n for (const ul of Array.from(this.host.getElementsByTagName(\"ul\"))) {\r\n ul.setAttribute(\"role\", \"group\");\r\n for (const li of Array.from(ul.getElementsByTagName(\"li\"))) {\r\n li.setAttribute(\"role\", \"none\");\r\n }\r\n }\r\n\r\n if (this.popper) {\r\n return;\r\n }\r\n\r\n const dropdownOptionsElement = this.host.querySelector(\".dso-dropdown-options\");\r\n\r\n if (!(dropdownOptionsElement instanceof HTMLElement)) {\r\n throw new Error(\"dropdown options element is not instanceof HTMLElement\");\r\n }\r\n\r\n this.popper = createPopper(this.button, dropdownOptionsElement, {\r\n placement: this.placement || (this.dropdownAlign === \"right\" ? \"bottom-end\" : \"bottom-start\"),\r\n modifiers: [\r\n {\r\n name: \"offset\",\r\n options: {\r\n offset: [0, this.dropdownOptionsOffset],\r\n },\r\n },\r\n {\r\n name: \"preventOverflow\",\r\n options: {\r\n boundary: this.boundary ? document.querySelector(this.boundary) : null,\r\n },\r\n enabled: this.boundary !== undefined,\r\n },\r\n ],\r\n });\r\n }\r\n\r\n componentDidRender() {\r\n this.setStrategy();\r\n if (this.open) {\r\n this.popper?.update();\r\n this.createTrap();\r\n } else {\r\n this.trap?.deactivate();\r\n\r\n delete this.trap;\r\n }\r\n\r\n for (const li of Array.from(this.host.getElementsByTagName(\"li\"))) {\r\n for (const tab of tabbable(li)) {\r\n tab.setAttribute(\"role\", this.checkable ? \"menuitemradio\" : \"menuitem\");\r\n\r\n if (this.checkable) {\r\n tab.setAttribute(\"aria-checked\", li.classList.contains(\"dso-checked\").toString());\r\n }\r\n }\r\n }\r\n\r\n this.host.removeEventListener(\"keydown\", this.keyDownListener);\r\n this.button.setAttribute(\"aria-expanded\", this.open ? \"true\" : \"false\");\r\n if (this.open) {\r\n this.host.addEventListener(\"keydown\", this.keyDownListener);\r\n }\r\n\r\n this.tabbables.forEach((focusableElement) => {\r\n focusableElement.removeEventListener(\"click\", this.escape);\r\n if (this.open) {\r\n focusableElement.addEventListener(\"click\", this.escape);\r\n }\r\n });\r\n }\r\n\r\n disconnectedCallback() {\r\n this.popper?.destroy();\r\n }\r\n\r\n private keyDownListener = (event: KeyboardEvent) => {\r\n if (event.defaultPrevented) {\r\n return;\r\n }\r\n\r\n switch (event.key) {\r\n case \"ArrowDown\":\r\n this.tabInPopup(1);\r\n break;\r\n\r\n case \"ArrowUp\":\r\n this.tabInPopup(-1);\r\n break;\r\n\r\n case \"Escape\":\r\n this.escape();\r\n break;\r\n\r\n case \" \":\r\n if (event.target instanceof HTMLElement) {\r\n event.target.click();\r\n }\r\n\r\n break;\r\n\r\n default:\r\n return;\r\n }\r\n\r\n event.preventDefault();\r\n };\r\n\r\n private getActiveElement(root: Document | ShadowRoot = document): Element | null {\r\n const activeEl = root.activeElement;\r\n\r\n if (!activeEl) {\r\n return null;\r\n }\r\n\r\n if (activeEl.shadowRoot) {\r\n return this.getActiveElement(activeEl.shadowRoot);\r\n }\r\n\r\n return activeEl;\r\n }\r\n\r\n private tabInPopup(direction: number) {\r\n const tabs = this.tabbables;\r\n const currentIndex = tabs.findIndex((e) => e === this.getActiveElement());\r\n\r\n let nextIndex = currentIndex + direction;\r\n if (nextIndex >= tabs.length) {\r\n nextIndex = 0;\r\n } else if (nextIndex < 0) {\r\n nextIndex = tabs.length - 1;\r\n }\r\n\r\n tabs[nextIndex]?.focus();\r\n }\r\n\r\n private escape = () => {\r\n this.button.focus();\r\n this.open = false;\r\n };\r\n\r\n render() {\r\n return (\r\n <Host tabindex={this.open ? \"-1\" : undefined}>\r\n <slot name=\"toggle\" />\r\n <div hidden={!this.open}>\r\n <slot />\r\n </div>\r\n </Host>\r\n );\r\n }\r\n}\r\n"],"version":3}
1
+ {"file":"dropdown-menu.js","mappings":"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","names":["uuidv4"],"sources":["src/components/dropdown-menu/dropdown-menu.scss?tag=dso-dropdown-menu&encapsulation=shadow","src/components/dropdown-menu/dropdown-menu.tsx"],"sourcesContent":["@use \"~dso-toolkit/src/components/dropdown-menu\";\r\n\r\n:host(:focus) {\r\n outline: none;\r\n}\r\n\r\n:host {\r\n @include dropdown-menu.root();\r\n}\r\n","import { createPopper, Placement, Instance as PopperInstance } from \"@popperjs/core\";\r\nimport { h, Component, Element, Host, Prop, Watch, Listen } from \"@stencil/core\";\r\nimport { FocusableElement, tabbable } from \"tabbable\";\r\nimport { v4 as uuidv4 } from \"uuid\";\r\n\r\nimport { hasOverflow } from \"../../utils/has-overflow\";\r\n\r\n@Component({\r\n tag: \"dso-dropdown-menu\",\r\n styleUrl: \"dropdown-menu.scss\",\r\n shadow: true,\r\n})\r\nexport class DropdownMenu {\r\n /**\r\n * Whether the menu is open or closed.\r\n * This attribute is reflected and mutable.\r\n */\r\n @Prop({ reflect: true, mutable: true })\r\n open = false;\r\n\r\n /**\r\n * Alignment of the dropdown\r\n */\r\n @Prop()\r\n dropdownAlign: \"left\" | \"right\" = \"left\";\r\n\r\n /**\r\n * Space between button and dropdown options\r\n */\r\n @Prop()\r\n dropdownOptionsOffset = 2;\r\n\r\n /**\r\n * Whether the menu is checkable.\r\n */\r\n @Prop()\r\n checkable = false;\r\n\r\n /**\r\n * Selector for the element the dropdown options should not be overflowing.\r\n */\r\n @Prop()\r\n boundary?: string;\r\n\r\n /**\r\n * Force placement of dropdown.\r\n *\r\n * This property overrides `dropdownAlign`.\r\n */\r\n @Prop()\r\n placement?: Placement;\r\n\r\n /**\r\n * Set position strategy of dropdown options\r\n */\r\n @Prop()\r\n strategy: \"auto\" | \"absolute\" | \"fixed\" = \"auto\";\r\n\r\n @Watch(\"placement\")\r\n @Watch(\"dropdownAlign\")\r\n watchPosition() {\r\n if (!this.popper) {\r\n return;\r\n }\r\n\r\n this.popper.setOptions({\r\n placement: this.placement || (this.dropdownAlign === \"right\" ? \"bottom-end\" : \"bottom-start\"),\r\n });\r\n }\r\n\r\n @Watch(\"dropdownOptionsOffset\")\r\n watchOptionsOffset() {\r\n this.popper?.setOptions({\r\n modifiers: [\r\n {\r\n name: \"offset\",\r\n options: {\r\n offset: [0, this.dropdownOptionsOffset],\r\n },\r\n },\r\n ],\r\n });\r\n }\r\n\r\n @Watch(\"strategy\")\r\n watchStrategy() {\r\n this.setStrategy();\r\n }\r\n\r\n private setStrategy() {\r\n if (!this.popper) {\r\n return;\r\n }\r\n\r\n if (this.strategy === \"absolute\" || this.strategy === \"fixed\") {\r\n this.popper.setOptions({\r\n strategy: this.strategy,\r\n });\r\n\r\n return;\r\n }\r\n\r\n let element: Element | null = this.host;\r\n\r\n const boundary = this.boundary || document;\r\n\r\n while (element && element.parentNode !== boundary) {\r\n element = element.parentNode instanceof ShadowRoot ? element.parentNode.host : element.parentElement;\r\n if (element !== null && hasOverflow(element)) {\r\n this.popper.setOptions({\r\n strategy: \"fixed\",\r\n });\r\n\r\n return;\r\n }\r\n }\r\n\r\n this.popper.setOptions({\r\n strategy: \"absolute\",\r\n });\r\n }\r\n\r\n @Element()\r\n host!: HTMLDsoDropdownMenuElement;\r\n\r\n private popper: PopperInstance | undefined;\r\n\r\n get button(): HTMLButtonElement {\r\n const button = this.host.querySelector('button[slot=\"toggle\"]');\r\n\r\n if (!(button instanceof HTMLButtonElement)) {\r\n throw new ReferenceError(\"Mandatory toggle button not found\");\r\n }\r\n\r\n return button;\r\n }\r\n\r\n private tabbables(withButton: boolean): FocusableElement[] {\r\n const tabbables = tabbable(this.host);\r\n\r\n return withButton ? tabbables : tabbables.filter((element) => element !== this.button);\r\n }\r\n\r\n componentDidLoad() {\r\n this.button.setAttribute(\"aria-haspopup\", \"menu\");\r\n this.button.setAttribute(\"aria-expanded\", \"false\");\r\n if (!this.button.id) {\r\n this.button.id = uuidv4();\r\n }\r\n\r\n this.button.addEventListener(\"click\", () => {\r\n this.open = !this.open;\r\n });\r\n\r\n const options = this.host.querySelector(\".dso-dropdown-options\");\r\n if (!options) {\r\n throw new ReferenceError(\"Dropdown options not found\");\r\n }\r\n\r\n options.setAttribute(\"role\", \"menu\");\r\n options.setAttribute(\"aria-labelledby\", this.button.id);\r\n\r\n for (const ul of Array.from(this.host.getElementsByTagName(\"ul\"))) {\r\n ul.setAttribute(\"role\", \"group\");\r\n for (const li of Array.from(ul.getElementsByTagName(\"li\"))) {\r\n li.setAttribute(\"role\", \"none\");\r\n }\r\n }\r\n\r\n if (this.popper) {\r\n return;\r\n }\r\n\r\n const dropdownOptionsElement = this.host.querySelector(\".dso-dropdown-options\");\r\n\r\n if (!(dropdownOptionsElement instanceof HTMLElement)) {\r\n throw new Error(\"dropdown options element is not instanceof HTMLElement\");\r\n }\r\n\r\n this.popper = createPopper(this.button, dropdownOptionsElement, {\r\n placement: this.placement || (this.dropdownAlign === \"right\" ? \"bottom-end\" : \"bottom-start\"),\r\n modifiers: [\r\n {\r\n name: \"offset\",\r\n options: {\r\n offset: [0, this.dropdownOptionsOffset],\r\n },\r\n },\r\n {\r\n name: \"preventOverflow\",\r\n options: {\r\n boundary: this.boundary ? document.querySelector(this.boundary) : null,\r\n },\r\n enabled: this.boundary !== undefined,\r\n },\r\n ],\r\n });\r\n }\r\n\r\n componentDidRender() {\r\n this.setStrategy();\r\n if (this.open) {\r\n this.popper?.update();\r\n }\r\n\r\n for (const li of Array.from(this.host.getElementsByTagName(\"li\"))) {\r\n for (const tab of tabbable(li)) {\r\n tab.setAttribute(\"role\", this.checkable ? \"menuitemradio\" : \"menuitem\");\r\n\r\n if (this.checkable) {\r\n tab.setAttribute(\"aria-checked\", li.classList.contains(\"dso-checked\").toString());\r\n }\r\n }\r\n }\r\n\r\n this.button.setAttribute(\"aria-expanded\", this.open ? \"true\" : \"false\");\r\n }\r\n\r\n @Listen(\"click\", { target: \"window\" })\r\n onClick(event: MouseEvent) {\r\n if (!(event.target instanceof HTMLElement)) {\r\n return;\r\n }\r\n\r\n if (this.open && (!this.host.contains(event.target) || this.tabbables(false).includes(event.target))) {\r\n this.open = false;\r\n }\r\n }\r\n\r\n disconnectedCallback() {\r\n this.popper?.destroy();\r\n }\r\n\r\n private focusOutListener = (event: FocusEvent) => {\r\n if (!(event.relatedTarget instanceof HTMLElement)) {\r\n return;\r\n }\r\n\r\n if (!this.tabbables(true).includes(event.relatedTarget)) {\r\n this.open = false;\r\n }\r\n };\r\n\r\n @Listen(\"keydown\", { target: \"window\" })\r\n keyDownListener(event: KeyboardEvent) {\r\n if (event.defaultPrevented || !this.open) {\r\n return;\r\n }\r\n\r\n switch (event.key) {\r\n case \"Tab\":\r\n if (event.shiftKey) {\r\n this.tabInPopup(this.tabbables(true), -1);\r\n } else {\r\n this.tabInPopup(this.tabbables(true), 1);\r\n }\r\n\r\n break;\r\n case \"ArrowDown\":\r\n this.tabInPopup(this.tabbables(false), 1);\r\n break;\r\n\r\n case \"ArrowUp\":\r\n this.tabInPopup(this.tabbables(false), -1);\r\n break;\r\n\r\n case \"Escape\":\r\n this.escape();\r\n break;\r\n\r\n case \" \":\r\n if (event.target instanceof HTMLElement) {\r\n event.target.click();\r\n }\r\n\r\n break;\r\n\r\n default:\r\n return;\r\n }\r\n\r\n event.preventDefault();\r\n }\r\n\r\n private getActiveElement(root: Document | ShadowRoot = document): Element | null {\r\n const activeEl = root.activeElement;\r\n\r\n if (!activeEl) {\r\n return null;\r\n }\r\n\r\n if (activeEl.shadowRoot) {\r\n return this.getActiveElement(activeEl.shadowRoot);\r\n }\r\n\r\n return activeEl;\r\n }\r\n\r\n private tabInPopup(tabbables: FocusableElement[], direction: number) {\r\n const currentIndex = tabbables.findIndex((e) => e === this.getActiveElement());\r\n\r\n let nextIndex = currentIndex + direction;\r\n if (nextIndex >= tabbables.length) {\r\n nextIndex = 0;\r\n } else if (nextIndex < 0) {\r\n nextIndex = tabbables.length - 1;\r\n }\r\n\r\n tabbables[nextIndex]?.focus();\r\n }\r\n\r\n private escape = () => {\r\n this.button.focus();\r\n this.open = false;\r\n };\r\n\r\n render() {\r\n return (\r\n <Host onFocusout={this.focusOutListener}>\r\n <slot name=\"toggle\" />\r\n <div hidden={!this.open}>\r\n <slot />\r\n </div>\r\n </Host>\r\n );\r\n }\r\n}\r\n"],"version":3}