@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,281 +1,115 @@
1
- import { Button } from '../../components/button/index';
2
- import { DataTable } from '../../components/datatable/index';
3
-
4
- /** @return {HTMLLabelElement} */
5
- function constructTableCheckbox() {
6
- const checkboxLabel = document.createElement('label');
7
- checkboxLabel.classList.add('mdw-selection');
8
- const input = document.createElement('input');
9
- input.classList.add('mdw-selection__input');
10
- input.setAttribute('type', 'checkbox');
11
- const icon = document.createElement('div');
12
- icon.classList.add('mdw-selection__icon');
13
- checkboxLabel.appendChild(input);
14
- checkboxLabel.appendChild(icon);
15
- return checkboxLabel;
16
- }
1
+ import * as Button from '../../components/button/index.js';
2
+ import * as DataTableCell from '../../components/datatable/cell.js';
3
+ import * as DataTableColumnHeader from '../../components/datatable/columnheader.js';
4
+ import * as DataTable from '../../components/datatable/index.js';
5
+ import * as DataTableRow from '../../components/datatable/row.js';
6
+ import * as DataTableRowHeader from '../../components/datatable/rowheader.js';
7
+ import * as Selection from '../../components/selection/index.js';
8
+ import * as RovingTabIndex from '../../core/aria/rovingtabindex.js';
17
9
 
18
- /**
19
- * @callback DataTableAdapterColumnFormatter<T>
20
- * @param {any} value
21
- * @param {T=} object
22
- * @return {any}
23
- * @template T
24
- */
10
+ import DataTableAdapterColumn from './column.js';
25
11
 
26
12
  /**
27
- * @callback DataTableAdapterColumnRenderer<T>
28
- * @param {HTMLTableCellElement} cell,
29
- * @param {any} value
30
- * @param {T} data
31
- * @return {void}
32
- * @template T
13
+ * @template {Record<string, any>} T
14
+ * @template {(keyof T & string)|string} K
15
+ * @typedef {import('./column').DataTableAdapterColumnOptions<T,K>} DataTableAdapterColumnOptions<T,K>
33
16
  */
34
17
 
35
18
  /**
36
19
  * Callback fired when value change is requested
37
20
  * Return truthy value to cancel updating object
21
+ * @template {Record<string, any>} T
22
+ * @template {keyof T & string} K
38
23
  * @callback DataTableAdapterOnValueChangeRequestedCallback<T>
39
24
  * @param {T} object
40
- * @param {string} key
41
- * @param {any} value
25
+ * @param {K} key
26
+ * @param {T[K]} value
42
27
  * @return {boolean} cancel
43
- * @template T
44
28
  */
45
29
 
46
30
  /**
47
31
  * Callback fired when value is changed
32
+ * @template {Record<string, any>} T
33
+ * @template {keyof T & string} K
48
34
  * @callback DataTableAdapterOnValueChangedCallback<T>
49
35
  * @param {T} object
50
- * @param {string} key
51
- * @param {any} value
36
+ * @param {K} key
37
+ * @param {T[K]} value
52
38
  * @return {void}
53
- * @template T
54
39
  */
55
40
 
56
41
  /**
42
+ * @template {Record<string, any>} T
57
43
  * @callback DataTableAdapterFilter<T>
58
44
  * @param {T} element
59
- * @param {number=} index
60
- * @param {T[]=} array
45
+ * @param {number} [index]
46
+ * @param {T[]} array
61
47
  * @return {any}
62
- * @template T
63
48
  */
64
49
 
65
50
  /**
51
+ * @template {Record<string, any>} T
66
52
  * @callback DataTableAdapterSorter<T>
67
53
  * @param {T} a
68
54
  * @param {T} b
69
55
  * @return {number}
70
- * @template T
71
- */
72
-
73
- /**
74
- * Constructor options for DataTableAdapterColumn
75
- * @typedef DataTableAdapterColumnOptions<T>
76
- * @property {string} key
77
- * @property {(string|DocumentFragment)=} name
78
- * @property {string=} type
79
- * @property {boolean=} rowSelector
80
- * @property {string=} tooltip
81
- * @property {boolean=} sortable
82
- * @property {boolean=} primaryColumn
83
- * @property {HTMLElement=} customSortIcon
84
- * @property {string=} innerHTML
85
- * @property {DocumentFragment=} fragment
86
- * @property {DataTableAdapterColumnRenderer<T>=} renderer
87
- * @property {DataTableAdapterColumnFormatter<T>=} formatter
88
- * @template T
89
56
  */
90
57
 
91
58
  /**
59
+ * Constructor options for DataTableAdapter
92
60
  * @template T
61
+ * @typedef DataTableAdapterOptions<T>
62
+ * @prop {HTMLElement} datatable
63
+ * @prop {T[]} datasource Object array
64
+ * @prop {DataTableAdapterFilter<T>} [filter]
65
+ * @prop {DataTableAdapterOnValueChangeRequestedCallback<T,keyof T & string>} [onValueChangeRequested]
66
+ * @prop {DataTableAdapterOnValueChangedCallback<T,keyof T & string>} [onValueChanged]
67
+ * @prop {DataTableAdapterSorter<T>} [sorter]
93
68
  */
