@shortfuse/materialdesignweb 0.2.0 → 0.5.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.
Files changed (416) hide show
  1. package/.browserslistrc +2 -1
  2. package/.eslintrc.json +188 -30
  3. package/.stylelintrc.json +643 -2
  4. package/.vscode/launch.json +20 -5
  5. package/.vscode/settings.json +3 -0
  6. package/CHANGELOG.md +36 -0
  7. package/README.md +82 -2
  8. package/adapters/datatable/column.js +176 -0
  9. package/adapters/datatable/index.js +253 -437
  10. package/adapters/dom/index.js +586 -0
  11. package/adapters/list/index.js +36 -113
  12. package/adapters/search/index.js +153 -180
  13. package/components/appbar/_spec.scss +165 -0
  14. package/components/appbar/_theme.scss +0 -0
  15. package/components/appbar/index.scss +2 -0
  16. package/components/banner/_spec.scss +83 -0
  17. package/components/banner/_theme.scss +0 -0
  18. package/components/banner/index.scss +2 -0
  19. package/components/bottomnav/README.md +4 -4
  20. package/components/bottomnav/_spec.scss +149 -0
  21. package/components/bottomnav/_theme.scss +0 -0
  22. package/components/bottomnav/index.js +100 -120
  23. package/components/bottomnav/index.scss +2 -0
  24. package/components/bottomnav/item.js +88 -0
  25. package/components/button/README.md +16 -22
  26. package/components/button/_spec.scss +162 -0
  27. package/components/button/_theme.scss +42 -0
  28. package/components/button/index.eta +32 -0
  29. package/components/button/index.js +37 -48
  30. package/components/button/index.pug +18 -0
  31. package/components/button/index.scss +2 -0
  32. package/components/card/_spec.scss +241 -0
  33. package/components/card/_theme.scss +0 -0
  34. package/components/card/index.scss +2 -0
  35. package/components/chip/_spec.scss +111 -0
  36. package/components/chip/_theme.scss +105 -0
  37. package/components/chip/index.js +23 -0
  38. package/components/chip/index.scss +2 -0
  39. package/components/chip/item.js +20 -0
  40. package/components/datatable/_spec.scss +225 -0
  41. package/components/datatable/_theme.scss +128 -0
  42. package/components/datatable/cell.js +44 -0
  43. package/components/datatable/columnheader.js +46 -0
  44. package/components/datatable/index.js +339 -443
  45. package/components/datatable/index.scss +2 -0
  46. package/components/datatable/row.js +48 -0
  47. package/components/datatable/rowheader.js +18 -0
  48. package/components/dialog/_spec.scss +203 -0
  49. package/components/dialog/_theme.scss +7 -0
  50. package/components/dialog/index.js +512 -437
  51. package/components/dialog/index.scss +2 -0
  52. package/components/divider/_spec.scss +11 -0
  53. package/components/divider/_theme.scss +0 -0
  54. package/components/divider/index.scss +2 -0
  55. package/components/elevation/_spec.scss +9 -0
  56. package/components/elevation/_theme.scss +0 -0
  57. package/components/elevation/index.scss +2 -0
  58. package/components/fab/{style.scss → _spec.scss} +104 -79
  59. package/components/fab/_theme.scss +0 -0
  60. package/components/fab/index.js +85 -79
  61. package/components/fab/index.scss +2 -0
  62. package/components/grid/_spec.scss +169 -0
  63. package/components/grid/_theme.scss +0 -0
  64. package/components/grid/index.scss +2 -0
  65. package/components/layout/_mixins.scss +11 -0
  66. package/components/layout/_spec.scss +916 -0
  67. package/components/layout/_theme.scss +19 -0
  68. package/components/layout/index.js +454 -0
  69. package/components/layout/index.scss +2 -0
  70. package/components/list/_spec.scss +363 -0
  71. package/components/list/_theme.scss +102 -0
  72. package/components/list/content.js +106 -0
  73. package/components/list/index.js +234 -79
  74. package/components/list/index.scss +2 -0
  75. package/components/list/item.js +167 -0
  76. package/components/list/secondary.js +45 -0
  77. package/components/menu/_spec.scss +329 -0
  78. package/components/menu/_theme.scss +0 -0
  79. package/components/menu/index.js +636 -651
  80. package/components/menu/index.scss +2 -0
  81. package/components/menu/item.js +231 -0
  82. package/components/progress/_spec.scss +156 -0
  83. package/components/progress/_theme.scss +0 -0
  84. package/components/progress/index.js +29 -13
  85. package/components/progress/index.scss +2 -0
  86. package/components/selection/_spec.scss +376 -0
  87. package/components/selection/_theme.scss +134 -0
  88. package/components/selection/index.eta +60 -0
  89. package/components/selection/index.js +70 -0
  90. package/components/selection/index.pug +30 -0
  91. package/components/selection/index.scss +2 -0
  92. package/components/selection/input.js +54 -0
  93. package/components/selection/radiogroup.js +40 -0
  94. package/components/slider/{style.scss → _spec.scss} +31 -34
  95. package/components/slider/_theme.scss +0 -0
  96. package/components/slider/index.scss +2 -0
  97. package/components/snackbar/_spec.scss +150 -0
  98. package/components/snackbar/_theme.scss +0 -0
  99. package/components/snackbar/index.js +293 -206
  100. package/components/snackbar/index.scss +2 -0
  101. package/components/tab/_spec.scss +220 -0
  102. package/components/tab/_theme.scss +0 -0
  103. package/components/tab/content.js +210 -0
  104. package/components/tab/index.js +229 -213
  105. package/components/tab/index.scss +2 -0
  106. package/components/tab/item.js +88 -0
  107. package/components/tab/list.js +196 -0
  108. package/components/tab/panel.js +54 -0
  109. package/components/textfield/README.md +4 -4
  110. package/components/textfield/_spec.scss +763 -0
  111. package/components/textfield/_theme.scss +264 -0
  112. package/components/textfield/index.eta +74 -0
  113. package/components/textfield/index.js +132 -138
  114. package/components/textfield/index.pug +30 -0
  115. package/components/textfield/index.scss +2 -0
  116. package/components/tooltip/_spec.scss +185 -0
  117. package/components/tooltip/_theme.scss +0 -0
  118. package/components/tooltip/index.scss +2 -0
  119. package/components/type/_spec.scss +227 -0
  120. package/components/type/_theme.scss +0 -0
  121. package/components/type/index.scss +2 -0
  122. package/core/_breakpoint.scss +189 -0
  123. package/core/_elevation.scss +78 -0
  124. package/core/_length.scss +8 -0
  125. package/core/_motion.scss +31 -0
  126. package/core/_platform.scss +12 -0
  127. package/core/_type.scss +128 -0
  128. package/core/aria/attributes.js +141 -0
  129. package/core/aria/button.js +49 -0
  130. package/core/aria/keyboard.js +92 -0
  131. package/core/aria/rovingtabindex.js +175 -0
  132. package/core/aria/tab.js +59 -0
  133. package/core/document/index.js +39 -0
  134. package/core/dom.js +180 -0
  135. package/core/overlay/_spec.scss +28 -0
  136. package/core/overlay/_theme.scss +147 -0
  137. package/core/overlay/index.js +95 -0
  138. package/core/overlay/index.scss +2 -0
  139. package/core/ripple/_spec.scss +196 -0
  140. package/core/ripple/_theme.scss +20 -0
  141. package/core/ripple/index.js +286 -0
  142. package/core/ripple/index.scss +2 -0
  143. package/core/theme/_aliases.scss +15 -0
  144. package/core/theme/_config.scss +8 -0
  145. package/core/theme/_functions.scss +22 -0
  146. package/{components/theming/palettes.scss → core/theme/_palettes.scss} +173 -151
  147. package/core/theme/_spec.scss +0 -0
  148. package/core/theme/_theme.scss +268 -0
  149. package/core/theme/index.js +50 -0
  150. package/core/theme/index.scss +4 -0
  151. package/core/throttler.js +42 -0
  152. package/core/transition/index.js +465 -0
  153. package/docs/_flex.scss +28 -0
  154. package/docs/_menuoptions.js +183 -0
  155. package/docs/_partials/_androidnavbar.eta +5 -0
  156. package/docs/_partials/_androidstatusbar.eta +13 -0
  157. package/docs/_partials/_appbar.eta +27 -0
  158. package/docs/_partials/_buttontest.eta +31 -0
  159. package/docs/_partials/_header.eta +146 -0
  160. package/docs/_partials/_navlistitem.eta +16 -0
  161. package/docs/_partials/_target.eta +1 -0
  162. package/docs/_sample-utils.js +88 -0
  163. package/docs/{src/storage.js → _storage.js} +0 -0
  164. package/docs/docs.scss +331 -0
  165. package/docs/framework.scss +26 -0
  166. package/docs/index.eta +12 -0
  167. package/docs/index.js +7 -0
  168. package/docs/pages/appbar.eta +108 -0
  169. package/docs/pages/appbar.js +0 -0
  170. package/docs/pages/bottomnav.eta +188 -0
  171. package/docs/pages/bottomnav.js +118 -0
  172. package/docs/pages/button.eta +124 -0
  173. package/docs/pages/button.js +224 -0
  174. package/docs/pages/card.eta +90 -0
  175. package/docs/pages/card.js +175 -0
  176. package/docs/pages/chip.eta +122 -0
  177. package/docs/pages/chip.js +80 -0
  178. package/docs/pages/color.eta +143 -0
  179. package/docs/pages/color.js +261 -0
  180. package/docs/pages/datatable.eta +323 -0
  181. package/docs/pages/datatable.js +160 -0
  182. package/docs/pages/dialog.eta +184 -0
  183. package/docs/{src/components → pages}/dialog.js +35 -48
  184. package/docs/pages/dom.eta +26 -0
  185. package/docs/pages/dom.js +140 -0
  186. package/docs/pages/elevation.eta +35 -0
  187. package/docs/pages/elevation.js +0 -0
  188. package/docs/pages/fab.eta +99 -0
  189. package/docs/{src/components → pages}/fab.js +6 -13
  190. package/docs/pages/grid.eta +135 -0
  191. package/docs/pages/grid.js +128 -0
  192. package/docs/pages/layout.eta +8 -0
  193. package/docs/pages/layout.js +0 -0
  194. package/docs/pages/list.eta +465 -0
  195. package/docs/pages/list.js +8 -0
  196. package/docs/pages/menu.eta +274 -0
  197. package/docs/{src/components → pages}/menu.js +26 -42
  198. package/docs/pages/overlay.eta +69 -0
  199. package/docs/pages/overlay.js +3 -0
  200. package/docs/pages/progress.eta +23 -0
  201. package/docs/{src/components → pages}/progress.js +2 -4
  202. package/docs/pages/ripple.eta +27 -0
  203. package/docs/pages/ripple.js +3 -0
  204. package/docs/pages/search.eta +242 -0
  205. package/docs/pages/search.js +226 -0
  206. package/docs/pages/selection.eta +107 -0
  207. package/docs/pages/selection.js +12 -0
  208. package/docs/pages/slider.eta +23 -0
  209. package/docs/pages/slider.js +0 -0
  210. package/docs/pages/snackbar.eta +83 -0
  211. package/docs/{src/components → pages}/snackbar.js +31 -36
  212. package/docs/pages/tab.eta +407 -0
  213. package/docs/pages/tab.js +152 -0
  214. package/docs/pages/textfield.eta +487 -0
  215. package/docs/{src/components → pages}/textfield.js +41 -45
  216. package/docs/pages/tooltip.eta +92 -0
  217. package/docs/pages/tooltip.js +0 -0
  218. package/docs/pages/transition.eta +117 -0
  219. package/docs/pages/transition.js +52 -0
  220. package/docs/pages/type.eta +31 -0
  221. package/docs/pages/type.js +0 -0
  222. package/docs/postrender.js +41 -0
  223. package/docs/prerender.js +16 -0
  224. package/docs/pwa/_dialogs.eta +143 -0
  225. package/docs/pwa/_menus.eta +16 -0
  226. package/docs/pwa/pwa-prerender.js +3 -0
  227. package/docs/pwa/pwa.eta +478 -0
  228. package/docs/pwa/pwa.js +298 -0
  229. package/docs/pwa/pwa.scss +31 -0
  230. package/docs/themes/theme-colored.scss +15 -0
  231. package/docs/themes/theme-default.scss +3 -0
  232. package/index.scss +27 -0
  233. package/jsconfig.json +8 -2
  234. package/package.json +54 -27
  235. package/scripts/deploy-docs.sh +9 -0
  236. package/templates/index.eta +2 -0
  237. package/templates/index.pug +3 -0
  238. package/tsconfig.json +16 -0
  239. package/utils/function.js +3 -0
  240. package/webpack.config.js +224 -68
  241. package/_index.scss +0 -4
  242. package/components/all-components.scss +0 -21
  243. package/components/bottomnav/style.scss +0 -190
  244. package/components/bottomnav/theming.scss +0 -76
  245. package/components/button/style.scss +0 -315
  246. package/components/button/theming.scss +0 -134
  247. package/components/card/style.scss +0 -175
  248. package/components/card/theming.scss +0 -43
  249. package/components/common/dom.js +0 -51
  250. package/components/common/functions.scss +0 -174
  251. package/components/common/mixins.scss +0 -122
  252. package/components/common/motion.scss +0 -36
  253. package/components/common/type.scss +0 -104
  254. package/components/common/variables.scss +0 -46
  255. package/components/datatable/style.scss +0 -257
  256. package/components/datatable/theming.scss +0 -119
  257. package/components/dialog/style.scss +0 -159
  258. package/components/dialog/theming.scss +0 -29
  259. package/components/divider/style.scss +0 -7
  260. package/components/divider/theming.scss +0 -20
  261. package/components/elevation/style.scss +0 -32
  262. package/components/layout/style.scss +0 -223
  263. package/components/list/style.scss +0 -358
  264. package/components/list/theming.scss +0 -83
  265. package/components/menu/style.scss +0 -280
  266. package/components/menu/theming.scss +0 -80
  267. package/components/navdrawer/index.js +0 -200
  268. package/components/navdrawer/style.scss +0 -595
  269. package/components/navdrawer/theming.scss +0 -62
  270. package/components/progress/style.scss +0 -136
  271. package/components/ripple/index.js +0 -63
  272. package/components/ripple/ripple.scss +0 -122
  273. package/components/selection/style.scss +0 -320
  274. package/components/selection/theming.scss +0 -98
  275. package/components/snackbar/style.scss +0 -212
  276. package/components/switch/style.scss +0 -3
  277. package/components/tab/style.scss +0 -275
  278. package/components/tab/theming.scss +0 -34
  279. package/components/template/theming.scss +0 -31
  280. package/components/textfield/style.scss +0 -795
  281. package/components/textfield/theming.scss +0 -256
  282. package/components/theming/globals.scss +0 -25
  283. package/components/theming/theming.scss +0 -559
  284. package/components/toolbar/style.scss +0 -190
  285. package/components/toolbar/theming.scss +0 -32
  286. package/components/tooltip/style.scss +0 -135
  287. package/components/type/style.scss +0 -167
  288. package/components/type/theming.scss +0 -25
  289. package/docs/bottomnav.html +0 -1
  290. package/docs/bottomnav.min.js +0 -2
  291. package/docs/bottomnav.min.js.map +0 -1
  292. package/docs/button.html +0 -1
  293. package/docs/button.min.js +0 -2
  294. package/docs/button.min.js.map +0 -1
  295. package/docs/card.html +0 -1
  296. package/docs/card.min.js +0 -2
  297. package/docs/card.min.js.map +0 -1
  298. package/docs/components.min.css +0 -1
  299. package/docs/components.min.js +0 -2
  300. package/docs/components.min.js.map +0 -1
  301. package/docs/datatable.html +0 -1
  302. package/docs/datatable.min.js +0 -2
  303. package/docs/datatable.min.js.map +0 -1
  304. package/docs/dialog.html +0 -1
  305. package/docs/dialog.min.js +0 -2
  306. package/docs/dialog.min.js.map +0 -1
  307. package/docs/docs.min.css +0 -1
  308. package/docs/docs.min.js +0 -2
  309. package/docs/docs.min.js.map +0 -1
  310. package/docs/elevation.html +0 -1
  311. package/docs/elevation.min.js +0 -2
  312. package/docs/elevation.min.js.map +0 -1
  313. package/docs/fab.html +0 -1
  314. package/docs/fab.min.js +0 -2
  315. package/docs/fab.min.js.map +0 -1
  316. package/docs/index.html +0 -1
  317. package/docs/index.min.js +0 -2
  318. package/docs/index.min.js.map +0 -1
  319. package/docs/layout.html +0 -1
  320. package/docs/layout.min.js +0 -2
  321. package/docs/layout.min.js.map +0 -1
  322. package/docs/list.html +0 -1
  323. package/docs/list.min.js +0 -2
  324. package/docs/list.min.js.map +0 -1
  325. package/docs/menu.html +0 -1
  326. package/docs/menu.min.js +0 -2
  327. package/docs/menu.min.js.map +0 -1
  328. package/docs/navdrawer.html +0 -1
  329. package/docs/navdrawer.min.js +0 -2
  330. package/docs/navdrawer.min.js.map +0 -1
  331. package/docs/prerender.min.js +0 -2
  332. package/docs/prerender.min.js.map +0 -1
  333. package/docs/progress.html +0 -1
  334. package/docs/progress.min.js +0 -2
  335. package/docs/progress.min.js.map +0 -1
  336. package/docs/search.html +0 -1
  337. package/docs/search.min.js +0 -2
  338. package/docs/search.min.js.map +0 -1
  339. package/docs/selection.html +0 -1
  340. package/docs/selection.min.js +0 -2
  341. package/docs/selection.min.js.map +0 -1
  342. package/docs/slider.html +0 -1
  343. package/docs/slider.min.js +0 -2
  344. package/docs/slider.min.js.map +0 -1
  345. package/docs/snackbar.html +0 -1
  346. package/docs/snackbar.min.js +0 -2
  347. package/docs/snackbar.min.js.map +0 -1
  348. package/docs/src/components/bottomnav.js +0 -16
  349. package/docs/src/components/bottomnav.pug +0 -112
  350. package/docs/src/components/button.js +0 -156
  351. package/docs/src/components/button.pug +0 -194
  352. package/docs/src/components/card.js +0 -136
  353. package/docs/src/components/card.pug +0 -133
  354. package/docs/src/components/datatable.js +0 -183
  355. package/docs/src/components/datatable.pug +0 -324
  356. package/docs/src/components/dialog.pug +0 -138
  357. package/docs/src/components/elevation.js +0 -3
  358. package/docs/src/components/elevation.pug +0 -17
  359. package/docs/src/components/fab.pug +0 -84
  360. package/docs/src/components/layout.js +0 -116
  361. package/docs/src/components/layout.pug +0 -104
  362. package/docs/src/components/list.js +0 -15
  363. package/docs/src/components/list.pug +0 -293
  364. package/docs/src/components/menu.pug +0 -292
  365. package/docs/src/components/navdrawer.js +0 -112
  366. package/docs/src/components/navdrawer.pug +0 -113
  367. package/docs/src/components/progress.pug +0 -17
  368. package/docs/src/components/search.js +0 -206
  369. package/docs/src/components/search.pug +0 -149
  370. package/docs/src/components/selection.js +0 -6
  371. package/docs/src/components/selection.pug +0 -116
  372. package/docs/src/components/slider.js +0 -3
  373. package/docs/src/components/slider.pug +0 -19
  374. package/docs/src/components/snackbar.pug +0 -145
  375. package/docs/src/components/tab.js +0 -137
  376. package/docs/src/components/tab.pug +0 -329
  377. package/docs/src/components/textfield.pug +0 -416
  378. package/docs/src/components/toolbar.js +0 -6
  379. package/docs/src/components/toolbar.pug +0 -86
  380. package/docs/src/components/tooltip.js +0 -6
  381. package/docs/src/components/tooltip.pug +0 -76
  382. package/docs/src/components/type.js +0 -6
  383. package/docs/src/components/type.pug +0 -34
  384. package/docs/src/components.scss +0 -1
  385. package/docs/src/docs.scss +0 -284
  386. package/docs/src/index.js +0 -3
  387. package/docs/src/index.pug +0 -6
  388. package/docs/src/menuoptions.js +0 -136
  389. package/docs/src/mixins.pug +0 -139
  390. package/docs/src/prerender.js +0 -26
  391. package/docs/src/sample-utils.js +0 -108
  392. package/docs/src/targetHandler.js +0 -50
  393. package/docs/src/theming.ie11.scss +0 -18
  394. package/docs/src/theming.scss +0 -18
  395. package/docs/tab.html +0 -1
  396. package/docs/tab.min.js +0 -2
  397. package/docs/tab.min.js.map +0 -1
  398. package/docs/textfield.html +0 -2
  399. package/docs/textfield.min.js +0 -2
  400. package/docs/textfield.min.js.map +0 -1
  401. package/docs/theming.ie11.min.css +0 -1
  402. package/docs/theming.ie11.min.js +0 -2
  403. package/docs/theming.ie11.min.js.map +0 -1
  404. package/docs/theming.min.css +0 -1
  405. package/docs/theming.min.js +0 -2
  406. package/docs/theming.min.js.map +0 -1
  407. package/docs/toolbar.html +0 -1
  408. package/docs/toolbar.min.js +0 -2
  409. package/docs/toolbar.min.js.map +0 -1
  410. package/docs/tooltip.html +0 -1
  411. package/docs/tooltip.min.js +0 -2
  412. package/docs/tooltip.min.js.map +0 -1
  413. package/docs/type.html +0 -1
  414. package/docs/type.min.js +0 -2
  415. package/docs/type.min.js.map +0 -1
  416. package/index.js +0 -16
