@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,16 +1,29 @@
1
- import { ListItem } from '../../components/list/index';
1
+ import * as ListItem from '../../components/list/item.js';
2
+ import DomAdapter from '../dom/index.js';
3
+
4
+ /**
5
+ * @template T1
6
+ * @template T2
7
+ * @typedef {import('../dom/index').DomAdapterCreateOptions<T1,T2>} DomAdapterCreateOptions<T1,T2>
8
+ */
2
9
 
3
10
  /**
4
11
  * @param {HTMLLIElement} element
5
12
  * @param {any} data
6
13
  * @return {void}
7
14
  */
8
- function AnyListAdapterRendererFn(element, data) {
15
+ function AnyListAdapterRenderer(element, data) {
9
16
  let primaryText = element.getElementsByClassName('mdw-list__text')[0];
10
17
  if (!primaryText) {
18
+ let contentElement = element.getElementsByClassName('mdw-list__content')[0];
19
+ if (!contentElement) {
20
+ contentElement = document.createElement('div');
21
+ contentElement.className = 'mdw-list__content';
22
+ element.appendChild(contentElement);
23
+ }
11
24
  primaryText = document.createElement('div');
12
- primaryText.classList.add('mdw-list__text');
13
- element.appendChild(primaryText);
25
+ primaryText.className = 'mdw-list__text';
26
+ contentElement.appendChild(primaryText);
14
27
  ListItem.attach(element);
15
28
  }
16
29
  let s = '';
@@ -28,119 +41,29 @@ function AnyListAdapterRendererFn(element, data) {
28
41
 
29
42
  /**
30
43
  * @template T
44
+ * @typedef {Object} ListAdapterCreateOptions
45
+ * @prop {HTMLElement} element
46
+ * @prop {Array<T>} datasource
47
+ * @prop {function(HTMLLIElement, T):void} [render={function(HTMLIElement,T):void}]
31
48
  */
32
- class ListAdapter {
33
- /**
34
- * @param {HTMLElement} element
35
- * @param {Array<T>} datasource
36
- * @param {function(HTMLLIElement, T)=} renderFn
37
- */
38
- constructor(element, datasource, renderFn) {
39
- this.element = element;
40
- this.datasource = datasource;
41
- /** @type {Map<T, HTMLLIElement>} */
42
- this.map = new Map();
43
- this.render = renderFn || AnyListAdapterRendererFn;
44
- }
45
-
46
- /** @return {void} */
47
- refresh() {
48
- const unlinkedDataItems = [];
49
- this.map.forEach((element, data) => {
50
- if (this.datasource.indexOf(data) === -1) {
51
- unlinkedDataItems.push(data);
52
- }
53
- });
54
- unlinkedDataItems.forEach(data => () => {
55
- this.removeItem(data);
56
- });
57
- this.datasource.forEach((data) => {
58
- this.refreshItem(data);
59
- });
60
- }
61
-
62
- /** @return {void} */
63
- clear() {
64
- this.map.clear();
65
- while (this.element.lastChild) {
66
- this.element.removeChild(this.element.lastChild);
67
- }
68
- }
69
49
 
70
- /**
71
- * @param {T} data
72
- * @param {boolean} [checkPosition=true]
73
- * @return {void}
74
- */
75
- refreshItem(data, checkPosition = true) {
76
- const index = this.datasource.indexOf(data);
77
- if (index === -1) {
78
- this.removeItem(data);
79
- return;
80
- }
81
- let elementIndex = -1;
82
- let element = this.map.get(data);
83
- if (!element) {
84
- element = document.createElement('li');
85
- element.classList.add('mdw-list__item');
86
- this.map.set(data, element);
87
- } else if (checkPosition) {
88
- elementIndex = index;
89
- } else {
90
- let sibling = this.element.firstChild;
91
- let siblingIndex = -1;
92
- while (sibling) {
93
- siblingIndex += 1;
94
- if (sibling === element) {
95
- elementIndex = siblingIndex;
96
- break;
97
- }
98
- sibling = sibling.nextSibling;
99
- }
100
- }
101
- this.render(element, data);
102
- if (elementIndex === index) {
103
- return;
104
- }
105
- if (elementIndex !== -1 && element.parentElement) {
106
- element.parentElement.removeChild(element);
107
- }
108
- let idx = index;
109
- let nextDataObject = this.datasource[idx + 1];
110
- while (nextDataObject) {
111
- const nextElement = this.map.get(nextDataObject);
112
- if (nextElement) {
113
- this.element.insertBefore(element, nextElement);
114
- return;
115
- }
116
- idx += 1;
117
- nextDataObject = this.datasource[idx + 1];
118
- }
119
- this.element.appendChild(element);
120
- }
121
-
122
- /**
123
- * @param {T} data
124
- * @return {void}
125
- */
126
- removeItem(data) {
127
- const element = this.map.get(data);
128
- if (!element) {
129
- return;
130
- }
131
- if (element.parentElement) {
132
- element.parentElement.removeChild(element);
50
+ /**
51
+ * @template T
52
+ * @extends {DomAdapter<T, HTMLLIElement>}
53
+ */
54
+ export default class ListAdapter extends DomAdapter {
55
+ /** @param {ListAdapterCreateOptions<T>} options */
56
+ constructor(options) {
57
+ super(options);
58
+ if (!options.render) {
59
+ this.render = AnyListAdapterRenderer;
133
60
  }
134
- this.map.delete(data);
61
+ this.create = ListAdapter.create;
135
62
  }
136
63
 
137
- detach() {
138
- this.element.removeAttribute('mdw-adapter');
139
- this.element = null;
140
- this.datasource = null;
64
+ static create() {
65
+ const element = document.createElement('li');
66
+ element.classList.add('mdw-list__item');
67
+ return element;
141
68
  }
142
69
  }
143
-
144
- export {
145
- ListAdapter,
146
- };
@@ -1,4 +1,9 @@
1
- import { TextField } from '../../components/textfield/index';
1
+ // https://www.w3.org/TR/wai-aria-practices/#combobox
2
+
3
+ import * as ListContent from '../../components/list/content.js';
4
+ import * as List from '../../components/list/index.js';
5
+ import * as TextField from '../../components/textfield/index.js';
6
+ import { getTextNode } from '../../core/dom.js';
2
7
 
3
8
  /**
4
9
  * @param {string} input
@@ -7,7 +12,7 @@ import { TextField } from '../../components/textfield/index';
7
12
  */
8
13
  function containsTextFilter(input, content) {
9
14
  return content.trim().toLocaleLowerCase()
10
- .indexOf(input.trim().toLocaleLowerCase()) !== -1;
15
+ .includes(input.trim().toLocaleLowerCase());
11
16
  }
12
17
 
13
18
  /**
@@ -25,54 +30,40 @@ function startsWithTextFilter(input, content) {
25
30
  * @return {string}
26
31
  */
27
32
  function defaultItemTextParser(item) {
28
- /**
29
- * @param {Node} node
30
- * @return {string}
31
- */
32
- function getTextNodeOnly(node) {
33
- let text = '';
34
- for (let i = 0; i < node.childNodes.length; i += 1) {
35
- const childNode = node.childNodes[i];
36
- if (childNode.nodeType === Node.TEXT_NODE) {
37
- text += childNode.textContent;
38
- }
39
- }
40
- return text;
41
- }
42
33
  if (item.hasAttribute('data-mdw-search-text')) {
43
34
  return item.getAttribute('data-mdw-search-text');
44
35
  }
45
- let textElement = item.querySelector('.mdw-list__text .mdw-list__text-line');
46
- if (!textElement) {
47
- textElement = item.querySelector('.mdw-list__text');
48
- }
49
- if (!textElement) {
50
- textElement = item.querySelector('.mdw-list__text');
51
- }
52
- if (textElement) {
53
- return getTextNodeOnly(textElement);
36
+ let textElement;
37
+ [
38
+ 'mdw-list__text-line',
39
+ 'mdw-list__text',
40
+ 'mdw-list__content',
41
+ ].some((className) => {
42
+ textElement = item.getElementsByClassName(className)[0];
43
+ return textElement != null;
44
+ });
45
+
46
+ const textNode = textElement ? getTextNode(textElement) : getTextNode(item);
47
+ if (textNode) {
48
+ return textNode.textContent || '';
54
49
  }
55
- return getTextNodeOnly(item);
50
+ return '';
56
51
  }
57
52
 
58
53
  /**
59
54
  * @param {Element} list
60
- * @param {boolean=} backwards
55
+ * @param {boolean} [backwards=false]
61
56
  * @return {Element} sibling
62
57
  */
63
58
  function selectSibling(list, backwards) {
64
- const current = list.querySelector('.mdw-list__item[mdw-selected]');
65
- const items = list.querySelectorAll('.mdw-list__item:not([hidden]):not([disabled])');
59
+ const current = list.querySelector('[role="option"][aria-selected="true"]');
60
+ const items = list.querySelectorAll('[role="option"]:not([aria-hidden="true"]):not([disabled])');
66
61
  let sibling;
67
- if (current && !current.hasAttribute('hidden')) {
68
- for (let i = 0; i < items.length; i += 1) {
69
- const item = items[i];
62
+ if (current && current.getAttribute('aria-hidden') !== 'true') {
63
+ for (let i = 0; i < items.length; i++) {
64
+ const item = items.item(i);
70
65
  if (item === current) {
71
- if (backwards) {
72
- sibling = items[i - 1];
73
- } else {
74
- sibling = items[i + 1];
75
- }
66
+ sibling = backwards ? items[i - 1] : items[i + 1];
76
67
  break;
77
68
  }
78
69
  }
@@ -83,46 +74,50 @@ function selectSibling(list, backwards) {
83
74
  }
84
75
  if (sibling && sibling !== current) {
85
76
  if (current) {
86
- current.removeAttribute('mdw-selected');
77
+ current.setAttribute('aria-selected', 'false');
87
78
  }
88
- sibling.setAttribute('mdw-selected', '');
79
+ sibling.setAttribute('aria-selected', 'true');
89
80
  return sibling;
90
81
  }
91
82
  return null;
92
83
  }
93
84
 
94
85
  /**
95
- * @param {Element} listItem
96
- * @return {void}
86
+ * @param {Element} el
87
+ * @return {number}
97
88
  */
98
- function scrollItemIntoView(listItem) {
99
- /**
100
- * @param {Element} el
101
- * @return {number}
102
- */
103
- function getElementVisibility(el) {
104
- let rect = el.getBoundingClientRect();
105
- const { top, height } = rect;
106
- let next = el.parentElement;
107
- do {
108
- rect = next.getBoundingClientRect();
109
- if ((top + height) > rect.bottom) {
110
- // bottom clipped
111
- return 1;
112
- }
113
- if (top < rect.top) {
114
- // top clipped
115
- return -1;
116
- }
117
- next = next.parentElement;
118
- } while (next !== document.body);
119
- if (top < 0) {
120
- return -1;
121
- } else if ((top + height) > document.documentElement.clientHeight) {
89
+ function getElementVisibility(el) {
90
+ let rect = el.getBoundingClientRect();
91
+ const { top, height } = rect;
92
+ let next = el.parentElement;
93
+ do {
94
+ rect = next.getBoundingClientRect();
95
+ // eslint-disable-next-line unicorn/consistent-destructuring
96
+ if ((top + height) > rect.bottom) {
97
+ // bottom clipped
122
98
  return 1;
123
99
  }
124
- return 0;
100
+ // eslint-disable-next-line unicorn/consistent-destructuring
101
+ if (top < rect.top) {
102
+ // top clipped
103
+ return -1;
104
+ }
105
+ next = next.parentElement;
106
+ } while (next !== document.body);
107
+ if (top < 0) {
108
+ return -1;
109
+ }
110
+ if ((top + height) > document.documentElement.clientHeight) {
111
+ return 1;
125
112
  }
113
+ return 0;
114
+ }
115
+
116
+ /**
117
+ * @param {Element} listItem
118
+ * @return {void}
119
+ */
120
+ function scrollItemIntoView(listItem) {
126
121
  const visibility = getElementVisibility(listItem);
127
122
  if (visibility < 0) {
128
123
  listItem.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'center' });
@@ -136,28 +131,32 @@ function scrollItemIntoView(listItem) {
136
131
  * @param {string} input
137
132
  * @param {string} content
138
133
  * @return {boolean} passes
139
- */
134
+ */
140
135
 
141
136
  /**
137
+ * @template T
142
138
  * @typedef SearchAdapterOptions
143
139
  * @prop {Element} textfield
144
140
  * @prop {Element} list
145
- * @prop {('contains'|'startsWith'|SearchAdapterTextFilter)=} [textFilter='contains']
146
- * @prop {(function(Element):string)=} itemTextParser
147
- * @prop {boolean=} [dropdown=false]
148
- * @prop {boolean=} [filterItems=true]
141
+ * @prop {('contains'|'startsWith'|SearchAdapterTextFilter)} [textFilter='contains']
142
+ * @prop {function(Element):string} [itemTextParser]
143
+ * @prop {boolean} [dropdown=false]
144
+ * @prop {boolean} [filterItems=true]
149
145
  * @prop {('replace'|'append'|'none')} [suggestionMethod='replace']
150
- * @prop {(function(string, Function, Function=))=} performSearch
151
- * @prop {(function(Function, Function))=} updateList
152
- * @prop {boolean=} [searchOnFocus=true]
153
- * @prop {number=} debounce Debounce time in milliseconds
146
+ * @prop {function(string):(Promise<T[]>)|T[]} [performSearch]
147
+ * @prop {function(T[]):(Promise<void>|void)} [updateList]
148
+ * @prop {boolean} [searchOnFocus=true]
149
+ * @prop {number} [debounce=null] Debounce time in milliseconds
154
150
  */
155
151
 
156
- class SearchAdapter {
157
- /** @param {SearchAdapterOptions} options */
152
+ /** @template T */
153
+ export default class SearchAdapter {
154
+ /** @param {SearchAdapterOptions<T>} options */
158
155
  constructor(options) {
159
156
  this.textfield = options.textfield;
160
157
  this.list = options.list;
158
+ this.list.setAttribute('role', 'listbox');
159
+ List.attach(this.list);
161
160
  if (typeof options.textFilter === 'function') {
162
161
  this.filter = options.textFilter;
163
162
  } else if (options.textFilter === 'startsWith') {
@@ -166,12 +165,10 @@ class SearchAdapter {
166
165
  this.filter = containsTextFilter;
167
166
  }
168
167
  this.itemTextParser = defaultItemTextParser || options.itemTextParser;
169
- /** @type {HTMLInputElement} */
170
- const input = (this.textfield.getElementsByClassName('mdw-textfield__input')[0]);
168
+ const input = /** @type {HTMLInputElement} */ (this.textfield.getElementsByClassName('mdw-textfield__input')[0]);
171
169
  this.input = input;
172
- this.list.addEventListener('mdw:itemactivated', (event) => {
173
- /** @type {HTMLElement} */
174
- const item = (event.target);
170
+ this.list.addEventListener(ListContent.ACTIVATE_EVENT, (event) => {
171
+ const item = /** @type {HTMLElement} */(event.target);
175
172
  this.onItemSelected(item);
176
173
  const inputValue = this.input.value || '';
177
174
  this.suggestedInput = inputValue;
@@ -208,20 +205,38 @@ class SearchAdapter {
208
205
  this.debounce = options.debounce;
209
206
  this.suggestionMethod = options.suggestionMethod || 'replace';
210
207
  this.currentSearchTerm = this.input.value || '';
211
- /** @type {string} */
208
+ /** @type {?string} */
212
209
  this.suggestedInput = null;
213
- /** @type {string} */
210
+ /** @type {?string} */
214
211
  this.previousValue = null;
215
- this.performSearch = options.performSearch || ((value, resolve = () => {}) => resolve());
216
- this.updateList = options.updateList || ((results, resolve = () => {}) => resolve());
212
+ this.performSearch = options.performSearch || this.performSearch.bind(this);
213
+ this.updateList = options.updateList || this.updateList.bind(this);
214
+ }
215
+
216
+ /**
217
+ * @param {string} value
218
+ * @return {Promise<T[]>|T[]}
219
+ */
220
+ // eslint-disable-next-line class-methods-use-this
221
+ performSearch(value) {
222
+ return [];
223
+ }
224
+
225
+ /**
226
+ * @param {T[]} results
227
+ * @return {Promise<void>|void}
228
+ */
229
+ // eslint-disable-next-line class-methods-use-this
230
+ updateList(results) {
231
+ return undefined;
217
232
  }
218
233
 
219
234
  /**
220
235
  * Default input handler
221
- * @param {Event=} event
236
+ * @param {Event} [event]
222
237
  * @return {void}
223
238
  */
224
- handleInputEvent(event) { // eslint-disable-line no-unused-vars
239
+ handleInputEvent(event) { // eslint-disable-line @typescript-eslint/no-unused-vars
225
240
  if (document.activeElement !== this.input) {
226
241
  return;
227
242
  }
@@ -236,89 +251,54 @@ class SearchAdapter {
236
251
  this.previousValue = inputValue;
237
252
  this.currentSearchTerm = inputValue;
238
253
 
239
- /**
240
- * @param {Error} error
241
- * @return {void}
242
- */
243
- function onErrorCallback(error) {
244
- if (error.message === 'debounced') {
245
- return;
246
- }
247
- if (error.message === 'expired') {
248
- return;
254
+ void (async () => {
255
+ try {
256
+ await this.performDebounce(inputValue);
257
+ await this.checkExpired(inputValue);
258
+ const searchResults = await this.performSearch(inputValue);
259
+ await this.checkExpired(inputValue);
260
+ await this.updateList(searchResults);
261
+ await this.filterListItems();
262
+ } catch (error) {
263
+ if (error.message === 'debounced') {
264
+ return;
265
+ }
266
+ if (error.message === 'expired') {
267
+ return;
268
+ }
269
+ throw error;
249
270
  }
250
- throw error;
251
- }
252
- // IE does not support ES6 Promises
253
- // Using resolve/reject callbacks instead.
254
- // Syntactically ugly, but removes need for a polyfill.
255
- this.performDebounce(inputValue, () => {
256
- this.checkExpired(inputValue, () => {
257
- this.performSearch(inputValue, (searchResults) => {
258
- this.checkExpired(inputValue, () => {
259
- this.updateList(searchResults, () => {
260
- this.filterListItems();
261
- });
262
- }, onErrorCallback);
263
- }, onErrorCallback);
264
- }, onErrorCallback);
265
- }, onErrorCallback);
266
-
267
- /**
268
- * Promise.resolve()
269
- * .then(() => this.performDebounce(inputValue))
270
- * .then(() => this.checkExpired(inputValue))
271
- * .then(() => this.performSearch(inputValue))
272
- * .then((searchResults) => {
273
- * results = searchResults;
274
- * })
275
- * .then(() => this.checkExpired(inputValue))
276
- * .then(() => this.updateList(results))
277
- * .then(() => this.filterListItems())
278
- * .catch((error) => {
279
- * if (error.message === 'debounced') {
280
- * return;
281
- * }
282
- * if (error.message === 'expired') {
283
- * return;
284
- * }
285
- * throw error;
286
- * });
287
- */
271
+ })();
288
272
  }
289
273
 
290
274
  /**
291
275
  * @param {string} inputValue
292
- * @param {function} resolve
293
- * @param {function(Error)} reject
294
- * @return {void}
276
+ * @return {Promise<void>}
295
277
  */
296
- checkExpired(inputValue, resolve, reject) {
278
+ checkExpired(inputValue) {
297
279
  if (inputValue === this.currentSearchTerm) {
298
- resolve();
299
- } else {
300
- reject(new Error('expired'));
280
+ return Promise.resolve();
301
281
  }
282
+ return Promise.reject(new Error('expired'));
302
283
  }
303
284
 
304
285
  /**
305
286
  * @param {string} searchTerm
306
- * @param {function} resolve
307
- * @param {function(Error)} reject
308
- * @return {void}
287
+ * @return {Promise<void>}
309
288
  */
310
- performDebounce(searchTerm, resolve, reject) {
289
+ performDebounce(searchTerm) {
311
290
  if (!this.debounce) {
312
- resolve();
313
- return;
291
+ return Promise.resolve();
314
292
  }
315
- setTimeout(() => {
316
- if (searchTerm !== this.currentSearchTerm) {
317
- reject(new Error('debounced'));
318
- return;
319
- }
320
- resolve();
321
- }, this.debounce);
293
+ return new Promise((resolve, reject) => {
294
+ setTimeout(() => {
295
+ if (searchTerm !== this.currentSearchTerm) {
296
+ reject(new Error('debounced'));
297
+ return;
298
+ }
299
+ resolve();
300
+ }, this.debounce);
301
+ });
322
302
  }
323
303
 
324
304
  /** @return {boolean} handled */
@@ -374,7 +354,7 @@ class SearchAdapter {
374
354
  * @param {Event|FocusEvent} event
375
355
  * @return {void}
376
356
  */
377
- handleBlurEvent(event) { // eslint-disable-line no-unused-vars
357
+ handleBlurEvent(event) { // eslint-disable-line @typescript-eslint/no-unused-vars
378
358
  if (this.dropdown) {
379
359
  const dropDownElement = this.textfield.querySelector('.mdw-textfield__dropdown');
380
360
  if (dropDownElement.hasAttribute('mdw-show')) {
@@ -410,7 +390,7 @@ class SearchAdapter {
410
390
  }
411
391
 
412
392
  /**
413
- * @param {SearchAdapterTextFilter=} fnFilter
393
+ * @param {SearchAdapterTextFilter} [fnFilter]
414
394
  * @return {void}
415
395
  */
416
396
  filterListItems(fnFilter) {
@@ -418,21 +398,20 @@ class SearchAdapter {
418
398
  return;
419
399
  }
420
400
  const input = this.input.value;
421
- const current = this.list.querySelector('.mdw-list__item[mdw-selected]');
422
- const items = this.list.querySelectorAll('.mdw-list__item');
401
+ const current = this.list.querySelector('[role="option"][aria-selected="true"]');
402
+ const items = this.list.querySelectorAll('[role="option"]');
423
403
  let hasItem = false;
424
- for (let i = 0; i < items.length; i += 1) {
425
- const item = items[i];
404
+ for (const item of items) {
426
405
  const content = this.itemTextParser(item);
427
406
  const fn = fnFilter || this.filter;
428
407
  if (fn(input, content)) {
429
408
  hasItem = true;
430
- item.removeAttribute('hidden');
409
+ item.setAttribute('aria-hidden', 'false');
431
410
  } else {
432
- item.setAttribute('hidden', '');
411
+ item.setAttribute('aria-hidden', 'true');
433
412
  }
434
413
  }
435
- if (current && current.hasAttribute('hidden')) {
414
+ if (current && current.getAttribute('aria-hidden') === 'true') {
436
415
  const newSelection = selectSibling(this.list);
437
416
  if (newSelection) {
438
417
  this.onItemSelected(newSelection);
@@ -493,24 +472,20 @@ class SearchAdapter {
493
472
  }
494
473
  case 'Enter': {
495
474
  /** @type {HTMLElement} */
496
- const current = this.list.querySelector('.mdw-list__item[mdw-selected]');
497
- if (current) {
498
- if (this.hideDropDown()) {
499
- current.click();
500
- event.stopPropagation();
501
- event.preventDefault();
502
- }
475
+ const current = this.list.querySelector('[role="option"][aria-selected="true"]');
476
+ if (current && this.hideDropDown()) {
477
+ current.click();
478
+ event.stopPropagation();
479
+ event.preventDefault();
503
480
  }
504
481
  break;
505
482
  }
506
483
  case 'Tab': {
507
484
  /** @type {HTMLElement} */
508
- const current = this.list.querySelector('.mdw-list__item[mdw-selected]');
509
- if (current) {
510
- if (this.hideDropDown()) {
511
- current.click();
512
- event.stopPropagation();
513
- }
485
+ const current = this.list.querySelector('[role="option"][aria-selected="true"]');
486
+ if (current && this.hideDropDown()) {
487
+ current.click();
488
+ event.stopPropagation();
514
489
  }
515
490
  break;
516
491
  }
@@ -518,5 +493,3 @@ class SearchAdapter {
518
493
  }
519
494
  }
520
495
  }
521
-
522
- export { SearchAdapter }; // eslint-disable-line import/prefer-default-export