94
- class DataTableAdapterColumn {
95
- /** @param {DataTableAdapterColumnOptions<T>} options */
96
- constructor(options) {
97
- this.element = document.createElement('th');
98
- if (options.innerHTML != null) {
99
- this.element.innerHTML = options.innerHTML;
100
- } else if (options.fragment) {
101
- this.element.appendChild(options.fragment);
102
- }
103
- this.key = options.key;
104
- this.element.dataset.key = options.key;
105
-
106
- if (options.sortable) {
107
- this.element.setAttribute('mdw-sortable', '');
108
- let sortIcon = options.customSortIcon;
109
- if (!sortIcon) {
110
- sortIcon = document.createElement('div');
111
- sortIcon.classList.add('mdw-datatable__sort-icon', 'material-icons');
112
- sortIcon.textContent = 'arrow_downward';
113
- }
114
- if (this.element.hasChildNodes()) {
115
- this.element.insertBefore(this.element.firstChild, options.customSortIcon);
116
- } else {
117
- this.element.appendChild(sortIcon);
118
- }
119
- }
120
- if (options.tooltip) {
121
- const wrapper = document.createElement('span');
122
- wrapper.classList.add('mdw-tooltip__wrapper');
123
- const target = document.createElement('span');
124
- target.classList.add('mdw-tooltip__target');
125
- if (!options.name) {
126
- target.textContent = '';
127
- } else if (typeof options.name === 'string') {
128
- target.textContent = options.name;
129
- } else {
130
- target.appendChild(options.name);
131
- }
132
- const tooltip = document.createElement('div');
133
- tooltip.classList.add('mdw-tooltip');
134
- tooltip.textContent = options.tooltip;
135
- wrapper.appendChild(target);
136
- wrapper.appendChild(tooltip);
137
- this.element.appendChild(wrapper);
138
- } else if (options.name) {
139
- let node;
140
- if (typeof options.name === 'string') {
141
- node = document.createTextNode(options.name);
142
- } else {
143
- node = options.name;
144
- }
145
- this.element.appendChild(node);
146
- }
147
-
148
- this.primaryColumn = options.primaryColumn;
149
- if (options.primaryColumn) {
150
- this.element.setAttribute('mdw-primary-column', '');
151
- }
152
-
153
- this.rowSelector = options.rowSelector;
154
-
155
- if (this.rowSelector) {
156
- this.element.setAttribute('mdw-selector', '');
157
- }
158
- if (options.type) {
159
- this.element.dataset.type = options.type;
160
- this.type = options.type;
161
- }
162
- if (this.rowSelector && this.type === 'checkbox') {
163
- const checkboxLabel = constructTableCheckbox();
164
- this.element.appendChild(checkboxLabel);
165
- }
166
-
167
- if (options.renderer) {
168
- this.renderer = options.renderer;
169
- } else if (options.type === 'checkbox') {
170
- this.renderer = DataTableAdapterColumn.defaultCheckboxRenderer;
171
- } else {
172
- this.renderer = DataTableAdapterColumn.defaultCellRenderer;
173
- }
174
- this.formatter = options.formatter || (value => value);
175
- }
176
-
177
- /**
178
- * @param {HTMLTableCellElement} cell
179
- * @param {any} value
180
- * @return {void}
181
- */
182
- static defaultCheckboxRenderer(cell, value) {
183
- let input = cell.getElementsByTagName('input')[0];
184
- const checked = !!value;
185
- if (!input) {
186
- const checkboxLabel = document.createElement('label');
187
- checkboxLabel.classList.add('mdw-selection');
188
- input = document.createElement('input');
189
- input.classList.add('mdw-selection__input');
190
- input.setAttribute('type', 'checkbox');
191
- input.checked = checked;
192
- const icon = document.createElement('div');
193
- icon.classList.add('mdw-selection__icon');
194
- checkboxLabel.appendChild(input);
195
- checkboxLabel.appendChild(icon);
196
- cell.appendChild(checkboxLabel);
197
- }
198
- if (input.checked !== checked) {
199
- input.checked = checked;
200
- }
201
- }
202
-
203
- /**
204
- * @param {HTMLTableCellElement} cell
205
- * @param {any} value
206
- * @return {void}
207
- */
208
- static defaultCellRenderer(cell, value) {
209
- let stringValue;
210
- if (value == null) {
211
- stringValue = '';
212
- } else {
213
- stringValue = value.toString();
214
- }
215
- const len = cell.childNodes.length;
216
- let foundTextNode = false;
217
- for (let i = len - 1; i >= 0; i -= 1) {
218
- const node = cell.childNodes.item(i);
219
- if (!foundTextNode && node.nodeType === Node.TEXT_NODE) {
220
- if (node.nodeValue !== stringValue) {
221
- node.nodeValue = stringValue;
222
- }
223
- foundTextNode = true;
224
- } else {
225
- cell.removeChild(node);
226
- }
227
- }
228
- if (!foundTextNode) {
229
- const node = document.createTextNode(stringValue);
230
- cell.appendChild(node);
231
- }
232
- }
233
- }
234
69
 