@@ -1,720 +1,705 @@
1
- import { Ripple } from '../ripple/index';
2
- import { dispatchDomEvent, isRtl } from '../common/dom';
3
-
4
1
  // https://www.w3.org/TR/wai-aria-practices/#menu
5
2
 
6
- class MenuItem {
7
- /**
8
- * @param {Element} element
9
- * @return {void}
10
- */
11
- static attach(element) {
12
- element.setAttribute('mdw-js', '');
13
- Ripple.attach(element);
14
- // If mouseover is used, an item can still lose focus via keyboard navigation.
15
- // An extra event listener would need to be created to catch blur but the cursor
16
- // would still remain over the element, thus needing another mousemove event.
17
- // Prioritization is given to less event listeners rather than operations per second.
18
- element.addEventListener('mousemove', MenuItem.onMouseMove);
19
- element.addEventListener('click', MenuItem.onClick);
20
- }
21
-
22
- /**
23
- * @param {MouseEvent|KeyboardEvent|PointerEvent} event
24
- * @return {void}
25
- */
26
- static onClick(event) {
27
- const el = event.currentTarget;
28
- dispatchDomEvent(el, 'mdw:itemactivated');
29
- }
30
-
31
- static onMouseMove(event) {
32
- const el = event.currentTarget;
33
- if (!el) {
34
- return;
35
- }
36
- const previousFocus = document.activeElement;
37
- if (previousFocus === el) {
38
- // Already focused
39
- return;
40
- }
41
- el.focus();
42
- if (document.activeElement !== el) {
43
- if (previousFocus && document.activeElement !== previousFocus) {
44
- previousFocus.focus();
45
- }
46
- }
47
- }
3
+ import {
4
+ dispatchDomEvent,
5
+ isRtl,
6
+ } from '../../core/dom.js';
48
7
 
49
- /**
50
- * @param {Element} element
51
- * @return {void}
52
- */
53
- static detach(element) {
54
- element.removeEventListener('click', MenuItem.onClick);
55
- element.removeEventListener('mousemove', MenuItem.onMouseMove);
56
- element.removeAttribute('mdw-js');
57
- Ripple.detach(element);
58
- }
59
- }
8
+ import * as MenuItem from './item.js';
60
9
 
