@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,14 +1,18 @@
1
1
  // https://www.w3.org/TR/wai-aria-practices/#dialog_modal
2
2
 
3
- import { Button } from '../button/index';
4
- import { getChildElementByClass, findElementParentByClassName, dispatchDomEvent } from '../common/dom';
3
+ import {
4
+ dispatchDomEvent,
5
+ getChildElementByClass,
6
+ setTextNode,
7
+ } from '../../core/dom.js';
8
+ import * as Button from '../button/index.js';
5
9
 
6
10
  class DialogStack {
7
11
  /**
8
12
  * @param {Element} element
9
- * @param {Element} previousFocus
10
- * @param {Object=} state
11
- * @param {Object=} previousState
13
+ * @param {?Element} [previousFocus]
14
+ * @param {{hash:string}} [state]
15
+ * @param {{hash:string}} [previousState]
12
16
  */
13
17
  constructor(element, previousFocus, state, previousState) {
14
18
  this.element = element;
@@ -20,507 +24,578 @@ class DialogStack {
20
24
 
21
25
  /** @type {DialogStack[]} */
22
26
  const OPEN_DIALOGS = [];
23
- class Dialog {
24
- static get CANCEL_EVENT() {
25
- return 'mdw:dialog-cancel';
26
- }
27
27
 
28
- static get CONFIRM_EVENT() {
29
- return 'mdw:dialog-confirm';
28
+ export const CANCEL_EVENT = 'mdw:dialog-cancel';
29
+ export const CONFIRM_EVENT = 'mdw:dialog-confirm';
30
+ export const CUSTOM_EVENT = 'mdw:dialog-custom';
31
+ export const DISMISS_EVENT = 'mdw:dialog-dismiss';
32
+
33
+ /**
34
+ * @param {TransitionEvent} event
35
+ * @return {void}
36
+ */
37
+ export function onTransitionEnd(event) {
38
+ if (event.propertyName !== 'opacity') {
39
+ return;
30
40
  }
31
-
32
- static get CUSTOM_EVENT() {
33
- return 'mdw:dialog-custom';
41
+ const dialogElement = /** @type {HTMLElement} */ (event.currentTarget);
42
+ if (dialogElement.getAttribute('aria-hidden') !== 'true') {
43
+ return;
44
+ }
45
+ const popupElement = /** @type {HTMLElement} */ (dialogElement.getElementsByClassName('mdw-dialog__popup')[0]);
46
+ if (!popupElement) {
47
+ return;
34
48
  }
49
+ popupElement.style.removeProperty('display');
50
+ }
35
51
 
36
- static get DISMISS_EVENT() {
37
- return 'mdw:dialog-dismiss';
52
+ /**
53
+ * @param {Element} dialogElement
54
+ * @return {void}
55
+ */
56
+ export function setupARIA(dialogElement) {
57
+ if (dialogElement.hasAttribute('mdw-no-aria')) {
58
+ return;
59
+ }
60
+ dialogElement.setAttribute('role', 'dialog');
61
+ dialogElement.setAttribute('aria-modal', 'true');
62
+ if (!dialogElement.hasAttribute('aria-hidden')) {
63
+ dialogElement.setAttribute('aria-hidden', 'true');
64
+ }
65
+ const popupElement = dialogElement.getElementsByClassName('mdw-dialog__popup')[0];
66
+ if (!popupElement) {
67
+ return;
38
68
  }
39
69
 
40
- /**
41
- * @param {Element} element
42
- * @return {void}
43
- */
44
- static attach(element) {
45
- let dialogCloser = getChildElementByClass(element, 'mdw-dialog__close');
46
- if (!dialogCloser) {
47
- dialogCloser = document.createElement('div');
48
- dialogCloser.classList.add('mdw-dialog__close');
49
- if (element.firstChild) {
50
- element.insertBefore(dialogCloser, element.firstChild);
51
- } else {
52
- element.appendChild(dialogCloser);
53
- }
54
- }
55
- dialogCloser.addEventListener('click', Dialog.onCancelClick);
56
- const popup = getChildElementByClass(element, 'mdw-dialog__popup');
57
- popup.addEventListener('keydown', Dialog.onKeyDown);
58
- const buttons = popup.querySelectorAll('.mdw-dialog__button-area .mdw-button');
59
- let foundConfirmButton = false;
60
- let foundCancelButton = false;
61
- for (let i = 0; i < buttons.length; i += 1) {
62
- const button = buttons.item(i);
63
- Button.attach(button);
64
- if (button.hasAttribute('mdw-custom')) {
65
- button.addEventListener('click', Dialog.onCustomButtonClick);
66
- } else if (!foundConfirmButton) {
67
- button.addEventListener('click', Dialog.onConfirmClick);
68
- foundConfirmButton = true;
69
- } else if (!foundCancelButton) {
70
- button.addEventListener('click', Dialog.onCancelClick);
71
- foundCancelButton = true;
72
- } else {
73
- button.addEventListener('click', Dialog.onCustomButtonClick);
74
- }
70
+ if (!popupElement.hasAttribute('aria-label') && !popupElement.hasAttribute('aria-labelledby')) {
71
+ const titleElement = dialogElement.getElementsByClassName('mdw-dialog__title')[0];
72
+ if (titleElement) {
73
+ // titleElement
75
74
  }
76
75
  }
76
+ }
77
77
 
78
- static detach(dialogElement) {
79
- const dialogCloser = getChildElementByClass(dialogElement, 'mdw-dialog__close');
80
- if (dialogCloser) {
81
- dialogCloser.removeEventListener('click', Dialog.onCancelClick);
82
- }
83
- dialogElement.removeAttribute('mdw-js');
84
- dialogElement.removeAttribute('mdw-show');
85
- dialogElement.removeAttribute('mdw-hide');
86
- const popupElement = dialogElement.getElementsByClassName('mdw-dialog__popup')[0];
87
- if (popupElement) {
88
- popupElement.removeEventListener('keydown', Dialog.onKeyDown);
89
- popupElement.style.removeProperty('transform-origin');
90
- if (popupElement.hasAttribute('style') && !popupElement.getAttribute('style')) {
91
- popupElement.removeAttribute('style');
78
+ /**
79
+ * @param {Element} dialogElement
80
+ * @return {boolean} handled
81
+ */
82
+ export function hide(dialogElement) {
83
+ if (dialogElement.getAttribute('aria-hidden') === 'true') {
84
+ return false;
85
+ }
86
+ if (!dispatchDomEvent(dialogElement, DISMISS_EVENT)) {
87
+ return false;
88
+ }
89
+ dialogElement.setAttribute('aria-hidden', 'true');
90
+ // .mdw-dialog__popup hidden by transitionEnd event
91
+ let stackIndex = -1;
92
+ OPEN_DIALOGS.some((stack, index) => {
93
+ if (stack.element === dialogElement) {
94
+ stackIndex = index;
95
+ return true;
96
+ }
97
+ return false;
98
+ });
99
+ if (stackIndex !== -1) {
100
+ const stack = OPEN_DIALOGS[stackIndex];
101
+ if (stack.previousFocus && stack.previousFocus instanceof HTMLElement && document.activeElement?.closest('.mdw-dialog') === dialogElement) {
102
+ // Only pop focus back when hiding a dialog with focus within itself.
103
+ try {
104
+ stack.previousFocus.focus();
105
+ } catch {
106
+ // Failed to focus
92
107
  }
93
108
  }
94
- const buttons = popupElement.querySelectorAll('.mdw-dialog__button-area .mdw-button');
95
- for (let i = 0; i < buttons.length; i += 1) {
96
- const button = buttons.item(i);
97
- Button.detach(button);
98
- button.removeEventListener('click', Dialog.onConfirmClick);
99
- button.removeEventListener('click', Dialog.onCancelClick);
100
- button.removeEventListener('click', Dialog.onCustomButtonClick);
109
+ OPEN_DIALOGS.splice(stackIndex, 1);
110
+ if (stack.state && window.history && window.history.state // IE11 returns a cloned state object, not the original
111
+ && stack.state.hash === window.history.state.hash) {
112
+ window.history.back();
101
113
  }
102
114
  }
115
+ if (!OPEN_DIALOGS.length) {
116
+ // eslint-disable-next-line no-use-before-define
117
+ window.removeEventListener('popstate', onPopState);
118
+ }
119
+ return true;
120
+ }
103
121
 
104
- static onCancelClick(event) {
105
- if (event && event.currentTarget instanceof HTMLAnchorElement) {
106
- event.preventDefault();
107
- }
108
- if (dispatchDomEvent(event.currentTarget, Dialog.CANCEL_EVENT)) {
109
- const dialogElement = findElementParentByClassName(event.currentTarget, 'mdw-dialog');
110
- Dialog.hide(dialogElement);
111
- }
122
+ /**
123
+ * @param {Element} dialogElement
124
+ * @return {void}
125
+ */
126
+ export function cancel(dialogElement) {
127
+ if (dispatchDomEvent(dialogElement, CANCEL_EVENT)) {
128
+ hide(dialogElement);
112
129
  }
130
+ }
113
131
 
114
- static onConfirmClick(event) {
115
- if (event && event.currentTarget instanceof HTMLAnchorElement) {
116
- event.preventDefault();
117
- }
118
- if (dispatchDomEvent(event.currentTarget, Dialog.CONFIRM_EVENT)) {
119
- const dialogElement = findElementParentByClassName(event.currentTarget, 'mdw-dialog');
120
- Dialog.hide(dialogElement);
121
- }
132
+ /**
133
+ * @param {Element} dialogElement
134
+ * @return {void}
135
+ */
136
+ export function confirm(dialogElement) {
137
+ if (dispatchDomEvent(dialogElement, CONFIRM_EVENT)) {
138
+ hide(dialogElement);
122
139
  }
140
+ }
123
141
 
124
- static onCustomButtonClick(event) {
125
- if (event && event.currentTarget instanceof HTMLAnchorElement) {
126
- event.preventDefault();
127
- }
128
- if (dispatchDomEvent(event.currentTarget, Dialog.CUSTOM_EVENT)) {
129
- const dialogElement = findElementParentByClassName(event.currentTarget, 'mdw-dialog');
130
- Dialog.hide(dialogElement);
131
- }
142
+ /**
143
+ * @param {MouseEvent} event
144
+ * @return {void}
145
+ */
146
+ export function onCancelClick(event) {
147
+ if (event && event.currentTarget instanceof HTMLAnchorElement) {
148
+ event.preventDefault();
132
149
  }
150
+ const cancelElement = /** @type {HTMLElement} */ (event.currentTarget);
151
+ const dialogElement = cancelElement.closest('.mdw-dialog');
152
+ cancel(dialogElement);
153
+ }
133
154
 
134
- static onKeyDown(event) {
135
- if (event.key === 'Tab') {
136
- Dialog.handleTabKeyPress(event);
137
- return;
138
- }
139
- if (event.key === 'Escape' || event.key === 'Esc') {
140
- Dialog.onEscapeKeyDown(event);
141
- }
155
+ /**
156
+ * @param {MouseEvent} event
157
+ * @return {void}
158
+ */
159
+ export function onConfirmClick(event) {
160
+ if (event && event.currentTarget instanceof HTMLAnchorElement) {
161
+ event.preventDefault();
142
162
  }
163
+ const button = /** @type {HTMLElement} */ (event.currentTarget);
164
+ const dialogElement = button.closest('.mdw-dialog');
165
+ confirm(dialogElement);
166
+ }
143
167
 
144
- static onEscapeKeyDown(event) {
145
- event.stopPropagation();
168
+ /**
169
+ * @param {MouseEvent} event
170
+ * @return {void}
171
+ */
172
+ export function onCustomButtonClick(event) {
173
+ if (event && event.currentTarget instanceof HTMLAnchorElement) {
146
174
  event.preventDefault();
147
- const dialogElement = findElementParentByClassName(event.currentTarget, 'mdw-dialog');
148
- Dialog.cancel(dialogElement);
149
175
  }
176
+ const button = /** @type {HTMLElement} */ (event.currentTarget);
177
+ if (dispatchDomEvent(button, CUSTOM_EVENT)) {
178
+ const dialogElement = button.closest('.mdw-dialog');
179
+ hide(dialogElement);
180
+ }
181
+ }
150
182
 
151
- /**
152
- * @param {PopStateEvent} event
153
- * @return {void}
154
- */
155
- static onPopState(event) {
156
- if (!event.state) {
157
- return;
158
- }
159
- const lastOpenDialog = OPEN_DIALOGS[OPEN_DIALOGS.length - 1];
160
- if (!lastOpenDialog || !lastOpenDialog.previousState) {
161
- return;
162
- }
163
- if ((lastOpenDialog.previousState === event.state) || Object.keys(event.state)
164
- .every(key => event.state[key] === lastOpenDialog.previousState[key])) {
165
- if (dispatchDomEvent(lastOpenDialog.element, Dialog.CANCEL_EVENT)) {
166
- Dialog.hide(lastOpenDialog.element);
167
- } else {
168
- // Revert pop state by pushing state again
169
- const title = Dialog.getTitleText(lastOpenDialog.element);
170
- window.history.pushState(lastOpenDialog.state, title);
183
+ /**
184
+ * @param {KeyboardEvent} event
185
+ * @return {void}
186
+ */
187
+ export function handleTabKeyPress(event) {
188
+ const popupElement = /** @type {Element} */ (event.currentTarget);
189
+ const focusableElements = popupElement.querySelectorAll([
190
+ 'button:not(:disabled):not([tabindex="-1"])',
191
+ '[href]:not(:disabled):not([tabindex="-1"])',
192
+ 'input:not(:disabled):not([tabindex="-1"])',
193
+ 'select:not(:disabled):not([tabindex="-1"])',
194
+ 'textarea:not(:disabled):not([tabindex="-1"])',
195
+ '[tabindex]:not([tabindex="-1"])'].join(', '));
196
+ let foundTarget = false;
197
+ let candidate = null;
198
+ for (const el of focusableElements) {
199
+ if (el === event.target) {
200
+ foundTarget = true;
201
+ if (event.shiftKey) {
202
+ break;
171
203
  }
204
+ } else if (event.shiftKey) {
205
+ candidate = el;
206
+ } else if (foundTarget) {
207
+ candidate = el;
208
+ break;
172
209
  }
173
210
  }
174
-
175
- /**
176
- * @param {Element} dialogElement
177
- * @return {void}
178
- */
179
- static cancel(dialogElement) {
180
- if (dispatchDomEvent(dialogElement, Dialog.CANCEL_EVENT)) {
181
- Dialog.hide(dialogElement);
211
+ if (!candidate) {
212
+ candidate = event.shiftKey ? focusableElements[focusableElements.length - 1] : focusableElements[0];
213
+ }
214
+ event.stopPropagation();
215
+ event.preventDefault();
216
+ if (candidate && candidate instanceof HTMLElement) {
217
+ try {
218
+ candidate.focus();
219
+ } catch {
220
+ // Failed to focus
182
221
  }
183
222
  }
223
+ }
184
224
 
185
- /**
186
- * @param {Element} dialogElement
187
- * @return {void}
188
- */
189
- static confirm(dialogElement) {
190
- if (dispatchDomEvent(dialogElement, Dialog.CONFIRM_EVENT)) {
191
- Dialog.hide(dialogElement);
225
+ /**
226
+ * @param {KeyboardEvent} event
227
+ * @return {void}
228
+ */
229
+ export function onEscapeKeyDown(event) {
230
+ event.stopPropagation();
231
+ event.preventDefault();
232
+ const popupElement = /** @type {HTMLElement} */ (event.currentTarget);
233
+ const dialogElement = popupElement.closest('.mdw-dialog');
234
+ cancel(dialogElement);
235
+ }
236
+
237
+ /**
238
+ * @param {KeyboardEvent} event
239
+ * @return {void}
240
+ */
241
+ export function onKeyDown(event) {
242
+ if (event.key === 'Tab') {
243
+ handleTabKeyPress(event);
244
+ return;
245
+ }
246
+ if (event.key === 'Escape' || event.key === 'Esc') {
247
+ onEscapeKeyDown(event);
248
+ }
249
+ }
250
+
251
+ /**
252
+ * @param {Element} dialogElement
253
+ * @return {string}
254
+ */
255
+ export function getTitleText(dialogElement) {
256
+ let title = 'Dialog';
257
+ const titleElement = dialogElement.getElementsByClassName('mdw-dialog__title')[0];
258
+ if (titleElement) {
259
+ title = titleElement.textContent ?? '';
260
+ } else {
261
+ const bodyElement = dialogElement.getElementsByClassName('mdw-dialog__body')[0];
262
+ if (bodyElement) {
263
+ title = bodyElement.textContent ?? '';
192
264
  }
193
265
  }
266
+ return title;
267
+ }
194
268
 
195
- /**
196
- * @param {Element} dialogElement
197
- * @return {string}
198
- */
199
- static getTitleText(dialogElement) {
200
- let title = 'Dialog';
201
- const titleElement = dialogElement.getElementsByClassName('mdw-dialog__title')[0];
202
- if (titleElement) {
203
- title = titleElement.textContent;
269
+ /**
270
+ * @param {PopStateEvent} event
271
+ * @return {void}
272
+ */
273
+ export function onPopState(event) {
274
+ if (!event.state) {
275
+ return;
276
+ }
277
+ const lastOpenDialog = OPEN_DIALOGS[OPEN_DIALOGS.length - 1];
278
+ if (!lastOpenDialog || !lastOpenDialog.previousState) {
279
+ return;
280
+ }
281
+ if ((lastOpenDialog.previousState === event.state) || Object.keys(event.state)
282
+ .every((key) => event.state[key] === lastOpenDialog.previousState[key])) {
283
+ if (dispatchDomEvent(lastOpenDialog.element, CANCEL_EVENT)) {
284
+ hide(lastOpenDialog.element);
204
285
  } else {
205
- const bodyElement = dialogElement.getElementsByClassName('mdw-dialog__body')[0];
206
- if (bodyElement) {
207
- title = bodyElement.textContent;
208
- }
286
+ // Revert pop state by pushing state again
287
+ const title = getTitleText(lastOpenDialog.element);
288
+ window.history.pushState(lastOpenDialog.state, title);
209
289
  }
210
- return title;
211
290
  }
291
+ }
212
292
 
213
- /**
214
- * @param {Element} dialogElement
215
- * @return {boolean} handled
216
- */
217
- static hide(dialogElement) {
218
- if (dialogElement.hasAttribute('mdw-hide')) {
219
- return false;
220
- }
221
- if (!dispatchDomEvent(dialogElement, Dialog.DISMISS_EVENT)) {
222
- return false;
223
- }
224
- dialogElement.setAttribute('mdw-hide', '');
225
- let stackIndex = -1;
226
- OPEN_DIALOGS.some((stack, index) => {
227
- if (stack.element === dialogElement) {
228
- stackIndex = index;
229
- return true;
230
- }
231
- return false;
232
- });
233
- if (stackIndex !== -1) {
234
- const stack = OPEN_DIALOGS[stackIndex];
235
- if (stack.previousFocus) {
236
- if (findElementParentByClassName(document.activeElement, 'mdw-dialog') === dialogElement) {
237
- // Only pop focus back when hiding a dialog with focus within itself.
238
- stack.previousFocus.focus();
239
- }
240
- }
241
- OPEN_DIALOGS.splice(stackIndex, 1);
242
- if (stack.state && window.history && window.history.state) {
243
- // IE11 returns a cloned state object, not the original
244
- if (stack.state.hash === window.history.state.hash) {
245
- window.history.back();
246
- }
247
- }
248
- }
249
- if (!OPEN_DIALOGS.length) {
250
- window.removeEventListener('popstate', Dialog.onPopState);
293
+ /**
294
+ * @param {Element} dialogElement
295
+ * @param {(DocumentFragment|string|null)} [content]
296
+ * @return {void}
297
+ */
298
+ export function updateTitle(dialogElement, content) {
299
+ let title = dialogElement.getElementsByClassName('mdw-dialog__title')[0];
300
+ if (!content) {
301
+ if (!title) {
302
+ // Nothing to be done
303
+ return;
251
304
  }
252
- return true;
305
+ title.remove();
306
+ return;
253
307
  }
254
-
255
- /**
256
- * @param {Element} dialogElement
257
- * @param {(MouseEvent|KeyboardEvent|PointerEvent)=} event
258
- * @return {boolean} handled
259
- */
260
- static show(dialogElement, event) {
261
- if (event && event.currentTarget instanceof HTMLAnchorElement) {
262
- // Prevent anchor link
263
- event.preventDefault();
308
+ if (!title) {
309
+ title = document.createElement('div');
310
+ title.className = 'mdw-dialog__title';
311
+ const popup = dialogElement.getElementsByClassName('mdw-dialog__popup')[0];
312
+ popup.prepend(title);
313
+ }
314
+ if (content instanceof DocumentFragment) {
315
+ while (title.lastChild) {
316
+ title.removeChild(title.lastChild);
264
317
  }
265
- let changed = false;
318
+ title.appendChild(content);
319
+ } else {
320
+ title.textContent = content;
321
+ }
322
+ }
266
323
 
267
- Dialog.updateTransformOrigin(dialogElement, event);
268
- if (dialogElement.hasAttribute('mdw-hide')) {
269
- dialogElement.removeAttribute('mdw-hide');
270
- changed = true;
271
- }
272
- if (!dialogElement.hasAttribute('mdw-show')) {
273
- dialogElement.setAttribute('mdw-show', '');
274
- changed = true;
275
- }
276
- if (changed) {
277
- Dialog.attach(dialogElement);
278
- const previousFocus = document.activeElement;
279
- const newState = { hash: Math.random().toString(36).substr(2, 16) };
280
- let previousState = null;
281
- if (window.history && window.history.pushState) {
282
- if (!window.history.state) {
283
- // Create new previous state
284
- window.history.replaceState({
285
- hash: Math.random().toString(36).substr(2, 16),
286
- }, document.title);
287
- }
288
- previousState = window.history.state;
289
- const title = Dialog.getTitleText(dialogElement);
290
- window.history.pushState(newState, title);
291
- window.addEventListener('popstate', Dialog.onPopState);
292
- }
293
- const dialogStack = new DialogStack(dialogElement, previousFocus, newState, previousState);
294
- OPEN_DIALOGS.push(dialogStack);
295
- const focusElement = dialogElement.querySelector('[mdw-autofocus]');
296
- if (focusElement) {
297
- focusElement.focus();
298
- } else {
299
- dialogElement.focus();
300
- }
301
- }
302
- return changed;
324
+ /**
325
+ * @param {Element} dialogElement
326
+ * @param {string[]} texts
327
+ * @return {void}
328
+ */
329
+ export function updateButtonText(dialogElement, texts) {
330
+ const buttons = dialogElement.querySelectorAll('.mdw-dialog__button-area .mdw-button');
331
+ for (let i = 0; i < buttons.length; i++) {
332
+ setTextNode(buttons.item(i), texts[i]);
303
333
  }
334
+ }
304
335
 
305
- /**
306
- * @param {KeyboardEvent} event
307
- * @return {void}
308
- */
309
- static handleTabKeyPress(event) {
310
- const popupElement = event.currentTarget;
311
- const focusableElements = popupElement.querySelectorAll([
312
- 'button:not(:disabled):not([tabindex="-1"])',
313
- '[href]:not(:disabled):not([tabindex="-1"])',
314
- 'input:not(:disabled):not([tabindex="-1"])',
315
- 'select:not(:disabled):not([tabindex="-1"])',
316
- 'textarea:not(:disabled):not([tabindex="-1"])',
317
- '[tabindex]:not([tabindex="-1"])'].join(', '));
318
- let foundTarget = false;
319
- let candidate = null;
320
- for (let i = 0; i < focusableElements.length; i += 1) {
321
- const el = focusableElements.item(i);
322
- if (el === event.target) {
323
- foundTarget = true;
324
- if (event.shiftKey) {
325
- break;
326
- }
327
- } else if (event.shiftKey) {
328
- candidate = el;
329
- } else if (foundTarget) {
330
- candidate = el;
331
- break;
332
- }
336
+ /**
337
+ * @param {Element} dialogElement
338
+ * @param {(DocumentFragment|string)} [content]
339
+ * @return {void}
340
+ */
341
+ export function updateBody(dialogElement, content) {
342
+ let body = dialogElement.getElementsByClassName('mdw-dialog__body')[0];
343
+ if (!content) {
344
+ if (!body) {
345
+ // Nothing to be done
346
+ return;
333
347
  }
334
- if (!candidate) {
335
- if (event.shiftKey) {
336
- // Select last item
337
- candidate = focusableElements[focusableElements.length - 1];
338
- } else {
339
- // Select first item
340
- candidate = focusableElements[0];
341
- }
348
+ body.remove();
349
+ return;
350
+ }
351
+ if (!body) {
352
+ body = document.createElement('div');
353
+ body.className = 'mdw-dialog__body mdw-theme';
354
+ body.setAttribute('mdw-ink', 'medium');
355
+ const title = dialogElement.getElementsByClassName('mdw-dialog__title')[0];
356
+ if (title) {
357
+ title.after(body);
358
+ } else {
359
+ const popup = dialogElement.getElementsByClassName('mdw-dialog__popup')[0];
360
+ popup.prepend(body);
342
361
  }
343
- event.stopPropagation();
344
- event.preventDefault();
345
- if (candidate) {
346
- candidate.focus();
362
+ }
363
+ if (content instanceof DocumentFragment) {
364
+ while (body.lastChild) {
365
+ body.removeChild(body.lastChild);
347
366
  }
367
+ body.appendChild(content);
368
+ } else {
369
+ body.textContent = content;
348
370
  }
371
+ }
349
372
 
350
- /**
351
- * @param {Element} dialogElement
352
- * @param {(DocumentFragment|string)=} content
353
- * @return {void}
354
- */
355
- static updateTitle(dialogElement, content) {
356
- let title = dialogElement.getElementsByClassName('mdw-dialog__title')[0];
357
- if (!content) {
358
- if (!title) {
359
- // Nothing to be done
360
- return;
361
- }
362
- title.parentElement.removeChild(title);
373
+ /**
374
+ * @param {Element} dialogElement
375
+ * @param {Event|MouseEvent} [event]
376
+ * @return {void}
377
+ */
378
+ export function updateTransformOrigin(dialogElement, event) {
379
+ const popup = /** @type {HTMLElement} */ (dialogElement.getElementsByClassName('mdw-dialog__popup')[0]);
380
+ popup.style.removeProperty('transform-origin');
381
+ if (!event) {
382
+ return;
383
+ }
384
+ const hadLayer = popup.style.getPropertyValue('display') != null;
385
+ if (!hadLayer) {
386
+ popup.style.setProperty('display', 'flex');
387
+ }
388
+ let pageX;
389
+ let pageY;
390
+ const dialogRect = dialogElement.getBoundingClientRect();
391
+ if ('pageX' in event && 'pageY' in event) {
392
+ pageX = event.pageX - window.pageXOffset;
393
+ pageY = event.pageY - window.pageYOffset;
394
+ } else {
395
+ const target = (event.target || event.currentTarget);
396
+ if (!(target instanceof Element)) {
363
397
  return;
364
398
  }
365
- if (!title) {
366
- title = document.createElement('div');
367
- title.classList.add('mdw-dialog__title');
368
- const popup = dialogElement.getElementsByClassName('mdw-dialog__popup')[0];
369
- popup.insertBefore(title, popup.firstChild);
370
- }
371
- if (content instanceof DocumentFragment) {
372
- while (title.lastChild) {
373
- title.removeChild(title.lastChild);
374
- }
375
- title.appendChild(content);
376
- } else {
377
- title.textContent = content;
399
+ const rect = target.getBoundingClientRect();
400
+ if (!rect.width && !rect.width && !rect.left && !rect.top) {
401
+ return;
378
402
  }
403
+ pageX = rect.left + (rect.width / 2);
404
+ pageY = rect.top + (rect.height / 2);
379
405
  }
406
+ const transformOriginX = pageX - dialogRect.left - popup.offsetLeft;
407
+ const transformOriginY = pageY - dialogRect.top - popup.offsetTop;
408
+ popup.style.setProperty('transform-origin', `${transformOriginX}px ${transformOriginY}px`);
409
+ if (!hadLayer) {
410
+ popup.style.removeProperty('display');
411
+ }
412
+ }
380
413
 
381
-
382
- /**
383
- * @param {Element} dialogElement
384
- * @param {string[]} texts
385
- * @return {void}
386
- */
387
- static updateButtonText(dialogElement, texts) {
388
- const buttonElements = dialogElement.querySelectorAll('.mdw-dialog__button-area .mdw-button');
389
- for (let i = 0; i < buttonElements.length; i += 1) {
390
- const button = buttonElements.item(i);
391
- button.textContent = texts[i];
414
+ /**
415
+ * @param {Element} element
416
+ * @return {void}
417
+ */
418
+ export function attach(element) {
419
+ element.setAttribute('mdw-dialog-js', '');
420
+ element.addEventListener('transitionend', onTransitionEnd);
421
+
422
+ let dialogCloser = getChildElementByClass(element, 'mdw-dialog__close');
423
+ if (!dialogCloser) {
424
+ dialogCloser = document.createElement('div');
425
+ dialogCloser.className = 'mdw-dialog__close';
426
+ if (element.firstChild) {
427
+ element.prepend(dialogCloser);
428
+ } else {
429
+ element.appendChild(dialogCloser);
392
430
  }
393
431
  }
432
+ dialogCloser.addEventListener('click', onCancelClick);
433
+ const popup = getChildElementByClass(element, 'mdw-dialog__popup');
434
+ popup.addEventListener('keydown', onKeyDown);
435
+ const buttons = popup.querySelectorAll('.mdw-dialog__button-area .mdw-button');
436
+ let foundConfirmButton = false;
437
+ let foundCancelButton = false;
438
+ for (const button of buttons) {
439
+ Button.attach(button);
440
+ if (button.hasAttribute('mdw-custom')) {
441
+ button.addEventListener('click', onCustomButtonClick);
442
+ continue;
443
+ }
444
+ if (!foundConfirmButton) {
445
+ button.addEventListener('click', onConfirmClick);
446
+ foundConfirmButton = true;
447
+ continue;
448
+ }
449
+ if (!foundCancelButton) {
450
+ button.addEventListener('click', onCancelClick);
451
+ foundCancelButton = true;
452
+ continue;
453
+ }
454
+ button.addEventListener('click', onCustomButtonClick);
455
+ }
456
+ setupARIA(element);
457
+ }
394
458
 
395
- /**
396
- * @param {Element} dialogElement
397
- * @param {(DocumentFragment|string)=} content
398
- * @return {void}
399
- */
400
- static updateBody(dialogElement, content) {
401
- let body = dialogElement.getElementsByClassName('mdw-dialog__body')[0];
402
- if (!content) {
403
- if (!body) {
404
- // Nothing to be done
405
- return;
459
+ /**
460
+ * @param {Object} options
461
+ * @param {(DocumentFragment|string)} [options.title]
462
+ * @param {(DocumentFragment|string)} [options.body]
463
+ * @param {string[]} [options.buttons]
464
+ * @param {boolean} [options.stacked=false]
465
+ * @param {Element} [options.parent]
466
+ * @param {boolean} [options.custom=false] Use custom button events
467
+ * @param {(string|number)} [options.autofocus=0] Autofocus button by index or text
468
+ * @return {Element}
469
+ */
470
+ export function create(options) {
471
+ const element = document.createElement('div');
472
+ element.className = 'mdw-dialog';
473
+
474
+ const popup = document.createElement('div');
475
+ popup.className = 'mdw-dialog__popup mdw-theme';
476
+ popup.setAttribute('mdw-surface', 'card');
477
+ element.appendChild(popup);
478
+
479
+ updateTitle(element, options.title);
480
+ updateBody(element, options.body);
481
+
482
+ if (options.buttons) {
483
+ const buttonArea = document.createElement('div');
484
+ buttonArea.className = 'mdw-dialog__button-area';
485
+ let index = 0;
486
+ for (const buttonText of options.buttons) {
487
+ const button = document.createElement('div');
488
+ button.className = 'mdw-button mdw-theme';
489
+ button.setAttribute('tabindex', '0');
490
+ button.setAttribute('mdw-ink', 'secondary');
491
+ if (options.custom) {
492
+ button.setAttribute('mdw-custom', '');
406
493
  }
407
- body.parentElement.removeChild(body);
408
- return;
409
- }
410
- if (!body) {
411
- body = document.createElement('div');
412
- body.classList.add('mdw-dialog__body');
413
- const title = dialogElement.getElementsByClassName('mdw-dialog__title')[0];
414
- if (title) {
415
- title.insertAdjacentElement('afterend', body);
416
- } else {
417
- const popup = dialogElement.getElementsByClassName('mdw-dialog__popup')[0];
418
- popup.insertBefore(body, popup.firstChild);
494
+ button.textContent = buttonText;
495
+ buttonArea.appendChild(button);
496
+ if ((options.autofocus == null && index === 0)
497
+ || options.autofocus === index || options.autofocus === buttonText) {
498
+ button.setAttribute('mdw-autofocus', '');
419
499
  }
500
+ index += 1;
420
501
  }
421
- if (content instanceof DocumentFragment) {
422
- while (body.lastChild) {
423
- body.removeChild(body.lastChild);
424
- }
425
- body.appendChild(content);
426
- } else {
427
- body.textContent = content;
502
+ if (options.stacked) {
503
+ buttonArea.setAttribute('mdw-stacked', '');
428
504
  }
505
+ popup.appendChild(buttonArea);
429
506
  }
507
+ if (options.parent) {
508
+ options.parent.appendChild(element);
509
+ }
510
+ attach(element);
511
+ return element;
512
+ }
430
513
 
431
- /**
432
- * @param {Object} options
433
- * @param {(DocumentFragment|string)=} options.title
434
- * @param {(DocumentFragment|string)=} options.body
435
- * @param {string[]=} options.buttons
436
- * @param {boolean=} options.stacked
437
- * @param {Element=} options.parent
438
- * @param {boolean} [options.custom=false] Use custom button events
439
- * @param {(string|number)=} [options.autofocus=0] Autofocus button by index or text
440
- * @return {Element}
441
- */
442
- static create(options) {
443
- const element = document.createElement('div');
444
- element.classList.add('mdw-dialog');
445
-
446
- const popup = document.createElement('div');
447
- popup.classList.add('mdw-dialog__popup');
448
- element.appendChild(popup);
449
-
450
- Dialog.updateTitle(element, options.title);
451
- Dialog.updateBody(element, options.body);
452
-
453
- if (options.buttons) {
454
- const buttonArea = document.createElement('div');
455
- buttonArea.classList.add('mdw-dialog__button-area');
456
- let index = 0;
457
- options.buttons.forEach((buttonText) => {
458
- const button = document.createElement('div');
459
- button.classList.add('mdw-button');
460
- button.setAttribute('tabindex', '0');
461
- button.setAttribute('mdw-theme-color', 'accent');
462
- if (options.custom) {
463
- button.setAttribute('mdw-custom', '');
464
- }
465
- button.textContent = buttonText;
466
- buttonArea.appendChild(button);
467
- if ((options.autofocus == null && index === 0)
468
- || options.autofocus === index || options.autofocus === buttonText) {
469
- button.setAttribute('mdw-autofocus', '');
470
- }
471
- index += 1;
472
- });
473
- if (options.stacked) {
474
- buttonArea.setAttribute('mdw-stacked', '');
514
+ /**
515
+ * @param {Element} dialogElement
516
+ * @param {Event} [event]
517
+ * @return {boolean} handled
518
+ */
519
+ export function show(dialogElement, event) {
520
+ if (event && event.type === 'click' && event.currentTarget instanceof HTMLAnchorElement) {
521
+ // Prevent anchor link
522
+ event.preventDefault();
523
+ }
524
+ let changed = false;
525
+
526
+ if (dialogElement.getAttribute('aria-hidden') !== 'false') {
527
+ dialogElement.setAttribute('aria-hidden', 'false');
528
+ const popupElement = /** @type {HTMLElement} */ (dialogElement.getElementsByClassName('mdw-dialog__popup')[0]);
529
+ popupElement.style.setProperty('display', 'flex');
530
+ changed = true;
531
+ }
532
+
533
+ updateTransformOrigin(dialogElement, event);
534
+
535
+ if (!changed) {
536
+ return false;
537
+ }
538
+ attach(dialogElement);
539
+ const previousFocus = document.activeElement;
540
+ const newState = { hash: Math.random().toString(36).slice(2, 18) };
541
+ let previousState = null;
542
+ if (window.history && window.history.pushState) {
543
+ if (!window.history.state) {
544
+ // Create new previous state
545
+ window.history.replaceState({
546
+ hash: Math.random().toString(36).slice(2, 18),
547
+ }, document.title);
548
+ }
549
+ previousState = window.history.state;
550
+ const title = getTitleText(dialogElement);
551
+ window.history.pushState(newState, title);
552
+ window.addEventListener('popstate', onPopState);
553
+ }
554
+ const dialogStack = new DialogStack(dialogElement, previousFocus, newState, previousState);
555
+ OPEN_DIALOGS.push(dialogStack);
556
+ const focusElement = dialogElement.querySelector('[mdw-autofocus]');
557
+ try {
558
+ if (focusElement && focusElement instanceof HTMLElement) {
559
+ if (focusElement.scrollIntoView) {
560
+ focusElement.scrollIntoView();
475
561
  }
476
- popup.appendChild(buttonArea);
562
+ focusElement.focus();
563
+ } else if (dialogElement instanceof HTMLElement) {
564
+ dialogElement.focus();
477
565
  }
478
- if (options.parent) {
479
- options.parent.appendChild(element);
480
- }
481
- Dialog.attach(element);
482
- return element;
566
+ } catch {
567
+ // Failed to focus
483
568
  }
569
+ return true;
570
+ }
484
571
 
485
- /**
486
- * @param {Element} dialogElement
487
- * @param {(MouseEvent|KeyboardEvent|PointerEvent)=} event
488
- * @return {void}
489
- */
490
- static updateTransformOrigin(dialogElement, event) {
491
- /** @type {HTMLElement} */
492
- const popup = getChildElementByClass(dialogElement, 'mdw-dialog__popup');
493
- popup.style.removeProperty('transform-origin');
494
- if (!event) {
495
- return;
496
- }
497
- let popupPageX = 0;
498
- let popupPageY = 0;
499
- let element = popup;
500
- while (element != null) {
501
- popupPageX += element.offsetLeft - element.scrollLeft;
502
- popupPageY += element.offsetTop - element.scrollTop;
503
- element = element.offsetParent;
504
- }
505
- let { pageX, pageY } = event;
506
- if (!pageX && !pageY) {
507
- const target = event.currentTarget || event.target;
508
- const rect = target.getBoundingClientRect();
509
- if (!rect.width && !rect.width && !rect.x && !rect.y) {
510
- return;
511
- }
512
- pageX = rect.x + (rect.width / 2);
513
- pageY = rect.y + (rect.height / 2);
572
+ /**
573
+ * @param {Element} dialogElement
574
+ * @return {void}
575
+ */
576
+ export function detach(dialogElement) {
577
+ dialogElement.removeEventListener('transitionend', onTransitionEnd);
578
+ dialogElement.removeAttribute('mdw-dialog-js');
579
+ const dialogCloser = getChildElementByClass(dialogElement, 'mdw-dialog__close');
580
+ if (dialogCloser) {
581
+ dialogCloser.removeEventListener('click', onCancelClick);
582
+ }
583
+ const popupElement = /** @type {HTMLElement} */ (dialogElement.getElementsByClassName('mdw-dialog__popup')[0]);
584
+ if (popupElement) {
585
+ popupElement.removeEventListener('keydown', onKeyDown);
586
+ popupElement.style.removeProperty('transform-origin');
587
+ if (popupElement.hasAttribute('style') && !popupElement.getAttribute('style')) {
588
+ popupElement.removeAttribute('style');
514
589
  }
515
- const transformOriginX = `${pageX - popupPageX}px`;
516
- const transformOriginY = `${pageY - popupPageY}px`;
517
- popup.style.setProperty('transform-origin', `${transformOriginX} ${transformOriginY}`);
590
+ }
591
+ const buttons = popupElement.querySelectorAll('.mdw-dialog__button-area .mdw-button');
592
+ for (const button of buttons) {
593
+ Button.detach(button);
594
+ button.removeEventListener('click', onConfirmClick);
595
+ button.removeEventListener('click', onCancelClick);
596
+ button.removeEventListener('click', onCustomButtonClick);
518
597
  }
519
598
  }
520
599
 
521
600
  // Aliases
522
- Dialog.dismiss = Dialog.hide;
523
-
524
- export {
525
- Dialog,
526
- };
601
+ export const dismiss = hide;