235
- /**
236
- * @template T
237
- */
238
- class DataTableAdapter {
239
- /**
240
- * @param {Object} options
241
- * @param {HTMLElement} options.datatable
242
- * @param {T[]} options.datasource Object array
243
- * @param {DataTableAdapterFilter<T>=} options.filter
244
- * @param {DataTableAdapterOnValueChangeRequestedCallback<T>=} options.onValueChangeRequested
245
- * @param {DataTableAdapterOnValueChangedCallback<T>=} options.onValueChanged
246
- * @param {DataTableAdapterSorter<T>=} options.sorter
247
- */
70
+ /** @template {Record<string, any>} T */
71
+ export default class DataTableAdapter {
72
+ /** @param {DataTableAdapterOptions<T>} options */
248
73
  constructor(options) {
249
74
  this.element = options.datatable;
250
75
  this.datasource = options.datasource;
251
76
  this.filter = options.filter;
252
77
  this.sorter = options.sorter;
253
- this.onValueChangeRequested = options.onValueChangeRequested || (() => false);
254
- this.onValueChanged = options.onValueChanged || (() => {});
255
-
256
- this.onElementClickListener = (event) => {
257
- this.handleClickInteraction(event);
258
- };
259
- // Use one click event listener to reduce overhead and allow dynamic content
260
- this.element.addEventListener('click', this.onElementClickListener);
261
-
262
- this.onElementScrollListener = () => {
263
- this.onElementScroll();
264
- };
265
-
266
- /** @type {HTMLTableRowElement} */
267
- this.tabindexRow = null;
268
- /** @type {Object} */
269
- this.tabindexRowData = null;
270
- this.element.addEventListener('mdw:tabindexchanged', (event) => {
271
- if (this.element.hasAttribute('mdw-row-focusable')) {
272
- this.tabindexRow = /** @type {HTMLTableRowElement} */ (event.target);
273
- this.tabindexRowData = this.getDataForTableRow(this.tabindexRow);
274
- }
275
- });
78
+ this.onValueChangeRequested = options.onValueChangeRequested;
79
+ this.onValueChanged = options.onValueChanged;
276
80
  DataTable.attach(this.element);
277
- this.element.setAttribute('mdw-adapter', '');
278
- /** @type {DataTableAdapterColumn<T>[]} */
81
+
82
+ this.onElementScrollListener = () => this.onElementScroll();
83
+
84
+ /**
85
+ * @param {CustomEvent} event
86
+ * @return {void}
87
+ */
88
+ this.onDataTableColumnHeaderSortListener = (event) => this.onDataTableColumnHeaderSort(event);
89
+ /**
90
+ * @param {CustomEvent} event
91
+ * @return {void}
92
+ */
93
+ this.onCheckedChangeEventListener = (event) => this.onCheckedChangeEvent(event);
94
+
95
+ this.element.addEventListener(
96
+ DataTableColumnHeader.SORT_EVENT,
97
+ this.onDataTableColumnHeaderSortListener,
98
+ );
99
+ this.element.addEventListener(
100
+ Selection.CHECKED_CHANGE_EVENT,
101
+ this.onCheckedChangeEventListener,
102
+ );
103
+ this.element.addEventListener(
104
+ DataTableRow.SELECTED_CHANGE_EVENT,
105
+ DataTableAdapter.onRowSelectedChangeEvent,
106
+ );
107
+
108
+ /** @type {HTMLTableSectionElement} */
109
+ this.tbody = null;
110
+ this.scroller = DataTable.getScroller(this.element);
111
+ this.element.setAttribute('mdw-datatable-adapter', '');
112
+ /** @type {DataTableAdapterColumn<T,any>[]} */
279
113
  this.columns = [];
280
114
  this.page = 0;
281
115
  this.pageLimit = 0;
@@ -284,15 +118,28 @@ class DataTableAdapter {
284
118
  this.useLazyRendering = false;
285
119
  }
286
120
 
121
+ /**
122
+ * @param {CustomEvent} event
123
+ * @return {void}
124
+ */
125
+ onDataTableColumnHeaderSort(event) {
126
+ const cell = /** @type {HTMLTableHeaderCellElement} */ (event.target);
127
+ const ascending = event.detail.sort === 'ascending';
128
+ this.updateSortIcons(cell, ascending);
129
+ if (this.updateSortColumn) {
130
+ this.updateSortColumn(cell, ascending);
131
+ }
132
+ }
133
+
287
134
  detach() {
288
- this.element.removeAttribute('mdw-adapter');
135
+ this.element.removeAttribute('mdw-datatable-adapter');
289
136
  this.element = null;
290
137
  this.datasource = null;
291
138
  this.filteredDatasource = null;
292
139
  }
293
140
 
294
141
  /**
295
- * @param {boolean=} [forceRefresh=false]
142
+ * @param {boolean} [forceRefresh=false]
296
143
  * @return {void}
297
144
  */
298
145
  performThrottledRender(forceRefresh = false) {
@@ -304,7 +151,7 @@ class DataTableAdapter {
304
151
  }
305
152
 
306
153
  /**
307
- * @param {boolean=} [forceRefresh=false]
154
+ * @param {boolean} [forceRefresh=false]
308
155
  * @return {void}
309
156
  */
310
157
  scheduleThrottledRender(forceRefresh = false) {
@@ -315,6 +162,19 @@ class DataTableAdapter {
315
162
  }
316
163
  }
317
164
 
165
+ /**
166
+ * @param {CustomEvent} event
167
+ * @return {void}
168
+ */
169
+ static onRowSelectedChangeEvent(event) {
170
+ const row = /** @type {HTMLTableRowElement} */ (event.target);
171
+ const selectionElement = row.querySelector('[mdw-selector] .mdw-selection[aria-checked]');
172
+ if (!selectionElement) {
173
+ return;
174
+ }
175
+ Selection.setChecked(selectionElement, event.detail.value, true);
176
+ }
177
+
318
178
  onElementScroll() {
319
179
  if (this.debounceTimeout) {
320
180
  clearTimeout(this.debounceTimeout);
@@ -336,68 +196,44 @@ class DataTableAdapter {
336
196
  }
337
197
 
338
198
  buildScrollListener() {
339
- this.element.addEventListener('scroll', this.onElementScrollListener);
199
+ this.scroller.addEventListener('scroll', this.onElementScrollListener, { passive: true });
340
200
  }
341
201
 
342
202
  destroyScrollListener() {
343
- this.element.removeEventListener('scroll', this.onElementScrollListener);
203
+ this.scroller.removeEventListener('scroll', this.onElementScrollListener);
344
204
  }
345
205
 
346
206
  /**
347
- * @param {PointerEvent|MouseEvent} event
207
+ * @param {CustomEvent} event
348
208
  * @return {void}
349
209
  */
350
- handleClickInteraction(event) {
351
- const { target } = event;
352
- if (target instanceof HTMLInputElement) {
353
- if (target.hasAttribute('type') && target.getAttribute('type') === 'checkbox') {
354
- event.stopPropagation();
355
- const currentCell = this.getTableCell(target);
356
- if (currentCell.tagName.toLowerCase() === 'th') {
357
- this.setCheckOnAllRows(target.checked, currentCell.cellIndex);
358
- this.setHasSelection(target.checked);
359
- return;
360
- }
361
- const currentRow = this.getTableRow(target);
362
- const object = this.getDataForTableRow(currentRow);
363
- if (this.onValueChangeRequested(object, currentCell.dataset.key, target.checked)) {
364
- event.preventDefault();
365
- return;
366
- }
367
- object[currentCell.dataset.key] = target.checked;
368
- this.onValueChanged(object, currentCell.dataset.key, target.checked);
369
- if (currentCell.hasAttribute('mdw-selector')) {
370
- if (target.checked) {
371
- currentRow.setAttribute('mdw-selected', '');
372
- this.setHasSelection(true);
373
- } else {
374
- currentRow.removeAttribute('mdw-selected');
375
- const selectedRows = this.getSelectedRows();
376
- this.setHasSelection(selectedRows.length !== 0);
377
- }
378
- }
379
- }
210
+ onCheckedChangeEvent(event) {
211
+ const selectionElement = /** @type {HTMLElement} */ (event.target);
212
+ const checked = event.detail.value === 'true';
213
+ const currentCell = this.getTableCell(selectionElement);
214
+ if (currentCell.getAttribute('role') === 'columnheader') {
215
+ this.setCheckOnAllRows(checked, currentCell.cellIndex);
216
+ this.setHasSelection(checked);
380
217
  return;
381
218
  }
382
- const currentCell = this.getTableCell(/** @type {HTMLElement} */ (target));
383
- if (currentCell) {
384
- if (currentCell.tagName.toLowerCase() === 'th' && currentCell.hasAttribute('mdw-sortable')) {
385
- event.stopPropagation();
386
- let ascending = true;
387
- if (currentCell.hasAttribute('mdw-sorted') && !currentCell.getAttribute('mdw-sorted')) {
388
- ascending = false;
389
- }
390
- this.updateSortIcons(currentCell, ascending);
391
- if (this.updateSortColumn) {
392
- this.updateSortColumn(currentCell, ascending);
393
- }
394
- }
219
+ const currentRow = this.getTableRow(selectionElement);
220
+ const object = this.getDataForTableRow(currentRow);
221
+ // eslint-disable-next-line prefer-destructuring
222
+ const key = /** @type {keyof T & string} */ (currentCell.dataset.key);
223
+ if (this.onValueChangeRequested?.(object, key, checked)) {
224
+ event.preventDefault();
225
+ return;
226
+ }
227
+ object[key] = checked;
228
+ this.onValueChanged?.(object, key, checked);
229
+ if (currentCell.hasAttribute('mdw-selector')) {
230
+ DataTableRow.setSelected(currentRow, event.detail.value, true);
395
231
  }
396
232
  }
397
233
 
398
234
  /**
399
235
  * Overridable sorting method
400
- * @param {HTMLTableHeaderCellElement=} tableHeaderCell null if none
236
+ * @param {HTMLTableHeaderCellElement} [tableHeaderCell] null if none
401
237
  * @param {boolean} [ascending=false]
402
238
  * @return {void}
403
239
  */
@@ -414,9 +250,12 @@ class DataTableAdapter {
414
250
  const index = tableHeaderCell.cellIndex;
415
251
  const tableColumn = this.columns[index];
416
252
  const direction = ascending ? 1 : -1;
417
- this.sorter = ((a, b) => {
253
+ this.sorter = ((/** @type {T} */ a, /** @type {T} */ b) => {
418
254
  const valueA = a[tableColumn.key];
419
255
  const valueB = b[tableColumn.key];
256
+ if (tableColumn.sorter) {
257
+ return tableColumn.sorter(a, b) * direction;
258
+ }
420
259
  if (valueA == null) {
421
260
  if (valueB == null) {
422
261
  return 0;
@@ -427,14 +266,15 @@ class DataTableAdapter {
427
266
  return direction;
428
267
  }
429
268
  if (tableColumn.type === 'number') {
430
- return (parseFloat(valueA) - parseFloat(valueB)) * direction;
269
+ return (Number.parseFloat(valueA) - Number.parseFloat(valueB)) * direction;
431
270
  }
432
271
  if (tableColumn.type === 'checkbox') {
433
272
  return ((valueA ? 1 : 0) - (valueB ? 1 : 0)) * direction;
434
273
  }
435
- if (valueA.localeCompare) {
274
+ if ('localeCompare' in valueA) {
436
275
  return valueA.localeCompare(valueB) * direction;
437
276
  }
277
+ // eslint-disable-next-line eqeqeq
438
278
  if (valueA == valueB) {
439
279
  return 0;
440
280
  }
@@ -444,23 +284,21 @@ class DataTableAdapter {
444
284
  }
445
285
 
446
286
  /**
447
- * @param {HTMLTableHeaderCellElement=} sortedTableHeaderCell null if none
448
- * @param {boolean=} [ascending=false]
287
+ * @param {HTMLTableHeaderCellElement} [sortedTableHeaderCell] null if none
288
+ * @param {boolean} [ascending=false]
449
289
  * @return {void}
450
290
  */
451
291
  updateSortIcons(sortedTableHeaderCell, ascending) {
452
292
  if (sortedTableHeaderCell) {
453
293
  if (ascending) {
454
- sortedTableHeaderCell.setAttribute('mdw-sorted', '');
294
+ sortedTableHeaderCell.setAttribute('aria-sort', 'ascending');
455
295
  } else {
456
- sortedTableHeaderCell.setAttribute('mdw-sorted', 'desc');
296
+ sortedTableHeaderCell.setAttribute('aria-sort', 'descending');
457
297
  }
458
298
  }
459
- const tableHeaders = this.element.querySelectorAll('th');
460
- for (let i = 0; i < tableHeaders.length; i += 1) {
461
- const otherTableHeader = tableHeaders.item(i);
462
- if (otherTableHeader !== sortedTableHeaderCell) {
463
- otherTableHeader.removeAttribute('mdw-sorted');
299
+ for (const otherTableHeader of this.getHeaderRow().getElementsByTagName('th')) {
300
+ if (otherTableHeader !== sortedTableHeaderCell && otherTableHeader.hasAttribute('aria-sort')) {
301
+ otherTableHeader.setAttribute('aria-sort', 'none');
464
302
  }
465
303
  }
466
304
  }
@@ -472,9 +310,9 @@ class DataTableAdapter {
472
310
  */
473
311
  setCheckOnAllRows(value, columnIndex) {
474
312
  const column = this.columns[columnIndex];
475
- this.datasource.forEach((object) => {
476
- object[column.key] = value; // eslint-disable-line no-param-reassign
477
- });
313
+ for (const object of this.datasource) {
314
+ object[column.key] = value;
315
+ }
478
316
  this.refresh();
479
317
  }
480
318
 
@@ -514,11 +352,11 @@ class DataTableAdapter {
514
352
 
515
353
  /** @return {T[]} */
516
354
  getSelectedRows() {
517
- const selectorColumn = this.columns.filter(column => column.rowSelector)[0];
355
+ const selectorColumn = this.columns.find((column) => column.rowSelector);
518
356
  if (!selectorColumn) {
519
357
  return [];
520
358
  }
521
- return this.getDatasource().filter(row => row[selectorColumn.key]);
359
+ return this.getDatasource().filter((row) => row[selectorColumn.key]);
522
360
  }
523
361
 
524
362
  /**
@@ -556,21 +394,9 @@ class DataTableAdapter {
556
394
  }
557
395
 
558
396
  /**
559
- * @param {Object} options Text or options
560
- * @param {string} options.key
561
- * @param {(string|DocumentFragment)=} options.name
562
- * @param {boolean=} options.rowSelector
563
- * @param {string=} options.type
564
- * @param {boolean=} options.checkbox
565
- * @param {string=} options.tooltip
566
- * @param {boolean=} options.sortable
567
- * @param {boolean=} options.primaryColumn
568
- * @param {HTMLElement=} options.customSortIcon
569
- * @param {string=} options.innerHTML
570
- * @param {DocumentFragment=} options.fragment
571
- * @param {DataTableAdapterColumnRenderer<T>=} options.renderer
572
- * @param {DataTableAdapterColumnFormatter<T>=} options.formatter
573
- * @return {DataTableAdapterColumn<T>}
397
+ * @template {(keyof T & string)|string} K
398
+ * @param {DataTableAdapterColumnOptions<T,K>} options
399
+ * @return {DataTableAdapterColumn<T,K>}
574
400
  */
575
401
  addColumn(options) {
576
402
  const tableColumn = new DataTableAdapterColumn(options);
@@ -591,23 +417,15 @@ class DataTableAdapter {
591
417
  max = total;
592
418
  }
593
419
  this.paginationDetailsElement.textContent = `${min + 1}-${max} of ${total}`;
594
- if (min === 0) {
595
- this.previousPageButton.setAttribute('disabled', '');
596
- } else {
597
- this.previousPageButton.removeAttribute('disabled');
598
- }
599
- if (max === total) {
600
- this.nextPageButton.setAttribute('disabled', '');
601
- } else {
602
- this.nextPageButton.removeAttribute('disabled');
603
- }
420
+ this.previousPageButton.setAttribute('aria-disabled', (min === 0 ? 'true' : 'false'));
421
+ this.nextPageButton.setAttribute('aria-disabled', (max === total ? 'true' : 'false'));
604
422
  }
605
423
 
606
424
  /**
607
425
  * @param {Object} options
608
- * @param {boolean=} [options.disabled=false]
609
- * @param {number=} [options.limit=10]
610
- * @param {number[]=} [options.limits=[10,25,50,100]]
426
+ * @param {boolean} [options.disabled=false]
427
+ * @param {number} [options.limit=10]
428
+ * @param {number[]} [options.limits=[10,25,50,100]]
611
429
  * @return {void}
612
430
  */
613
431
  setPagination(options = {}) {
@@ -616,12 +434,12 @@ class DataTableAdapter {
616
434
  this.pageLimit = 0;
617
435
  this.page = 0;
618
436
  if (footer) {
619
- footer.style.display = 'none';
437
+ footer.style.setProperty('display', 'none');
620
438
  }
621
439
  this.needsDraw = true;
622
440
  return;
623
441
  }
624
- footer.style.display = '';
442
+ footer.style.removeProperty('display');
625
443
  let optionsElement = footer.getElementsByClassName('mdw-datatable__footer-options')[0];
626
444
  if (!optionsElement) {
627
445
  optionsElement = document.createElement('div');
@@ -634,13 +452,15 @@ class DataTableAdapter {
634
452
  limitsElement.setAttribute('mdw-solo', '');
635
453
  const select = document.createElement('select');
636
454
  select.classList.add('mdw-textfield__input');
637
- limits.forEach((limit) => {
455
+ for (const limit of limits) {
638
456
  const option = document.createElement('option');
639
457
  option.value = limit.toString();
640
458
  option.textContent = limit.toString();
459
+ option.className = 'mdw-theme';
460
+ option.setAttribute('mdw-surface', 'card');
641
461
  select.appendChild(option);
642
- });
643
- select.setAttribute('value', (options.limit && options.limit.toString()) || '10');
462
+ }
463
+ select.value = (options.limit && options.limit.toString()) || '10';
644
464
  const dropdownIcon = document.createElement('div');
645
465
  dropdownIcon.classList.add('mdw-textfield__icon');
646
466
  dropdownIcon.setAttribute('mdw-dropdown', '');
@@ -650,23 +470,23 @@ class DataTableAdapter {
650
470
  optionsElement.appendChild(limitsElement);
651
471
  footer.appendChild(optionsElement);
652
472
  select.addEventListener('input', () => {
653
- this.pageLimit = parseInt(select.value, 10);
473
+ this.pageLimit = Number.parseInt(select.value, 10);
654
474
  this.updateRowCount(false);
655
475
  this.updatePaginator();
656
476
  this.refreshRows();
657
477
  });
658
478
  }
659
- if (!this.paginationDetailsElement) {
660
- this.paginationDetailsElement = footer.getElementsByClassName('mdw-datatable__footer-details')[0];
661
- }
479
+
480
+ this.paginationDetailsElement ||= /** @type {HTMLDivElement} */ (footer.getElementsByClassName('mdw-datatable__footer-details')[0]);
481
+
662
482
  if (!this.paginationDetailsElement) {
663
483
  this.paginationDetailsElement = document.createElement('div');
664
484
  this.paginationDetailsElement.classList.add('mdw-datatable__footer-details');
665
485
  footer.appendChild(this.paginationDetailsElement);
666
486
  }
667
- if (!this.paginationControls) {
668
- this.paginationControls = footer.getElementsByClassName('mdw-datatable__footer-controls')[0];
669
- }
487
+
488
+ this.paginationControls ||= /** @type {HTMLDivElement} */ (footer.getElementsByClassName('mdw-datatable__footer-controls')[0]);
489
+
670
490
  if (!this.paginationControls) {
671
491
  this.paginationControls = document.createElement('div');
672
492
  this.paginationControls.classList.add('mdw-datatable__footer-controls');
@@ -675,15 +495,17 @@ class DataTableAdapter {
675
495
  if (!this.previousPageButton || !this.nextPageButton) {
676
496
  const buttons = this.paginationControls.getElementsByClassName('mdw-button');
677
497
  if (buttons.length !== 2) {
678
- this.previousPageButton = document.createElement('button');
679
- this.previousPageButton.classList.add('mdw-button', 'material-icons');
498
+ this.previousPageButton = document.createElement('a');
499
+ this.previousPageButton.classList.add('mdw-button');
500
+ this.previousPageButton.classList.add('material-icons');
680
501
  this.previousPageButton.setAttribute('mdw-icon', '');
681
502
  this.previousPageButton.textContent = 'chevron_left';
682
503
  this.paginationControls.appendChild(this.previousPageButton);
683
504
  Button.attach(this.previousPageButton);
684
505
 
685
- this.nextPageButton = document.createElement('button');
686
- this.nextPageButton.classList.add('mdw-button', 'material-icons');
506
+ this.nextPageButton = document.createElement('a');
507
+ this.nextPageButton.classList.add('mdw-button');
508
+ this.nextPageButton.classList.add('material-icons');
687
509
  this.nextPageButton.setAttribute('mdw-icon', '');
688
510
  this.nextPageButton.textContent = 'chevron_right';
689
511
  this.paginationControls.appendChild(this.nextPageButton);
@@ -693,7 +515,7 @@ class DataTableAdapter {
693
515
  this.nextPageButton = buttons[1];
694
516
  }
695
517
  this.previousPageButton.addEventListener('click', () => {
696
- if (this.previousPageButton.hasAttribute('disabled')) {
518
+ if (this.previousPageButton.getAttribute('aria-disabled') === 'true') {
697
519
  return;
698
520
  }
699
521
  this.page -= 1;
@@ -702,7 +524,7 @@ class DataTableAdapter {
702
524
  this.refreshRows();
703
525
  });
704
526
  this.nextPageButton.addEventListener('click', () => {
705
- if (this.nextPageButton.hasAttribute('disabled')) {
527
+ if (this.nextPageButton.getAttribute('aria-disabled') === 'true') {
706
528
  return;
707
529
  }
708
530
  this.page += 1;
@@ -722,8 +544,7 @@ class DataTableAdapter {
722
544
  * @return {HTMLElement}
723
545
  */
724
546
  getFooter(create) {
725
- /** @type {HTMLElement} */
726
- let footer = (this.element.getElementsByClassName('mdw-datatable__footer')[0]);
547
+ let footer = /** @type {HTMLElement} */ (this.element.getElementsByClassName('mdw-datatable__footer')[0]);
727
548
  if (!footer && create) {
728
549
  footer = document.createElement('div');
729
550
  footer.classList.add('mdw-datatable__footer');
@@ -734,16 +555,17 @@ class DataTableAdapter {
734
555
 
735
556
  /**
736
557
  * @param {HTMLTableRowElement} el
558
+ * @param {?number} viewportTop
559
+ * @param {?number} viewportBottom
737
560
  * @return {boolean}
738
561
  */
739
- isRowVisible(el) {
740
- const scrollingElement = this.element;
741
- const rowRect = el.getBoundingClientRect();
742
- const scrollingRect = scrollingElement.getBoundingClientRect();
743
- const rowTop = (rowRect.top) - (scrollingRect.top);
744
- const rowBottom = rowTop + rowRect.height;
745
- const viewportTop = 0;
746
- const viewportBottom = scrollingRect.height;
562
+ isRowVisible(
563
+ el,
564
+ viewportTop = this.scroller.scrollTop,
565
+ viewportBottom = this.scroller.scrollTop + this.scroller.offsetHeight,
566
+ ) {
567
+ const rowTop = el.offsetTop;
568
+ const rowBottom = rowTop + el.offsetHeight;
747
569
  if (rowTop > viewportTop && rowTop < viewportBottom) {
748
570
  // Top of row is visible
749
571
  return true;
@@ -761,27 +583,26 @@ class DataTableAdapter {
761
583
  */
762
584
  clearNonvisibleRows(visibleRows) {
763
585
  const tbody = this.getTableBody();
764
- const len = tbody.rows.length;
765
- if (visibleRows.length === len) {
586
+ if (visibleRows.length === tbody.rows.length) {
766
587
  return;
767
588
  }
768
- let firstRowIndex = Infinity;
769
- let lastRowIndex = -Infinity;
589
+ let firstRowIndex = Number.POSITIVE_INFINITY;
590
+ let lastRowIndex = Number.NEGATIVE_INFINITY;
770
591
  if (visibleRows.length) {
771
592
  firstRowIndex = visibleRows[0].sectionRowIndex;
772
593
  lastRowIndex = visibleRows[visibleRows.length - 1].sectionRowIndex;
773
594
  }
774
- for (let i = 0; i < len; i += 1) {
775
- if (i < firstRowIndex || i > lastRowIndex) {
776
- const row = tbody.rows.item(i);
777
- if (row.lastChild) {
778
- // Store row height to prevent layout shifting
779
- const rect = row.getBoundingClientRect();
780
- row.style.setProperty('height', `${rect.height || 0}px`);
781
- }
782
- while (row.lastChild) {
783
- row.removeChild(row.lastChild);
784
- }
595
+ for (let index = 0; index < tbody.rows.length; index++) {
596
+ const row = tbody.rows[index];
597
+ if (index >= firstRowIndex && index <= lastRowIndex) {
598
+ continue;
599
+ }
600
+ if (row.lastChild) {
601
+ // Store row height to prevent layout shifting
602
+ row.style.setProperty('height', `${row.offsetHeight || 0}px`);
603
+ }
604
+ while (row.lastChild) {
605
+ row.removeChild(row.lastChild);
785
606
  }
786
607
  }
787
608
  }
@@ -792,20 +613,24 @@ class DataTableAdapter {
792
613
  getLazyRenderRows() {
793
614
  const tbody = this.getTableBody();
794
615
  const len = tbody.rows.length;
616
+ /** @type {HTMLTableRowElement[]} */
795
617
  const rows = [];
796
618
  const minRowCount = window.screen.height / 48;
797
619
  if (len <= minRowCount) {
798
- for (let i = 0; i < len; i += 1) {
799
- rows.push(tbody.rows.item(i));
620
+ for (const row of tbody.rows) {
621
+ rows.push(row);
800
622
  }
801
623
  return rows;
802
624
  }
803
625
  let foundFirstVisibleRow = false;
804
626
  let startIndex = 0;
805
627
  let endIndex = 0;
806
- for (let i = 0; i < len; i += 1) {
628
+ const viewportTop = this.scroller.scrollTop;
629
+ const viewportBottom = viewportTop + this.scroller.offsetHeight;
630
+
631
+ for (let i = 0; i < tbody.rows.length; i++) {
807
632
  const row = tbody.rows.item(i);
808
- if (this.isRowVisible(row)) {
633
+ if (this.isRowVisible(row, viewportTop, viewportBottom)) {
809
634
  if (!foundFirstVisibleRow) {
810
635
  foundFirstVisibleRow = true;
811
636
  startIndex = i;
@@ -839,11 +664,11 @@ class DataTableAdapter {
839
664
  */
840
665
  performLazyRender(forceRefresh = false) {
841
666
  const visibleRows = this.getLazyRenderRows();
842
- visibleRows.forEach((row) => {
667
+ for (const row of visibleRows) {
843
668
  if (forceRefresh || !row.cells.length) {
844
669
  this.refreshRow(row.sectionRowIndex);
845
670
  }
846
- });
671
+ }
847
672
  this.clearNonvisibleRows(visibleRows);
848
673
  }
849
674
 
@@ -893,7 +718,7 @@ class DataTableAdapter {
893
718
 
894
719
  /**
895
720
  * Update number of rows in table
896
- * @param {boolean=} refresh Refresh new rows
721
+ * @param {boolean} [refresh=false] Refresh new rows
897
722
  * @return {void}
898
723
  */
899
724
  updateRowCount(refresh) {
@@ -930,21 +755,22 @@ class DataTableAdapter {
930
755
  const fragment = document.createDocumentFragment();
931
756
  for (let i = 0; i < rowDifference; i += 1) {
932
757
  const row = document.createElement('tr');
933
- if (this.element.hasAttribute('mdw-row-focusable')) {
934
- row.setAttribute('tabindex', '-1');
935
- }
758
+ DataTableRow.attach(row);
936
759
  newRows.push(row);
937
760
  fragment.appendChild(row);
938
761
  }
762
+ if (this.element.hasAttribute('mdw-row-focusable')) {
763
+ RovingTabIndex.setupTabIndexes(newRows);
764
+ }
939
765
  tbody.appendChild(fragment);
940
766
  }
941
767
  if (refresh && rowDifference !== 0) {
942
768
  if (this.useLazyRendering) {
943
769
  this.scheduleThrottledRender(true);
944
770
  } else {
945
- newRows.forEach((row) => {
771
+ for (const row of newRows) {
946
772
  this.refreshRow(row.sectionRowIndex);
947
- });
773
+ }
948
774
  }
949
775
  }
950
776
  }
@@ -955,26 +781,10 @@ class DataTableAdapter {
955
781
  this.performLazyRender(true);
956
782
  } else {
957
783
  const tbody = this.getTableBody();
958
- const len = tbody.rows.length;
959
- for (let i = 0; i < len; i += 1) {
784
+ for (let i = 0; i < tbody.rows.length; i++) {
960
785
  this.refreshRow(i);
961
786
  }
962
787
  }
963
- if (this.tabindexRowData) {
964
- const row = this.getTableRowForData(this.tabindexRowData);
965
- if (row !== this.tabindexRow) {
966
- if (this.tabindexRow) {
967
- this.tabindexRow.setAttribute('tabindex', '-1');
968
- }
969
- if (row) {
970
- row.setAttribute('tabindex', '0');
971
- if (document.activeElement === this.tabindexRow) {
972
- row.focus();
973
- }
974
- }
975
- this.tabindexRow = row;
976
- }
977
- }
978
788
  if (this.useLazyRendering) {
979
789
  this.scheduleThrottledRender();
980
790
  }
@@ -1017,8 +827,8 @@ class DataTableAdapter {
1017
827
  refreshRow(rowIndex) {
1018
828
  const row = this.getTableBody().rows.item(rowIndex);
1019
829
  row.style.removeProperty('height');
1020
- for (let columnIndex = 0; columnIndex < this.columns.length; columnIndex += 1) {
1021
- this.refreshCell(columnIndex, rowIndex);
830
+ for (let i = 0; i < this.columns.length; i++) {
831
+ this.refreshCell(i, rowIndex);
1022
832
  }
1023
833
  }
1024
834
 
@@ -1030,13 +840,33 @@ class DataTableAdapter {
1030
840
  refreshCell(columnIndex, rowIndex) {
1031
841
  const tableColumn = this.columns[columnIndex];
1032
842
  const row = this.getTableBody().rows.item(rowIndex);
843
+ if (!row) return;
1033
844
  let len = row.cells.length;
845
+ let createdCells = false;
1034
846
  while (len <= columnIndex) {
847
+ createdCells = true;
1035
848
  // Generate cells
1036
849
  const missingColumn = this.columns[len];
1037
- const missingCell = row.insertCell();
1038
- if (missingColumn.type) {
1039
- missingCell.dataset.type = missingColumn.type;
850
+ let missingCell;
851
+ if (missingColumn.rowSelector) {
852
+ missingCell = document.createElement('th');
853
+ row.appendChild(missingCell);
854
+ DataTableRowHeader.attach(missingCell);
855
+ } else {
856
+ missingCell = row.insertCell();
857
+ DataTableCell.attach(missingCell);
858
+ }
859
+ switch (missingColumn.type) {
860
+ case 'checkbox':
861
+ missingCell.setAttribute('mdw-checkbox', '');
862
+ break;
863
+ case 'number':
864
+ missingCell.setAttribute('mdw-number', '');
865
+ break;
866
+ case 'text':
867
+ missingCell.setAttribute('mdw-text', '');
868
+ break;
869
+ default:
1040
870
  }
1041
871
  missingCell.dataset.key = missingColumn.key;
1042
872
  if (missingColumn.rowSelector) {
@@ -1047,35 +877,33 @@ class DataTableAdapter {
1047
877
  }
1048
878
  len += 1;
1049
879
  }
880
+ if (createdCells && this.element.hasAttribute('mdw-cell-focusable')) {
881
+ RovingTabIndex.setupTabIndexes(row.querySelectorAll(DataTable.CELL_TABINDEX_QUERIES.join(',')));
882
+ }
1050
883
  const cell = row.cells.item(columnIndex);
1051
884
  const data = this.getDataForTableRow(row);
1052
885
  const value = data[tableColumn.key];
1053
- if (tableColumn.rowSelector) {
1054
- if (value) {
1055
- if (!row.hasAttribute('mdw-selected')) {
1056
- row.setAttribute('mdw-selected', '');
1057
- }
1058
- } else if (row.hasAttribute('mdw-selected')) {
1059
- row.removeAttribute('mdw-selected');
1060
- }
886
+ if (tableColumn.rowSelector && row.getAttribute('aria-selected') !== (value ? 'true' : 'false')) {
887
+ row.setAttribute('aria-selected', (value ? 'true' : 'false'));
1061
888
  }
1062
889
  const formattedValue = tableColumn.formatter(value, data);
1063
890
  tableColumn.renderer(cell, formattedValue, data);
1064
891
  }
1065
892
 
1066
893
  /**
1067
- * @param {HTMLTableCellElement|DataTableAdapterColumn|number|string} search
1068
- * @return {DataTableAdapterColumn<T>}
894
+ * @template {keyof T & string} K
895
+ * @param {HTMLTableCellElement|DataTableAdapterColumn<T,K>|number|string} search
896
+ * @return {DataTableAdapterColumn<T,K>}
1069
897
  */
1070
898
  getColumn(search) {
1071
899
  if (search instanceof DataTableAdapterColumn) {
1072
900
  return search;
1073
901
  }
1074
902
  if (search instanceof HTMLTableCellElement) {
1075
- return this.columns.filter(column => column.element === search)[0];
903
+ return this.columns.find((column) => column.element === search);
1076
904
  }
1077
905
  if (typeof search === 'string') {
1078
- return this.columns.filter(column => column.element.dataset.key === search)[0];
906
+ return this.columns.find((column) => column.element.dataset.key === search);
1079
907
  }
1080
908
  return this.columns[search];
1081
909
  }
@@ -1113,16 +941,10 @@ class DataTableAdapter {
1113
941
 
1114
942
  /** @return {HTMLTableSectionElement} */
1115
943
  getTableBody() {
1116
- if (this.tbody) {
1117
- return this.tbody;
1118
- }
1119
- this.tbody = this.element.getElementsByTagName('tbody')[0];
1120
- if (!this.tbody) {
1121
- const table = this.getTable();
1122
- this.tbody = document.createElement('tbody');
1123
- table.appendChild(this.tbody);
1124
- }
1125
- return this.tbody;
944
+ // eslint-disable-next-line no-return-assign
945
+ return this.tbody
946
+ ||= this.element.getElementsByTagName('tbody')[0]
947
+ || this.getTable().appendChild(document.createElement('tbody'));
1126
948
  }
1127
949
 
1128
950
  /**
@@ -1131,14 +953,8 @@ class DataTableAdapter {
1131
953
  */
1132
954
  refreshColumn(columnIndex) {
1133
955
  const tbody = this.getTableBody();
1134
- const rowLen = tbody.rows.length;
1135
- for (let rowIndex = 0; rowIndex < rowLen; rowIndex += 1) {
1136
- this.refreshCell(columnIndex, rowIndex);
956
+ for (let i = 0; i < tbody.rows.length; i++) {
957
+ this.refreshCell(columnIndex, i);
1137
958
  }
1138
959
  }
1139
960
  }
1140
-
1141
- export {
1142
- DataTableAdapter,
1143
- DataTableAdapterColumn,
1144
- };