61
10
  class MenuStack {
62
11
  /**
63
12
  * @param {Element} element
64
13
  * @param {Element} previousFocus
65
- * @param {Object=} state
66
- * @param {Object=} previousState
14
+ * @param {Object} [state]
15
+ * @param {Object} [previousState]
16
+ * @param {MouseEvent|PointerEvent} [originalEvent]
67
17
  */
68
- constructor(element, previousFocus, state, previousState) {
18
+ constructor(element, previousFocus, state, previousState, originalEvent) {
69
19
  this.element = element;
70
20
  this.previousFocus = previousFocus;
71
21
  this.state = state;
72
22
  this.previousState = previousState;
23
+ this.originalEvent = originalEvent;
24
+ this.pendingResizeOperation = null;
73
25
  }
74
26
  }
75
27
 
76
28
  /** @type {MenuStack[]} */
77
29
  const OPEN_MENUS = [];
78
- class Menu {
79
- /**
80
- * @param {Element} menuElement
81
- * @return {void}
82
- */
83
- static attach(menuElement) {
84
- menuElement.setAttribute('mdw-js', '');
85
- let menuCloser = menuElement.getElementsByClassName('mdw-menu__close')[0];
86
- if (!menuCloser) {
87
- menuCloser = document.createElement('div');
88
- menuCloser.classList.add('mdw-menu__close');
89
- if (menuElement.firstChild) {
90
- menuElement.insertBefore(menuCloser, menuElement.firstChild);
91
- } else {
92
- menuElement.appendChild(menuCloser);
93
- }
94
- }
95
- menuCloser.addEventListener('click', Menu.onMenuCloserClick);
96
- menuElement.addEventListener('keydown', Menu.onKeyDown);
97
- }
98
30
 
99
- static onMenuCloserClick(event) {
100
- const closer = event.currentTarget;
101
- if (!closer) {
102
- return;
103
- }
104
- const menu = closer.parentElement;
105
- if (!menu) {
106
- return;
107
- }
108
- if (closer instanceof HTMLAnchorElement) {
109
- event.preventDefault();
110
- }
111
- Menu.hide(menu);
112
- }
31
+ export const HIDE_EVENT = 'mdw:menu-hide';
113
32
 
114
- /**
115
- * @param {PopStateEvent} event
116
- * @return {void}
117
- */
118
- static onPopState(event) {
119
- if (!event.state) {
120
- return;
121
- }
122
- const lastOpenMenu = OPEN_MENUS[OPEN_MENUS.length - 1];
123
- if (!lastOpenMenu || !lastOpenMenu.previousState) {
124
- return;
125
- }
126
- if ((lastOpenMenu.previousState === event.state) || Object.keys(event.state)
127
- .every(key => event.state[key] === lastOpenMenu.previousState[key])) {
128
- Menu.hide(lastOpenMenu.element);
129
- }
33
+ /**
34
+ * @param {Element} menuElement
35
+ * @return {boolean} handled
36
+ */
37
+ export function hide(menuElement) {
38
+ if (menuElement.getAttribute('aria-hidden') === 'true') {
39
+ return false;
130
40
  }
131
-
132
- static onKeyDown(event) {
133
- const menuElement = event.currentTarget;
134
- if (!menuElement || menuElement.hasAttribute('mdw-hide') || !menuElement.hasAttribute('mdw-show')) {
135
- return;
136
- }
137
- if (event.key === 'Tab') {
138
- this.previousFocus = null;
139
- Menu.hide(menuElement);
140
- return;
141
- }
142
- if (event.key === 'Escape' || event.key === 'Esc') {
143
- event.stopPropagation();
144
- event.preventDefault();
145
- Menu.hide(menuElement);
146
- return;
147
- }
148
- if (event.key === 'ArrowUp' || (event.key === 'Up')) {
149
- event.stopPropagation();
150
- event.preventDefault();
151
- Menu.selectNextMenuItem(menuElement, true);
152
- return;
153
- }
154
- if (event.key === 'ArrowDown' || (event.key === 'Down')) {
155
- event.stopPropagation();
156
- event.preventDefault();
157
- Menu.selectNextMenuItem(menuElement, false);
158
- }
159
- if (!document.activeElement) {
160
- return;
161
- }
162
- if (document.activeElement === menuElement) {
163
- return;
164
- }
165
- if (document.activeElement.hasAttribute('disabled')) {
166
- return;
41
+ menuElement.setAttribute('aria-hidden', 'true');
42
+ let stackIndex = -1;
43
+ OPEN_MENUS.some((stack, index) => {
44
+ if (stack.element === menuElement) {
45
+ stackIndex = index;
46
+ return true;
167
47
  }
168
- if (event.key === 'Spacebar' || (event.key === ' ')) {
169
- event.stopPropagation();
170
- event.preventDefault();
171
- /**
172
- * if (document.activeElement.hasAttribute('mdw-checked')) {
173
- * document.activeElement.removeAttribute('mdw-checked');
174
- * } else {
175
- * document.activeElement.setAttribute('mdw-checked', '');
176
- * }
177
- */
178
- return;
48
+ return false;
49
+ });
50
+ if (stackIndex !== -1) {
51
+ const menuStack = OPEN_MENUS[stackIndex];
52
+ if (menuStack.previousFocus && menuStack.previousFocus instanceof HTMLElement) {
53
+ menuStack.previousFocus.focus();
179
54
  }
180
- if (event.key === 'Enter') {
181
- event.stopPropagation();
182
- event.preventDefault();
183
- document.activeElement.click();
55
+ OPEN_MENUS.splice(stackIndex, 1);
56
+ if (menuStack.state && window.history && window.history.state // IE11 returns a cloned state object, not the original
57
+ && menuStack.state.hash === window.history.state.hash) {
58
+ window.history.back();
184
59
  }
185
60
  }
61
+ if (!OPEN_MENUS.length) {
62
+ // eslint-disable-next-line no-use-before-define
63
+ window.removeEventListener('popstate', onPopState);
64
+ // eslint-disable-next-line no-use-before-define
65
+ window.removeEventListener('resize', onWindowResize);
66
+ }
67
+ dispatchDomEvent(menuElement, HIDE_EVENT);
68
+ return true;
69
+ }
186
70
 
187
- static detach(menuElement) {
188
- Menu.hide(menuElement);
189
- const menuCloser = menuElement.getElementsByClassName('mdw-menu__close')[0];
190
- if (menuCloser) {
191
- menuCloser.removeEventListener('click', Menu.onMenuCloserClick);
192
- }
193
- menuElement.addEventListener('keydown', Menu.onKeyDown);
194
- menuElement.removeAttribute('mdw-js');
195
- menuElement.removeAttribute('mdw-show');
196
- menuElement.removeAttribute('mdw-hide');
197
- const popupElement = menuElement.getElementsByClassName('mdw-menu__popup')[0];
198
- if (popupElement) {
199
- popupElement.style.removeProperty('top');
200
- popupElement.style.removeProperty('left');
201
- popupElement.style.removeProperty('right');
202
- popupElement.style.removeProperty('bottom');
203
- popupElement.style.removeProperty('margin');
204
- popupElement.style.removeProperty('transform-origin');
205
- popupElement.style.removeProperty('position');
206
- if (popupElement.hasAttribute('style') && !popupElement.getAttribute('style')) {
207
- popupElement.removeAttribute('style');
208
- }
209
- }
210
- const menuItems = menuElement.getElementsByClassName('mdw-menu__item');
211
- for (let i = 0; i < menuItems.length; i += 1) {
212
- MenuItem.detach(menuItems.item(i));
213
- }
71
+ /**
72
+ * @param {Element} menuElement
73
+ * @return {void}
74
+ */
75
+ export function setupARIA(menuElement) {
76
+ if (menuElement.hasAttribute('mdw-no-aria')) {
77
+ return;
78
+ }
79
+ if (!menuElement.hasAttribute('aria-hidden')) {
80
+ menuElement.setAttribute('aria-hidden', 'true');
214
81
  }
82
+ for (const el of menuElement.getElementsByClassName('mdw-divider')) {
83
+ el.setAttribute('role', 'separator');
84
+ }
85
+ menuElement.setAttribute('role', 'menu');
86
+ const popupElement = menuElement.getElementsByClassName('mdw-menu__popup')[0];
87
+ if (popupElement) {
88
+ popupElement.setAttribute('role', 'none');
89
+ }
90
+ }
215
91
 
216
- static selectNextMenuItem(menu, backwards) {
217
- const menuItems = menu.getElementsByClassName('mdw-menu__item');
218
- let foundTarget = false;
219
- let candidate = null;
220
- let firstFocusableItem = null;
221
- let lastFocusableElement = null;
222
- const target = document.activeElement;
92
+ /**
93
+ * @param {Event} event
94
+ * @return {void}
95
+ */
96
+ export function onMenuScroll(event) {
97
+ // JS needed for Safari
98
+ if (event.target === event.currentTarget) {
99
+ event.preventDefault();
100
+ event.stopPropagation();
101
+ }
102
+ if (event.type !== 'scroll') {
103
+ return;
104
+ }
105
+ const element = /** @type {HTMLElement} */ (event.currentTarget);
106
+ if (element.scrollTop !== 0) {
107
+ element.scrollTop = 0;
108
+ }
109
+ if (element.scrollLeft !== 0) {
110
+ element.scrollLeft = 0;
111
+ }
112
+ }
223
113
 
224
- // Hidden elements cannot be focused
225
- // Disabled elements cannot be focused on IE11
226
- // Skip elements that fail to receive focus
227
- for (let i = 0; i < menuItems.length; i += 1) {
228
- const el = menuItems.item(i);
229
- el.focus();
230
- const focusable = (document.activeElement === el);
231
- if (focusable) {
232
- if (!firstFocusableItem) {
233
- firstFocusableItem = el;
234
- }
235
- lastFocusableElement = el;
236
- }
237
- if (el === target) {
238
- foundTarget = true;
239
- if (backwards && candidate) {
240
- break;
241
- }
242
- } else if (backwards) {
243
- if (focusable) {
244
- candidate = el;
245
- }
246
- } else if (foundTarget) {
247
- if (focusable) {
248
- candidate = el;
249
- break;
250
- }
251
- }
252
- }
253
- if (!candidate) {
254
- if (backwards) {
255
- candidate = lastFocusableElement;
256
- } else {
257
- candidate = firstFocusableItem;
258
- }
259
- }
260
- if (candidate && document.activeElement !== candidate) {
261
- candidate.focus();
262
- }
114
+ /**
115
+ * @param {MouseEvent|PointerEvent} event
116
+ * @return {void}
117
+ */
118
+ export function onMenuClick(event) {
119
+ if (event.currentTarget === event.target && event.target instanceof HTMLElement) {
120
+ event.stopPropagation();
121
+ hide(event.target);
263
122
  }
123
+ }
264
124
 
265
- /**
266
- * @param {Element} menuElement
267
- * @param {Element} popupElement
268
- * @param {MouseEvent=} event
269
- * @param {boolean=} [alignTarget=true]
270
- * @return {void}
271
- */
272
- static updateMenuPosition(menuElement, popupElement, event, alignTarget) {
273
- let top = 'auto';
274
- let left = 'auto';
275
- let transformOrigin = '';
276
- const useAlignTarget = (alignTarget !== false);
277
- const margin = useAlignTarget ? '0' : '';
278
- const mdwPosition = menuElement.getAttribute('mdw-position') || '';
279
- const mdwDirection = menuElement.getAttribute('mdw-direction') || '';
280
- let alignTop = mdwPosition.indexOf('top') !== -1;
281
- let alignBottom = mdwPosition.indexOf('bottom') !== -1;
282
- let alignVCenter = mdwPosition.indexOf('vcenter') !== -1;
125
+ /**
126
+ * @param {Element} menuElement
127
+ * @param {HTMLElement} popupElement
128
+ * @param {MouseEvent|PointerEvent} [event]
129
+ * @param {boolean} [alignTarget=true]
130
+ * @return {void}
131
+ */
132
+ export function updateMenuPosition(menuElement, popupElement, event, alignTarget) {
133
+ let top = 'auto';
134
+ let left = 'auto';
135
+ let transformOrigin = '';
136
+ const useAlignTarget = (alignTarget !== false);
137
+ const margin = useAlignTarget ? '0' : '';
138
+ const mdwPosition = menuElement.getAttribute('mdw-position') || '';
139
+ const mdwDirection = menuElement.getAttribute('mdw-direction') || '';
140
+ let alignTop = mdwPosition.includes('top');
141
+ let alignBottom = mdwPosition.includes('bottom');
142
+ let alignVCenter = mdwPosition.includes('vcenter');
283
143
 
284
- const alignStart = mdwPosition.indexOf('start') !== -1;
285
- const alignEnd = mdwPosition.indexOf('end') !== -1;
286
- let alignLeft = mdwPosition.indexOf('left') !== -1;
287
- let alignRight = mdwPosition.indexOf('right') !== -1;
288
- let alignHCenter = mdwPosition.indexOf('hcenter') !== -1;
144
+ const alignStart = mdwPosition.includes('start');
145
+ const alignEnd = mdwPosition.includes('end');
146
+ let alignLeft = mdwPosition.includes('left');
147
+ let alignRight = mdwPosition.includes('right');
148
+ let alignHCenter = mdwPosition.includes('hcenter');
289
149
 
290
- let openUp = mdwDirection.indexOf('up') !== -1;
291
- let openDown = mdwDirection.indexOf('down') !== -1;
292
- const openNormal = mdwDirection.indexOf('normal') !== -1;
293
- const openReverse = mdwDirection.indexOf('reverse') !== -1;
294
- let openVCenter = mdwDirection.indexOf('vcenter') !== -1;
295
- let openHCenter = mdwDirection.indexOf('hcenter') !== -1;
296
- let openLtr = mdwDirection.indexOf('ltr') !== -1;
297
- let openRtl = mdwDirection.indexOf('rtl') !== -1;
150
+ let openUp = mdwDirection.includes('up');
151
+ let openDown = mdwDirection.includes('down');
152
+ const openNormal = mdwDirection.includes('normal');
153
+ const openReverse = mdwDirection.includes('reverse');
154
+ let openVCenter = mdwDirection.includes('vcenter');
155
+ let openHCenter = mdwDirection.includes('hcenter');
156
+ let openLtr = mdwDirection.includes('ltr');
157
+ let openRtl = mdwDirection.includes('rtl');
298
158
 
299
- const target = event.currentTarget || event.target;
159
+ const target = /** @type {HTMLElement} */ (event.currentTarget || event.target);
300
160
 
301
- let isPageRTL = null;
302
- if (alignStart || alignEnd || openNormal || openReverse) {
303
- // Using page-direction based values
304
- isPageRTL = isRtl();
305
- if (alignStart || alignEnd) {
306
- if (alignStart) {
307
- if (isPageRTL) {
308
- alignRight = true;
309
- } else {
310
- alignLeft = true;
311
- }
312
- } else if (isPageRTL) {
313
- alignLeft = true;
314
- } else {
161
+ let isPageRTL = null;
162
+ if (alignStart || alignEnd || openNormal || openReverse) {
163
+ // Using page-direction based values
164
+ isPageRTL = isRtl();
165
+ if (alignStart || alignEnd) {
166
+ if (alignStart) {
167
+ if (isPageRTL) {
315
168
  alignRight = true;
169
+ } else {
170
+ alignLeft = true;
316
171
  }
172
+ } else if (isPageRTL) {
173
+ alignLeft = true;
174
+ } else {
175
+ alignRight = true;
317
176
  }
318
- if (openNormal || openReverse) {
319
- if (openNormal) {
320
- if (isPageRTL) {
321
- openRtl = true;
322
- } else {
323
- openLtr = true;
324
- }
325
- } else if (isPageRTL) {
326
- openLtr = true;
327
- } else {
177
+ }
178
+ if (openNormal || openReverse) {
179
+ if (openNormal) {
180
+ if (isPageRTL) {
328
181
  openRtl = true;
182
+ } else {
183
+ openLtr = true;
329
184
  }
185
+ } else if (isPageRTL) {
186
+ openLtr = true;
187
+ } else {
188
+ openRtl = true;
330
189
  }
331
190
  }
332
- const offsetTop = (useAlignTarget ? -event.offsetY : 0);
333
- const offsetBottom = (useAlignTarget ? target.clientHeight - event.offsetY : 0);
334
- const offsetLeft = (useAlignTarget ? -event.offsetX : 0);
335
- const offsetRight = (useAlignTarget ? target.clientWidth - event.offsetX : 0);
336
- let { pageX, pageY } = event;
337
- if (!pageX && !pageY) {
338
- const rect = target.getBoundingClientRect();
339
- pageX = rect.x;
340
- pageY = rect.y;
341
- }
191
+ }
192
+ const offsetTop = useAlignTarget ? 0 : -event.offsetY;
193
+ const offsetBottom = useAlignTarget ? target.clientHeight : event.offsetY;
194
+ const offsetLeft = useAlignTarget ? 0 : -event.offsetX;
195
+ const offsetRight = useAlignTarget ? target.clientWidth : event.offsetX;
196
+ const rect = target.getBoundingClientRect();
197
+ const pageX = rect.left;
198
+ const pageY = rect.top;
342
199
 
343
- /* Automatic Positioning
344
- *
345
- * 9 Positions
346
- * 3 7 4
347
- * ┌─────────┐
348
- * │ │
349
- * 5 │ 9 │ 6
350
- * │ │
351
- * └─────────┘
352
- * 1 8 2
353
- *
354
- * 1: Bottom Left
355
- * 2: Bottom Right
356
- * 3: Top Left
357
- * 4: Top Right
358
- * 5: VCenter Left
359
- * 6: VCenter Right
360
- * 7: HCenter Top
361
- * 8: HCenter Bottom
362
- * 9: VCenter HCenter
363
- *
364
- * 9 Directions:
365
- * a - Down LTR
366
- * b - Down RTL
367
- * c - Up LTR
368
- * d - Up RTL
369
- * e - LTR
370
- * f - RTL
371
- * g - Down
372
- * h - Up
373
- * i - Center
374
- *
375
- *
376
- * 16 total combos
377
- * 1a 1b 1c 1d └↘ └↙ └↗ └↖
378
- * 2a 2b 2c 2d ┘↘ ┘↙ ┘↗ ┘↖
379
- * 3a 3b 3c 3d ┌↘ ┌↙ ┌↗ ┌↖
380
- * 4a 4b 4c 4d ┐↘ ┐↙ ┐↗ ┐↖
381
- *
382
- * Avoid using opposite angle
383
- *
384
- * 1a XX 1c 1d └↘ ██ └↗ └↖
385
- * XX 2b 2c 2d ██ ┘↙ ┘↗ ┘↖
386
- * 1a 3b 3c XX ┌↘ ┌↙ ┌↗ ██
387
- * 4a 4b XX 4d ┐↘ ┐↙ ██ ┐↖
388
- *
389
- *
390
- * Preference Order:
391
- * - Flow from corner 1a 2b 3c 4d └↘ ┘↙ ┌↗ ┐↖
392
- * - Open adjacent to target 4a 3b 2c 1d ┐↘ ┌↙ ┘↗ └↖
393
- * - Overlay target 3a 4b 1c 2d ┌↘ ┐↙ └↗ ┘↖
394
- * - Open from horizontal side 5e 6f │→ │←
395
- * - Open from center 9i █·
396
- */
200
+ /* Automatic Positioning
201
+ *
202
+ * 9 Positions
203
+ * 3 7 4
204
+ * ┌─────────┐
205
+ * │ │
206
+ * 5 │ 9 │ 6
207
+ * │ │
208
+ * └─────────┘
209
+ * 1 8 2
210
+ *
211
+ * 1: Bottom Left
212
+ * 2: Bottom Right
213
+ * 3: Top Left
214
+ * 4: Top Right
215
+ * 5: VCenter Left
216
+ * 6: VCenter Right
217
+ * 7: HCenter Top
218
+ * 8: HCenter Bottom
219
+ * 9: VCenter HCenter
220
+ *
221
+ * 9 Directions:
222
+ * a - Down LTR
223
+ * b - Down RTL
224
+ * c - Up LTR
225
+ * d - Up RTL
226
+ * e - LTR
227
+ * f - RTL
228
+ * g - Down
229
+ * h - Up
230
+ * i - Center
231
+ *
232
+ *
233
+ * 16 total combos
234
+ * 1a 1b 1c 1d └↘ └↙ └↗ └↖
235
+ * 2a 2b 2c 2d ┘↘ ┘↙ ┘↗ ┘↖
236
+ * 3a 3b 3c 3d ┌↘ ┌↙ ┌↗ ┌↖
237
+ * 4a 4b 4c 4d ┐↘ ┐↙ ┐↗ ┐↖
238
+ *
239
+ * Avoid using opposite angle
240
+ *
241
+ * 1a XX 1c 1d └↘ ██ └↗ └↖
242
+ * XX 2b 2c 2d ██ ┘↙ ┘↗ ┘↖
243
+ * 1a 3b 3c XX ┌↘ ┌↙ ┌↗ ██
244
+ * 4a 4b XX 4d ┐↘ ┐↙ ██ ┐↖
245
+ *
246
+ *
247
+ * Preference Order:
248
+ * - Flow from corner 1a 2b 3c 4d └↘ ┘↙ ┌↗ ┐↖
249
+ * - Open adjacent to target 4a 3b 2c 1d ┐↘ ┌↙ ┘↗ └↖
250
+ * - Overlay target 3a 4b 1c 2d ┌↘ ┐↙ └↗ ┘↖
251
+ * - Open from horizontal side 5e 6f │→ │←
252
+ * - Open from center 9i █·
253
+ */
397
254
 
398
- popupElement.style.setProperty('max-height', 'none');
399
- const popupElementHeight = popupElement.clientHeight;
400
- const canOpenDownwardsFromBottom = !alignTop && !alignVCenter
401
- && !openUp && !openVCenter
402
- && popupElementHeight + (pageY + offsetBottom) <= window.innerHeight;
403
- const canOpenDownwardsFromTop = !alignBottom && !alignVCenter
404
- && !openUp && !openVCenter
405
- && popupElementHeight + (pageY + offsetTop) <= window.innerHeight;
406
- const canOpenUpwardsFromTop = !alignBottom && !alignVCenter && !openDown
407
- && !openVCenter
408
- && pageY + offsetTop >= popupElementHeight;
409
- const canOpenUpwardsFromBottom = !alignTop && !alignVCenter && !openDown
410
- && !openVCenter
411
- && pageY + offsetBottom >= popupElementHeight;
412
- const canOpenRightwardsFromLeft = !alignRight && !alignHCenter
413
- && !openRtl && !openHCenter
414
- && popupElement.clientWidth + (pageX + offsetLeft) <= window.innerWidth;
415
- const canOpenRightwardsFromRight = !alignLeft && !alignHCenter
416
- && !openRtl && !openHCenter
417
- && popupElement.clientWidth + (pageX + offsetRight) <= window.innerWidth;
418
- const canOpenLeftwardsFromRight = !alignLeft && !alignHCenter
419
- && !openLtr && !openHCenter
420
- && pageX + offsetRight >= popupElement.clientWidth;
421
- const canOpenLeftwardsFromLeft = !alignRight && !alignHCenter
422
- && !openLtr && !openHCenter
423
- && pageX + offsetLeft >= popupElement.clientWidth;
424
- const canOpenFromCenter = !alignLeft && !alignRight && !alignTop && !alignBottom
425
- && !openUp && !openDown
426
- && ((pageX + offsetLeft) / 2) >= (popupElement.clientWidth / 2)
427
- && (popupElement.clientWidth / 2) + ((pageX + offsetLeft) / 2) <= window.innerWidth;
428
- popupElement.style.removeProperty('max-height');
429
- const candidates = [
430
- canOpenDownwardsFromBottom && canOpenRightwardsFromLeft, // 1a └↘
431
- canOpenDownwardsFromBottom && canOpenLeftwardsFromRight, // 2b ┘↙
432
- canOpenUpwardsFromTop && canOpenRightwardsFromLeft, // 3c ┌↗
433
- canOpenUpwardsFromTop && canOpenLeftwardsFromLeft, // 4d ┐↖
434
- canOpenDownwardsFromTop && canOpenRightwardsFromRight, // 4a ┐↘
435
- canOpenDownwardsFromTop && canOpenLeftwardsFromLeft, // 3b ┌↙
436
- canOpenUpwardsFromBottom && canOpenRightwardsFromRight, // 2c ┘↗
437
- canOpenUpwardsFromBottom && canOpenLeftwardsFromLeft, // 1d └↖
438
- canOpenDownwardsFromTop && canOpenRightwardsFromLeft, // 3a ┌↘
439
- canOpenDownwardsFromTop && canOpenLeftwardsFromRight, // 4b ┐↙
440
- canOpenUpwardsFromBottom && canOpenRightwardsFromLeft, // 1c └↗
441
- canOpenUpwardsFromBottom && canOpenRightwardsFromRight, // 2d ┘↖
442
- canOpenRightwardsFromLeft, // 5e │→
443
- canOpenLeftwardsFromRight, // 6f │←
444
- canOpenFromCenter, // 9i █·
445
- ].map((value, index) => {
446
- if (value) {
447
- return index + 1;
448
- }
449
- return 0;
450
- }).filter(value => value !== 0);
451
- if (candidates.length) {
452
- let candidateNumber;
453
- if (isPageRTL === null) {
454
- isPageRTL = isRtl();
455
- }
456
- if (isPageRTL) {
457
- candidateNumber = [2, 1, 4, 3, 6, 5, 8, 7, 10, 9, 12, 11, 14, 13, 15]
458
- .filter(number => candidates.indexOf(number) !== -1)[0];
459
- } else {
460
- candidateNumber = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
461
- .filter(number => candidates.indexOf(number) !== -1)[0];
462
- }
463
- if (candidateNumber == null) {
464
- candidateNumber = isPageRTL ? 2 : 1;
465
- }
466
- switch (candidateNumber) {
467
- // Position
468
- default:
469
- case 1:
470
- case 8:
471
- case 11:
472
- alignBottom = true; alignLeft = true;
473
- break;
474
- case 2:
475
- case 7:
476
- case 12:
477
- alignBottom = true; alignRight = true;
478
- break;
479
- case 3:
480
- case 6:
481
- case 9:
482
- alignTop = true; alignLeft = true;
483
- break;
484
- case 4:
485
- case 5:
486
- case 10:
487
- alignTop = true; alignRight = true;
488
- break;
489
- case 13:
490
- alignVCenter = true; alignLeft = true;
491
- break;
492
- case 14:
493
- alignVCenter = true; alignRight = true;
494
- break;
495
- case 15:
496
- alignVCenter = true; alignHCenter = true;
497
- }
498
- switch (candidateNumber) {
499
- // Direction
500
- default:
501
- case 1:
502
- case 5:
503
- case 9:
504
- openDown = true; openLtr = true;
505
- break;
506
- case 2:
507
- case 6:
508
- case 10:
509
- openDown = true; openRtl = true;
510
- break;
511
- case 3:
512
- case 7:
513
- case 11:
514
- openUp = true; openLtr = true;
515
- break;
516
- case 4:
517
- case 8:
518
- case 12:
519
- openUp = true; openRtl = true;
520
- break;
521
- case 13:
522
- openLtr = true; openVCenter = true;
523
- break;
524
- case 14:
525
- openRtl = true; openVCenter = true;
526
- break;
527
- case 15:
528
- openHCenter = true; openVCenter = true;
529
- break;
530
- }
255
+ popupElement.style.setProperty('max-height', 'none');
256
+ const popupElementHeight = popupElement.clientHeight;
257
+ const popupElementWidth = popupElement.clientWidth;
258
+ const canOpenDownwardsFromBottom = !alignTop && !alignVCenter
259
+ && !openUp && !openVCenter
260
+ && popupElementHeight + (pageY + offsetBottom) <= window.innerHeight;
261
+ const canOpenDownwardsFromTop = !alignBottom && !alignVCenter
262
+ && !openUp && !openVCenter
263
+ && popupElementHeight + (pageY + offsetTop) <= window.innerHeight;
264
+ const canOpenUpwardsFromTop = !alignBottom && !alignVCenter && !openDown
265
+ && !openVCenter
266
+ && pageY + offsetTop >= popupElementHeight;
267
+ const canOpenUpwardsFromBottom = !alignTop && !alignVCenter && !openDown
268
+ && !openVCenter
269
+ && pageY + offsetBottom >= popupElementHeight;
270
+ const canOpenRightwardsFromLeft = !alignRight && !alignHCenter
271
+ && !openRtl && !openHCenter
272
+ && popupElementWidth + (pageX + offsetLeft) <= window.innerWidth;
273
+ const canOpenRightwardsFromRight = !alignLeft && !alignHCenter
274
+ && !openRtl && !openHCenter
275
+ && popupElementWidth + (pageX + offsetRight) <= window.innerWidth;
276
+ const canOpenLeftwardsFromRight = !alignLeft && !alignHCenter
277
+ && !openLtr && !openHCenter
278
+ && pageX + offsetRight >= popupElementWidth;
279
+ const canOpenLeftwardsFromLeft = !alignRight && !alignHCenter
280
+ && !openLtr && !openHCenter
281
+ && pageX + offsetLeft >= popupElementWidth;
282
+ const canOpenFromCenter = !alignLeft && !alignRight && !alignTop && !alignBottom
283
+ && !openUp && !openDown
284
+ && ((pageX + offsetLeft) / 2) >= (popupElementWidth / 2)
285
+ && (popupElementWidth / 2) + ((pageX + offsetLeft) / 2) <= window.innerWidth;
286
+ popupElement.style.removeProperty('max-height');
287
+ const candidates = [
288
+ canOpenDownwardsFromBottom && canOpenRightwardsFromLeft, // 1a └↘
289
+ canOpenDownwardsFromBottom && canOpenLeftwardsFromRight, // 2b ┘↙
290
+ canOpenUpwardsFromTop && canOpenRightwardsFromLeft, // 3c ┌↗
291
+ canOpenUpwardsFromTop && canOpenLeftwardsFromLeft, // 4d ┐↖
292
+ canOpenDownwardsFromTop && canOpenRightwardsFromRight, // 4a ┐↘
293
+ canOpenDownwardsFromTop && canOpenLeftwardsFromLeft, // 3b ┌↙
294
+ canOpenUpwardsFromBottom && canOpenRightwardsFromRight, // 2c ┘↗
295
+ canOpenUpwardsFromBottom && canOpenLeftwardsFromLeft, // 1d └↖
296
+ canOpenDownwardsFromTop && canOpenRightwardsFromLeft, // 3a ┌↘
297
+ canOpenDownwardsFromTop && canOpenLeftwardsFromRight, // 4b ┐↙
298
+ canOpenUpwardsFromBottom && canOpenRightwardsFromLeft, // 1c └↗
299
+ canOpenUpwardsFromBottom && canOpenRightwardsFromRight, // 2d ┘↖
300
+ canOpenRightwardsFromLeft, // 5e │→
301
+ canOpenLeftwardsFromRight, // 6f │←
302
+ canOpenFromCenter, // 9i █·
303
+ ].map((value, index) => {
304
+ if (value) {
305
+ return index + 1;
306
+ }
307
+ return 0;
308
+ }).filter((value) => value !== 0);
309
+ if (candidates.length) {
310
+ let candidateNumber;
311
+ if (isPageRTL === null) {
312
+ isPageRTL = isRtl();
531
313
  }
314
+ if (isPageRTL) {
315
+ candidateNumber = [2, 1, 4, 3, 6, 5, 8, 7, 10, 9, 12, 11, 14, 13, 15]
316
+ .find((number) => candidates.includes(number));
317
+ } else {
318
+ candidateNumber = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
319
+ .find((number) => candidates.includes(number));
320
+ }
321
+ if (candidateNumber == null) {
322
+ candidateNumber = isPageRTL ? 2 : 1;
323
+ }
324
+ switch (candidateNumber) {
325
+ // Position
326
+ default:
327
+ case 1:
328
+ case 8:
329
+ case 11:
330
+ alignBottom = true; alignLeft = true;
331
+ break;
332
+ case 2:
333
+ case 7:
334
+ case 12:
335
+ alignBottom = true; alignRight = true;
336
+ break;
337
+ case 3:
338
+ case 6:
339
+ case 9:
340
+ alignTop = true; alignLeft = true;
341
+ break;
342
+ case 4:
343
+ case 5:
344
+ case 10:
345
+ alignTop = true; alignRight = true;
346
+ break;
347
+ case 13:
348
+ alignVCenter = true; alignLeft = true;
349
+ break;
350
+ case 14:
351
+ alignVCenter = true; alignRight = true;
352
+ break;
353
+ case 15:
354
+ alignVCenter = true; alignHCenter = true;
355
+ }
356
+ switch (candidateNumber) {
357
+ // Direction
358
+ default:
359
+ case 1:
360
+ case 5:
361
+ case 9:
362
+ openDown = true; openLtr = true;
363
+ break;
364
+ case 2:
365
+ case 6:
366
+ case 10:
367
+ openDown = true; openRtl = true;
368
+ break;
369
+ case 3:
370
+ case 7:
371
+ case 11:
372
+ openUp = true; openLtr = true;
373
+ break;
374
+ case 4:
375
+ case 8:
376
+ case 12:
377
+ openUp = true; openRtl = true;
378
+ break;
379
+ case 13:
380
+ openLtr = true; openVCenter = true;
381
+ break;
382
+ case 14:
383
+ openRtl = true; openVCenter = true;
384
+ break;
385
+ case 15:
386
+ openHCenter = true; openVCenter = true;
387
+ break;
388
+ }
389
+ }
532
390
 
533
- if (openLtr) {
534
- if (alignRight) {
535
- left = `${pageX + offsetRight}px`;
536
- } else if (alignHCenter) {
537
- left = `${pageX + ((offsetLeft + offsetRight) / 2)}px`;
538
- } else {
539
- left = `${pageX + offsetLeft}px`;
540
- }
541
- transformOrigin = 'left';
542
- } else if (openHCenter) {
543
- if (alignLeft) {
544
- left = `${(pageX + offsetLeft) - (popupElement.clientWidth / 2)}px`;
545
- } else if (alignRight) {
546
- left = `${(pageX + offsetRight) - (popupElement.clientWidth / 2)}px`;
547
- } else {
548
- left = `${(pageX + ((offsetLeft + offsetRight) / 2)) - (popupElement.clientWidth / 2)}px`;
549
- }
550
- transformOrigin = 'center';
391
+ if (openLtr) {
392
+ if (alignRight) {
393
+ left = `${pageX + offsetRight}px`;
394
+ } else if (alignHCenter) {
395
+ left = `${pageX + ((offsetLeft + offsetRight) / 2)}px`;
551
396
  } else {
552
- if (alignLeft) {
553
- left = `${(pageX + offsetLeft) - popupElement.clientWidth}px`;
554
- } else if (alignHCenter) {
555
- left = `${(pageX + ((offsetLeft + offsetRight) / 2)) - popupElement.clientWidth}px`;
556
- } else {
557
- left = `${(pageX + offsetRight) - popupElement.clientWidth}px`;
558
- }
559
- transformOrigin = 'right';
397
+ left = `${pageX + offsetLeft}px`;
398
+ }
399
+ transformOrigin = 'left';
400
+ } else if (openHCenter) {
401
+ if (alignLeft) {
402
+ left = `${(pageX + offsetLeft) - (popupElementWidth / 2)}px`;
403
+ } else if (alignRight) {
404
+ left = `${(pageX + offsetRight) - (popupElementWidth / 2)}px`;
405
+ } else {
406
+ left = `${(pageX + ((offsetLeft + offsetRight) / 2)) - (popupElementWidth / 2)}px`;
407
+ }
408
+ transformOrigin = 'center';
409
+ } else {
410
+ if (alignLeft) {
411
+ left = `${(pageX + offsetLeft) - popupElementWidth}px`;
412
+ } else if (alignHCenter) {
413
+ left = `${(pageX + ((offsetLeft + offsetRight) / 2)) - popupElementWidth}px`;
414
+ } else {
415
+ left = `${(pageX + offsetRight) - popupElementWidth}px`;
560
416
  }
417
+ transformOrigin = 'right';
418
+ }
561
419
 
562
- if (openUp) {
563
- if (alignBottom) {
564
- top = `${(pageY + offsetBottom) - popupElement.clientHeight}px`;
565
- } else if (alignVCenter) {
566
- top = `${(pageY + ((offsetTop + offsetBottom) / 2)) - popupElement.clientHeight}px`;
567
- } else {
568
- top = `${(pageY + offsetTop) - popupElement.clientHeight}px`;
569
- }
570
- transformOrigin += ' bottom';
571
- } else if (openVCenter) {
572
- if (alignBottom) {
573
- top = `${(pageY + offsetBottom) - (popupElement.clientHeight / 2)}px`;
574
- } else if (alignTop) {
575
- top = `${(pageY + offsetTop) - (popupElement.clientHeight / 2)}px`;
576
- } else {
577
- top = `${(pageY + ((offsetTop + offsetBottom) / 2)) - (popupElement.clientHeight / 2)}px`;
578
- }
579
- transformOrigin += ' center';
420
+ if (openUp) {
421
+ if (alignBottom) {
422
+ top = `${(pageY + offsetBottom) - popupElement.clientHeight}px`;
423
+ } else if (alignVCenter) {
424
+ top = `${(pageY + ((offsetTop + offsetBottom) / 2)) - popupElement.clientHeight}px`;
580
425
  } else {
581
- if (alignTop) {
582
- top = `${pageY + offsetTop}px`;
583
- } else if (alignVCenter) {
584
- top = `${pageY + ((offsetTop + offsetBottom) / 2)}px`;
585
- } else {
586
- top = `${pageY + offsetBottom}px`;
587
- }
588
- transformOrigin += ' top';
426
+ top = `${(pageY + offsetTop) - popupElement.clientHeight}px`;
427
+ }
428
+ transformOrigin += ' bottom';
429
+ } else if (openVCenter) {
430
+ if (alignBottom) {
431
+ top = `${(pageY + offsetBottom) - (popupElement.clientHeight / 2)}px`;
432
+ } else if (alignTop) {
433
+ top = `${(pageY + offsetTop) - (popupElement.clientHeight / 2)}px`;
434
+ } else {
435
+ top = `${(pageY + ((offsetTop + offsetBottom) / 2)) - (popupElement.clientHeight / 2)}px`;
436
+ }
437
+ transformOrigin += ' center';
438
+ } else {
439
+ if (alignTop) {
440
+ top = `${pageY + offsetTop}px`;
441
+ } else if (alignVCenter) {
442
+ top = `${pageY + ((offsetTop + offsetBottom) / 2)}px`;
443
+ } else {
444
+ top = `${pageY + offsetBottom}px`;
589
445
  }
446
+ transformOrigin += ' top';
447
+ }
590
448
 
591
- popupElement.style.setProperty('top', top);
592
- popupElement.style.setProperty('left', left);
593
- popupElement.style.setProperty('right', 'auto');
594
- popupElement.style.setProperty('bottom', 'auto');
595
- popupElement.style.setProperty('margin', margin);
596
- popupElement.style.setProperty('transform-origin', transformOrigin);
597
- popupElement.style.setProperty('position', 'fixed');
449
+ popupElement.style.setProperty('top', top);
450
+ popupElement.style.setProperty('left', left);
451
+ popupElement.style.setProperty('right', 'auto');
452
+ popupElement.style.setProperty('bottom', 'auto');
453
+ popupElement.style.setProperty('margin', margin);
454
+ popupElement.style.setProperty('transform-origin', transformOrigin);
455
+ }
456
+
457
+ /**
458
+ * @return {void}
459
+ */
460
+ export function onWindowResize() {
461
+ const lastOpenMenu = OPEN_MENUS[OPEN_MENUS.length - 1];
462
+ if (!lastOpenMenu || !lastOpenMenu.originalEvent) {
463
+ return;
464
+ }
465
+ if (lastOpenMenu.pendingResizeOperation) {
466
+ cancelAnimationFrame(lastOpenMenu.pendingResizeOperation);
598
467
  }
468
+ lastOpenMenu.pendingResizeOperation = requestAnimationFrame(() => {
469
+ updateMenuPosition(
470
+ lastOpenMenu.element,
471
+ /** @type {HTMLElement} */ (lastOpenMenu.element.getElementsByClassName('mdw-menu__popup')[0]),
472
+ lastOpenMenu.originalEvent,
473
+ );
474
+ lastOpenMenu.pendingResizeOperation = null;
475
+ });
476
+ }
599
477
 
600
- /**
601
- * @param {Element} menuElement
602
- * @return {void}
603
- */
604
- static refreshMenuItems(menuElement) {
605
- const menuItems = menuElement.getElementsByClassName('mdw-menu__item');
606
- for (let i = 0; i < menuItems.length; i += 1) {
607
- const menuItem = menuItems.item(i);
608
- menuItem.setAttribute('tabindex', '-1');
609
- MenuItem.attach(menuItem);
610
- }
611
- const popupElement = menuElement.getElementsByClassName('mdw-menu__popup')[0];
612
- popupElement.setAttribute('tabindex', '-1');
478
+ /**
479
+ * @param {PopStateEvent} event
480
+ * @return {void}
481
+ */
482
+ export function onPopState(event) {
483
+ if (!event.state) {
484
+ return;
485
+ }
486
+ const lastOpenMenu = OPEN_MENUS[OPEN_MENUS.length - 1];
487
+ if (!lastOpenMenu || !lastOpenMenu.previousState) {
488
+ return;
489
+ }
490
+ if ((lastOpenMenu.previousState === event.state) || Object.keys(event.state)
491
+ .every((key) => event.state[key] === lastOpenMenu.previousState[key])) {
492
+ hide(lastOpenMenu.element);
613
493
  }
494
+ }
614
495
 
615
- /**
616
- * @param {Element} menuElement
617
- * @param {MouseEvent=} event
618
- * @param {boolean=} [alignTarget=true]
619
- * @return {boolean} handled
620
- */
621
- static show(menuElement, event, alignTarget) {
622
- if (event && event.currentTarget instanceof HTMLAnchorElement) {
623
- // Prevent anchor link
624
- event.preventDefault();
625
- }
626
- const popupElement = menuElement.getElementsByClassName('mdw-menu__popup')[0];
627
- let changed = false;
628
- if (event) {
629
- Menu.updateMenuPosition(menuElement, popupElement, event, alignTarget);
630
- } else {
631
- popupElement.style.removeProperty('top');
632
- popupElement.style.removeProperty('left');
633
- popupElement.style.removeProperty('right');
634
- popupElement.style.removeProperty('bottom');
635
- popupElement.style.removeProperty('margin');
636
- popupElement.style.removeProperty('transform-origin');
637
- popupElement.style.removeProperty('position');
638
- if (popupElement.hasAttribute('style') && !popupElement.getAttribute('style')) {
639
- popupElement.removeAttribute('style');
496
+ /**
497
+ * @param {Element} menu
498
+ * @param {boolean} [backwards=false]
499
+ * @return {void}
500
+ */
501
+ export function selectNextMenuItem(menu, backwards) {
502
+ const menuItems = /** @type {HTMLCollectionOf<HTMLElement>} */ (menu.getElementsByClassName('mdw-menu__item'));
503
+ let foundTarget = false;
504
+ /** @type {HTMLElement} */
505
+ let candidate = null;
506
+ /** @type {HTMLElement} */
507
+ let firstFocusableItem = null;
508
+ let lastFocusableElement = null;
509
+ const target = document.activeElement;
510
+
511
+ // Hidden elements cannot be focused
512
+ // Disabled elements cannot be focused on IE11
513
+ // Skip elements that fail to receive focus
514
+ for (const el of menuItems) {
515
+ el.focus();
516
+ const focusable = (document.activeElement === el);
517
+ if (focusable) {
518
+ if (!firstFocusableItem) {
519
+ firstFocusableItem = el;
640
520
  }
521
+ lastFocusableElement = el;
641
522
  }
642
- if (menuElement.hasAttribute('mdw-hide')) {
643
- menuElement.removeAttribute('mdw-hide');
644
- changed = true;
645
- }
646
- if (!menuElement.hasAttribute('mdw-show')) {
647
- menuElement.setAttribute('mdw-show', '');
648
- changed = true;
523
+ if (el === target) {
524
+ foundTarget = true;
525
+ if (backwards && candidate) {
526
+ break;
527
+ }
528
+ continue;
649
529
  }
650
- if (changed) {
651
- Menu.attach(menuElement);
652
- const previousFocus = document.activeElement;
653
- const newState = { hash: Math.random().toString(36).substr(2, 16) };
654
- let previousState = null;
655
- if (window.history && window.history.pushState) {
656
- if (!window.history.state) {
657
- // Create new previous state
658
- window.history.replaceState({
659
- hash: Math.random().toString(36).substr(2, 16),
660
- }, document.title);
661
- }
662
- previousState = window.history.state;
663
- window.history.pushState(newState, document.title);
664
- window.addEventListener('popstate', Menu.onPopState);
530
+ if (backwards) {
531
+ if (focusable) {
532
+ candidate = el;
665
533
  }
666
- const menuStack = new MenuStack(menuElement, previousFocus, newState, previousState);
667
- OPEN_MENUS.push(menuStack);
668
- Menu.refreshMenuItems(menuElement);
669
- if (event && !event.pointerType && !event.detail) {
670
- // Triggered with keyboard event
671
- Menu.selectNextMenuItem(menuElement);
672
- } else {
673
- popupElement.focus();
534
+ continue;
535
+ }
536
+ if (foundTarget) {
537
+ if (focusable) {
538
+ candidate = el;
539
+ break;
674
540
  }
541
+ continue;
675
542
  }
676
- return changed;
677
543
  }
544
+ if (!candidate) {
545
+ candidate = backwards ? lastFocusableElement : firstFocusableItem;
546
+ }
547
+ if (candidate && document.activeElement !== candidate) {
548
+ candidate.focus();
549
+ }
550
+ }
678
551
 
679
- /**
680
- * @param {Element} menuElement
681
- * @return {boolean} handled
682
- */
683
- static hide(menuElement) {
684
- if (!menuElement.hasAttribute('mdw-hide')) {
685
- menuElement.setAttribute('mdw-hide', '');
686
- let stackIndex = -1;
687
- OPEN_MENUS.some((stack, index) => {
688
- if (stack.element === menuElement) {
689
- stackIndex = index;
690
- return true;
691
- }
692
- return false;
693
- });
694
- if (stackIndex !== -1) {
695
- const menuStack = OPEN_MENUS[stackIndex];
696
- if (menuStack.previousFocus) {
697
- menuStack.previousFocus.focus();
698
- }
699
- OPEN_MENUS.splice(stackIndex, 1);
700
- if (menuStack.state && window.history && window.history.state) {
701
- // IE11 returns a cloned state object, not the original
702
- if (menuStack.state.hash === window.history.state.hash) {
703
- window.history.back();
704
- }
705
- }
706
- }
707
- if (!OPEN_MENUS.length) {
708
- window.removeEventListener('popstate', Menu.onPopState);
552
+ /**
553
+ * @param {KeyboardEvent} event
554
+ * @return {void}
555
+ */
556
+ export function onKeyDown(event) {
557
+ const menuElement = /** @type {HTMLElement} */ (event.currentTarget);
558
+ if (!menuElement || menuElement.getAttribute('aria-hidden') === 'true') {
559
+ return;
560
+ }
561
+
562
+ if (event.key === 'ArrowDown' || (event.key === 'Down')) {
563
+ event.stopPropagation();
564
+ event.preventDefault();
565
+ selectNextMenuItem(menuElement, false);
566
+ return;
567
+ }
568
+ if (event.key === 'ArrowUp' || (event.key === 'Up')) {
569
+ event.stopPropagation();
570
+ event.preventDefault();
571
+ selectNextMenuItem(menuElement, true);
572
+ return;
573
+ }
574
+ if (event.key === 'Tab') {
575
+ // Hide menu allowing focus to revert to calling element
576
+ // To then allow browser default Tab interaction
577
+ // Unless menu hiding is cancelled
578
+ if (!hide(menuElement)) {
579
+ event.stopPropagation();
580
+ event.preventDefault();
581
+ }
582
+ return;
583
+ }
584
+ if (event.key === 'Escape' || event.key === 'Esc') {
585
+ event.stopPropagation();
586
+ event.preventDefault();
587
+ hide(menuElement);
588
+ }
589
+ }
590
+
591
+ /**
592
+ * @param {Element} menuElement
593
+ * @return {void}
594
+ */
595
+ export function refreshMenuItems(menuElement) {
596
+ for (const menuItem of menuElement.getElementsByClassName('mdw-menu__item')) {
597
+ menuItem.setAttribute('tabindex', '-1');
598
+ MenuItem.attach(menuItem);
599
+ }
600
+ const popupElement = menuElement.getElementsByClassName('mdw-menu__popup')[0];
601
+ popupElement.setAttribute('tabindex', '-1');
602
+ }
603
+
604
+ /**
605
+ * @param {Element} menuElement
606
+ * @return {void}
607
+ */
608
+ export function attach(menuElement) {
609
+ menuElement.setAttribute('mdw-menu-js', '');
610
+ menuElement.addEventListener('click', onMenuClick);
611
+ menuElement.addEventListener('scroll', onMenuScroll);
612
+ menuElement.addEventListener('touchmove', onMenuScroll);
613
+ menuElement.addEventListener('wheel', onMenuScroll);
614
+ menuElement.addEventListener('keydown', onKeyDown);
615
+ setupARIA(menuElement);
616
+ }
617
+
618
+ /**
619
+ * @param {Element} menuElement
620
+ * @param {MouseEvent|PointerEvent} [event]
621
+ * @param {boolean} [alignTarget=true]
622
+ * @return {boolean} handled
623
+ */
624
+ export function show(menuElement, event, alignTarget) {
625
+ if (event && event.currentTarget instanceof HTMLAnchorElement) {
626
+ // Prevent anchor link
627
+ event.preventDefault();
628
+ }
629
+ const popupElement = /** @type {HTMLElement} */ (menuElement.getElementsByClassName('mdw-menu__popup')[0]);
630
+ let changed = false;
631
+ if (event) {
632
+ updateMenuPosition(menuElement, popupElement, event, alignTarget);
633
+ } else {
634
+ popupElement.style.removeProperty('top');
635
+ popupElement.style.removeProperty('left');
636
+ popupElement.style.removeProperty('right');
637
+ popupElement.style.removeProperty('bottom');
638
+ popupElement.style.removeProperty('margin');
639
+ popupElement.style.removeProperty('transform-origin');
640
+ popupElement.style.removeProperty('position');
641
+ if (popupElement.hasAttribute('style') && !popupElement.getAttribute('style')) {
642
+ popupElement.removeAttribute('style');
643
+ }
644
+ }
645
+ if (menuElement.getAttribute('aria-hidden') !== 'false') {
646
+ menuElement.setAttribute('aria-hidden', 'false');
647
+ changed = true;
648
+ }
649
+ if (changed) {
650
+ attach(menuElement);
651
+ const previousFocus = document.activeElement;
652
+ const newState = { hash: Math.random().toString(36).slice(2, 18) };
653
+ let previousState = null;
654
+ if (window.history && window.history.pushState) {
655
+ if (!window.history.state) {
656
+ // Create new previous state
657
+ window.history.replaceState({
658
+ hash: Math.random().toString(36).slice(2, 18),
659
+ }, document.title);
709
660
  }
710
- dispatchDomEvent(menuElement, 'mdw:dismiss');
711
- return true;
661
+ previousState = window.history.state;
662
+ window.history.pushState(newState, document.title);
663
+ window.addEventListener('popstate', onPopState);
664
+ window.addEventListener('resize', onWindowResize);
665
+ }
666
+ const menuStack = new MenuStack(menuElement, previousFocus, newState, previousState, event);
667
+ OPEN_MENUS.push(menuStack);
668
+ refreshMenuItems(menuElement);
669
+ if (event && !event.detail && ('pointerType' in event) && !event.pointerType) {
670
+ // Triggered with keyboard event
671
+ selectNextMenuItem(menuElement);
672
+ } else {
673
+ popupElement.focus();
712
674
  }
713
- return false;
714
675
  }
676
+ return changed;
715
677
  }
716
678
 
717
- export {
718
- Menu,
719
- MenuItem,
720
- };
679
+ /**
680
+ * @param {Element} menuElement
681
+ * @return {void}
682
+ */
683
+ export function detach(menuElement) {
684
+ hide(menuElement);
685
+ menuElement.removeEventListener('click', onMenuClick);
686
+ menuElement.removeEventListener('scroll', onMenuScroll);
687
+ menuElement.removeEventListener('touchmove', onMenuScroll);
688
+ menuElement.removeEventListener('wheel', onMenuScroll);
689
+ menuElement.addEventListener('keydown', onKeyDown);
690
+ menuElement.removeAttribute('mdw-menu-js');
691
+ const popupElement = /** @type {HTMLElement} */ (menuElement.getElementsByClassName('mdw-menu__popup')[0]);
692
+ if (popupElement) {
693
+ popupElement.style.removeProperty('top');
694
+ popupElement.style.removeProperty('left');
695
+ popupElement.style.removeProperty('right');
696
+ popupElement.style.removeProperty('bottom');
697
+ popupElement.style.removeProperty('margin');
698
+ popupElement.style.removeProperty('transform-origin');
699
+ popupElement.style.removeProperty('position');
700
+ if (popupElement.hasAttribute('style') && !popupElement.getAttribute('style')) {
701
+ popupElement.removeAttribute('style');
702
+ }
703
+ }
704
+ for (const element of menuElement.getElementsByClassName('mdw-menu__item')) { MenuItem.detach(element); }
705
+ }