mtrl 0.10.0-next.1 → 0.10.0-next.3

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 (392) hide show
  1. package/README.md +26 -5
  2. package/dist/README.md +26 -5
  3. package/dist/components/bottom-sheet/config.js +11 -4
  4. package/dist/components/bottom-sheet/constants.d.ts +0 -2
  5. package/dist/components/bottom-sheet/constants.js +0 -1
  6. package/dist/components/bottom-sheet/features/state.js +39 -2
  7. package/dist/components/bottom-sheet/features/structure.js +5 -1
  8. package/dist/components/bottom-sheet/types.d.ts +15 -2
  9. package/dist/components/chips/features/controller.js +2 -1
  10. package/dist/components/datepicker/constants.d.ts +1 -0
  11. package/dist/components/datepicker/constants.js +1 -0
  12. package/dist/components/datepicker/datepicker.js +79 -10
  13. package/dist/components/datepicker/render.js +43 -10
  14. package/dist/components/datepicker/types.d.ts +4 -1
  15. package/dist/components/datepicker/utils.d.ts +0 -7
  16. package/dist/components/datepicker/utils.js +0 -9
  17. package/dist/components/dialog/api.js +5 -0
  18. package/dist/components/dialog/config.js +8 -2
  19. package/dist/components/dialog/features.js +73 -14
  20. package/dist/components/dialog/types.d.ts +11 -0
  21. package/dist/components/drawer/config.js +9 -2
  22. package/dist/components/drawer/features/items.js +2 -1
  23. package/dist/components/drawer/features/state.js +50 -9
  24. package/dist/components/drawer/types.d.ts +12 -0
  25. package/dist/components/extended-fab/config.js +1 -1
  26. package/dist/components/extended-fab/types.d.ts +3 -2
  27. package/dist/components/list/features/renderer.js +2 -1
  28. package/dist/components/menu/config.js +6 -1
  29. package/dist/components/menu/features/controller.js +23 -8
  30. package/dist/components/menu/features/keyboard.js +3 -2
  31. package/dist/components/menu/features/layer.d.ts +15 -0
  32. package/dist/components/menu/features/layer.js +16 -0
  33. package/dist/components/menu/features/opener.js +2 -1
  34. package/dist/components/menu/features/position.js +8 -6
  35. package/dist/components/menu/features/submenu.js +12 -4
  36. package/dist/components/menu/types.d.ts +12 -0
  37. package/dist/components/navigation-rail/navigation-rail.js +4 -3
  38. package/dist/components/search/api.d.ts +9 -1
  39. package/dist/components/search/api.js +16 -8
  40. package/dist/components/search/config.d.ts +17 -1
  41. package/dist/components/search/config.js +10 -1
  42. package/dist/components/search/constants.d.ts +11 -0
  43. package/dist/components/search/constants.js +9 -0
  44. package/dist/components/search/features/input.js +12 -3
  45. package/dist/components/search/features/states.d.ts +4 -2
  46. package/dist/components/search/features/states.js +98 -16
  47. package/dist/components/search/features/structure.d.ts +7 -20
  48. package/dist/components/search/features/structure.js +79 -29
  49. package/dist/components/search/features/suggestions.js +22 -6
  50. package/dist/components/search/index.d.ts +1 -1
  51. package/dist/components/search/types.d.ts +23 -0
  52. package/dist/components/segmented-button/segment.js +1 -1
  53. package/dist/components/select/features.js +6 -3
  54. package/dist/components/select/types.d.ts +7 -0
  55. package/dist/components/side-sheet/config.js +11 -3
  56. package/dist/components/side-sheet/features/state.js +38 -2
  57. package/dist/components/side-sheet/features/structure.js +1 -1
  58. package/dist/components/side-sheet/types.d.ts +12 -0
  59. package/dist/components/slider/features/controller.js +6 -2
  60. package/dist/components/slider/features/handlers.js +2 -1
  61. package/dist/components/slider/features/states.d.ts +0 -3
  62. package/dist/components/slider/features/states.js +2 -15
  63. package/dist/components/snackbar/api.js +28 -4
  64. package/dist/components/snackbar/config.d.ts +1 -0
  65. package/dist/components/snackbar/config.js +8 -1
  66. package/dist/components/snackbar/layer.d.ts +23 -0
  67. package/dist/components/snackbar/layer.js +85 -0
  68. package/dist/components/snackbar/types.d.ts +12 -0
  69. package/dist/components/split-button/features/menu.js +1 -0
  70. package/dist/components/split-button/types.d.ts +6 -0
  71. package/dist/components/textfield/constants.d.ts +11 -1
  72. package/dist/components/textfield/constants.js +5 -0
  73. package/dist/components/textfield/features/placement.js +45 -82
  74. package/dist/components/timepicker/api.js +112 -55
  75. package/dist/components/timepicker/config.d.ts +0 -1082
  76. package/dist/components/timepicker/config.js +3 -40
  77. package/dist/components/timepicker/constants.d.ts +21 -11
  78. package/dist/components/timepicker/constants.js +4 -3
  79. package/dist/components/timepicker/dial.d.ts +35 -0
  80. package/dist/components/timepicker/dial.js +175 -0
  81. package/dist/components/timepicker/render.js +232 -333
  82. package/dist/components/timepicker/timepicker.d.ts +0 -5
  83. package/dist/components/timepicker/timepicker.js +7 -21
  84. package/dist/components/timepicker/types.d.ts +42 -10
  85. package/dist/components/timepicker/utils.d.ts +18 -51
  86. package/dist/components/timepicker/utils.js +36 -86
  87. package/dist/components/tooltip/api.js +34 -3
  88. package/dist/components/tooltip/config.js +8 -1
  89. package/dist/components/tooltip/types.d.ts +12 -0
  90. package/dist/core/dom/focus.d.ts +36 -0
  91. package/dist/core/dom/focus.js +47 -0
  92. package/dist/core/dom/index.d.ts +3 -0
  93. package/dist/core/dom/index.js +2 -0
  94. package/dist/core/dom/layer.d.ts +58 -0
  95. package/dist/core/dom/layer.js +52 -0
  96. package/dist/elements/badge.d.ts +62 -0
  97. package/dist/elements/badge.js +33 -0
  98. package/dist/elements/bottom-app-bar.d.ts +46 -0
  99. package/dist/elements/bottom-app-bar.js +42 -0
  100. package/dist/elements/bottom-sheet.d.ts +121 -0
  101. package/dist/elements/bottom-sheet.js +95 -0
  102. package/dist/elements/button-group.d.ts +153 -0
  103. package/dist/elements/button-group.js +174 -0
  104. package/dist/elements/button.d.ts +5 -8
  105. package/dist/elements/button.js +4 -19
  106. package/dist/elements/card.d.ts +68 -0
  107. package/dist/elements/card.js +112 -0
  108. package/dist/elements/carousel.d.ts +123 -0
  109. package/dist/elements/carousel.js +117 -0
  110. package/dist/elements/checkbox.d.ts +92 -0
  111. package/dist/elements/checkbox.js +49 -0
  112. package/dist/elements/chips.d.ts +136 -0
  113. package/dist/elements/chips.js +214 -0
  114. package/dist/elements/css/bottom-app-bar.d.ts +1 -0
  115. package/dist/elements/css/bottom-app-bar.js +3 -0
  116. package/dist/elements/css/bottom-sheet.d.ts +1 -0
  117. package/dist/elements/css/bottom-sheet.js +3 -0
  118. package/dist/elements/css/button-group.d.ts +1 -0
  119. package/dist/elements/css/button-group.js +5 -0
  120. package/dist/elements/css/card.d.ts +1 -0
  121. package/dist/elements/css/card.js +4 -0
  122. package/dist/elements/css/carousel.d.ts +1 -0
  123. package/dist/elements/css/carousel.js +3 -0
  124. package/dist/elements/css/checkbox.d.ts +1 -0
  125. package/dist/elements/css/checkbox.js +3 -0
  126. package/dist/elements/css/chips.d.ts +1 -0
  127. package/dist/elements/css/chips.js +3 -0
  128. package/dist/elements/css/datepicker.d.ts +1 -0
  129. package/dist/elements/css/datepicker.js +3 -0
  130. package/dist/elements/css/dialog.d.ts +1 -0
  131. package/dist/elements/css/dialog.js +5 -0
  132. package/dist/elements/css/divider.d.ts +1 -0
  133. package/dist/elements/css/divider.js +3 -0
  134. package/dist/elements/css/drawer.d.ts +1 -0
  135. package/dist/elements/css/drawer.js +3 -0
  136. package/dist/elements/css/extended-fab.d.ts +1 -0
  137. package/dist/elements/css/extended-fab.js +3 -0
  138. package/dist/elements/css/fab.d.ts +1 -0
  139. package/dist/elements/css/fab.js +3 -0
  140. package/dist/elements/css/icon-button.d.ts +1 -0
  141. package/dist/elements/css/icon-button.js +3 -0
  142. package/dist/elements/css/index.js +29 -0
  143. package/dist/elements/css/list.d.ts +1 -0
  144. package/dist/elements/css/list.js +3 -0
  145. package/dist/elements/css/loading-indicator.d.ts +1 -0
  146. package/dist/elements/css/loading-indicator.js +3 -0
  147. package/dist/elements/css/menu.d.ts +1 -0
  148. package/dist/elements/css/menu.js +3 -0
  149. package/dist/elements/css/navigation-rail.d.ts +1 -0
  150. package/dist/elements/css/navigation-rail.js +3 -0
  151. package/dist/elements/css/radios.d.ts +1 -0
  152. package/dist/elements/css/radios.js +3 -0
  153. package/dist/elements/css/search.d.ts +1 -0
  154. package/dist/elements/css/search.js +3 -0
  155. package/dist/elements/css/select.d.ts +1 -0
  156. package/dist/elements/css/select.js +5 -0
  157. package/dist/elements/css/side-sheet.d.ts +1 -0
  158. package/dist/elements/css/side-sheet.js +3 -0
  159. package/dist/elements/css/slider.d.ts +1 -0
  160. package/dist/elements/css/slider.js +3 -0
  161. package/dist/elements/css/snackbar.d.ts +1 -0
  162. package/dist/elements/css/snackbar.js +5 -0
  163. package/dist/elements/css/split-button.d.ts +1 -0
  164. package/dist/elements/css/split-button.js +5 -0
  165. package/dist/elements/css/switch.js +1 -1
  166. package/dist/elements/css/textfield.d.ts +1 -0
  167. package/dist/elements/css/textfield.js +3 -0
  168. package/dist/elements/css/timepicker.d.ts +1 -0
  169. package/dist/elements/css/timepicker.js +3 -0
  170. package/dist/elements/css/tooltip.d.ts +1 -0
  171. package/dist/elements/css/tooltip.js +3 -0
  172. package/dist/elements/css/top-app-bar.d.ts +1 -0
  173. package/dist/elements/css/top-app-bar.js +3 -0
  174. package/dist/elements/datepicker.d.ts +155 -0
  175. package/dist/elements/datepicker.js +150 -0
  176. package/dist/elements/define.d.ts +38 -6
  177. package/dist/elements/define.js +68 -4
  178. package/dist/elements/dialog.d.ts +118 -0
  179. package/dist/elements/dialog.js +155 -0
  180. package/dist/elements/divider.d.ts +54 -0
  181. package/dist/elements/divider.js +23 -0
  182. package/dist/elements/drawer.d.ts +137 -0
  183. package/dist/elements/drawer.js +136 -0
  184. package/dist/elements/extended-fab.d.ts +82 -0
  185. package/dist/elements/extended-fab.js +32 -0
  186. package/dist/elements/fab.d.ts +65 -0
  187. package/dist/elements/fab.js +24 -0
  188. package/dist/elements/form-button.d.ts +29 -0
  189. package/dist/elements/form-button.js +19 -0
  190. package/dist/elements/icon-button.d.ts +104 -0
  191. package/dist/elements/icon-button.js +56 -0
  192. package/dist/elements/index.d.ts +357 -1
  193. package/dist/elements/index.js +134 -0
  194. package/dist/elements/list.d.ts +135 -0
  195. package/dist/elements/list.js +191 -0
  196. package/dist/elements/loading-indicator.d.ts +44 -0
  197. package/dist/elements/loading-indicator.js +26 -0
  198. package/dist/elements/menu.d.ts +165 -0
  199. package/dist/elements/menu.js +178 -0
  200. package/dist/elements/navigation-rail.d.ts +153 -0
  201. package/dist/elements/navigation-rail.js +129 -0
  202. package/dist/elements/progress.d.ts +90 -0
  203. package/dist/elements/progress.js +40 -0
  204. package/dist/elements/radios.d.ts +99 -0
  205. package/dist/elements/radios.js +126 -0
  206. package/dist/elements/search.d.ts +215 -0
  207. package/dist/elements/search.js +215 -0
  208. package/dist/elements/select.d.ts +134 -0
  209. package/dist/elements/select.js +149 -0
  210. package/dist/elements/sheet.d.ts +44 -0
  211. package/dist/elements/sheet.js +60 -0
  212. package/dist/elements/side-sheet.d.ts +91 -0
  213. package/dist/elements/side-sheet.js +63 -0
  214. package/dist/elements/slider.d.ts +160 -0
  215. package/dist/elements/slider.js +136 -0
  216. package/dist/elements/snackbar.d.ts +99 -0
  217. package/dist/elements/snackbar.js +54 -0
  218. package/dist/elements/split-button.d.ts +82 -0
  219. package/dist/elements/split-button.js +77 -0
  220. package/dist/elements/switch.d.ts +2 -1
  221. package/dist/elements/tabs.d.ts +3 -2
  222. package/dist/elements/tabs.js +3 -5
  223. package/dist/elements/textfield.d.ts +160 -0
  224. package/dist/elements/textfield.js +107 -0
  225. package/dist/elements/timepicker.d.ts +158 -0
  226. package/dist/elements/timepicker.js +159 -0
  227. package/dist/elements/tooltip.d.ts +89 -0
  228. package/dist/elements/tooltip.js +132 -0
  229. package/dist/elements/top-app-bar.d.ts +63 -0
  230. package/dist/elements/top-app-bar.js +94 -0
  231. package/dist/index.cjs +0 -0
  232. package/dist/package.json +1 -1
  233. package/dist/react/index.d.ts +71 -1
  234. package/dist/react/index.js +71 -1
  235. package/dist/solid/index.d.ts +71 -1
  236. package/dist/solid/index.js +71 -1
  237. package/dist/styles/badge.css +1 -1
  238. package/dist/styles/base.css +1 -1
  239. package/dist/styles/bottom-app-bar.css +1 -1
  240. package/dist/styles/bottom-sheet.css +2 -2
  241. package/dist/styles/button-group.css +1 -1
  242. package/dist/styles/button.css +1 -1
  243. package/dist/styles/card.css +1 -1
  244. package/dist/styles/carousel.css +1 -1
  245. package/dist/styles/checkbox.css +1 -1
  246. package/dist/styles/chips.css +1 -1
  247. package/dist/styles/datepicker.css +2 -2
  248. package/dist/styles/dialog.css +2 -2
  249. package/dist/styles/divider.css +1 -1
  250. package/dist/styles/drawer.css +2 -2
  251. package/dist/styles/extended-fab.css +1 -1
  252. package/dist/styles/fab.css +1 -1
  253. package/dist/styles/icon-button.css +1 -1
  254. package/dist/styles/list.css +1 -1
  255. package/dist/styles/loading-indicator.css +1 -1
  256. package/dist/styles/menu.css +2 -2
  257. package/dist/styles/navigation-rail.css +1 -1
  258. package/dist/styles/progress.css +1 -1
  259. package/dist/styles/radios.css +2 -2
  260. package/dist/styles/search.css +2 -2
  261. package/dist/styles/segmented-button.css +1 -1
  262. package/dist/styles/select.css +1 -1
  263. package/dist/styles/side-sheet.css +2 -2
  264. package/dist/styles/slider.css +1 -1
  265. package/dist/styles/snackbar.css +2 -2
  266. package/dist/styles/split-button.css +1 -1
  267. package/dist/styles/switch.css +2 -2
  268. package/dist/styles/tabs.css +1 -1
  269. package/dist/styles/textfield.css +2 -2
  270. package/dist/styles/timepicker.css +2 -2
  271. package/dist/styles/tooltip.css +2 -2
  272. package/dist/styles/top-app-bar.css +1 -1
  273. package/dist/styles/utilities.css +1 -1
  274. package/dist/styles.css +2 -2
  275. package/dist/svelte/Badge.svelte +20 -0
  276. package/dist/svelte/Badge.svelte.d.ts +5 -0
  277. package/dist/svelte/BottomAppBar.svelte +19 -0
  278. package/dist/svelte/BottomAppBar.svelte.d.ts +5 -0
  279. package/dist/svelte/BottomSheet.svelte +19 -0
  280. package/dist/svelte/BottomSheet.svelte.d.ts +5 -0
  281. package/dist/svelte/ButtonGroup.svelte +20 -0
  282. package/dist/svelte/ButtonGroup.svelte.d.ts +5 -0
  283. package/dist/svelte/ButtonGroupItem.svelte +11 -0
  284. package/dist/svelte/ButtonGroupItem.svelte.d.ts +5 -0
  285. package/dist/svelte/Card.svelte +19 -0
  286. package/dist/svelte/Card.svelte.d.ts +5 -0
  287. package/dist/svelte/Carousel.svelte +20 -0
  288. package/dist/svelte/Carousel.svelte.d.ts +5 -0
  289. package/dist/svelte/CarouselItem.svelte +11 -0
  290. package/dist/svelte/CarouselItem.svelte.d.ts +5 -0
  291. package/dist/svelte/Checkbox.svelte +21 -0
  292. package/dist/svelte/Checkbox.svelte.d.ts +5 -0
  293. package/dist/svelte/Chip.svelte +11 -0
  294. package/dist/svelte/Chip.svelte.d.ts +5 -0
  295. package/dist/svelte/Chips.svelte +20 -0
  296. package/dist/svelte/Chips.svelte.d.ts +5 -0
  297. package/dist/svelte/Datepicker.svelte +20 -0
  298. package/dist/svelte/Datepicker.svelte.d.ts +5 -0
  299. package/dist/svelte/Dialog.svelte +19 -0
  300. package/dist/svelte/Dialog.svelte.d.ts +5 -0
  301. package/dist/svelte/Divider.svelte +19 -0
  302. package/dist/svelte/Divider.svelte.d.ts +5 -0
  303. package/dist/svelte/Drawer.svelte +20 -0
  304. package/dist/svelte/Drawer.svelte.d.ts +5 -0
  305. package/dist/svelte/DrawerItem.svelte +11 -0
  306. package/dist/svelte/DrawerItem.svelte.d.ts +5 -0
  307. package/dist/svelte/ExtendedFab.svelte +19 -0
  308. package/dist/svelte/ExtendedFab.svelte.d.ts +5 -0
  309. package/dist/svelte/Fab.svelte +19 -0
  310. package/dist/svelte/Fab.svelte.d.ts +5 -0
  311. package/dist/svelte/IconButton.svelte +20 -0
  312. package/dist/svelte/IconButton.svelte.d.ts +5 -0
  313. package/dist/svelte/List.svelte +21 -0
  314. package/dist/svelte/List.svelte.d.ts +5 -0
  315. package/dist/svelte/ListItem.svelte +11 -0
  316. package/dist/svelte/ListItem.svelte.d.ts +5 -0
  317. package/dist/svelte/LoadingIndicator.svelte +19 -0
  318. package/dist/svelte/LoadingIndicator.svelte.d.ts +5 -0
  319. package/dist/svelte/Menu.svelte +20 -0
  320. package/dist/svelte/Menu.svelte.d.ts +5 -0
  321. package/dist/svelte/MenuItem.svelte +11 -0
  322. package/dist/svelte/MenuItem.svelte.d.ts +5 -0
  323. package/dist/svelte/NavigationRail.svelte +20 -0
  324. package/dist/svelte/NavigationRail.svelte.d.ts +5 -0
  325. package/dist/svelte/NavigationRailItem.svelte +11 -0
  326. package/dist/svelte/NavigationRailItem.svelte.d.ts +5 -0
  327. package/dist/svelte/Progress.svelte +21 -0
  328. package/dist/svelte/Progress.svelte.d.ts +5 -0
  329. package/dist/svelte/Radio.svelte +11 -0
  330. package/dist/svelte/Radio.svelte.d.ts +5 -0
  331. package/dist/svelte/Radios.svelte +20 -0
  332. package/dist/svelte/Radios.svelte.d.ts +5 -0
  333. package/dist/svelte/Search.svelte +20 -0
  334. package/dist/svelte/Search.svelte.d.ts +5 -0
  335. package/dist/svelte/SearchSuggestion.svelte +11 -0
  336. package/dist/svelte/SearchSuggestion.svelte.d.ts +5 -0
  337. package/dist/svelte/Select.svelte +20 -0
  338. package/dist/svelte/Select.svelte.d.ts +5 -0
  339. package/dist/svelte/SelectOption.svelte +11 -0
  340. package/dist/svelte/SelectOption.svelte.d.ts +5 -0
  341. package/dist/svelte/SideSheet.svelte +19 -0
  342. package/dist/svelte/SideSheet.svelte.d.ts +5 -0
  343. package/dist/svelte/Slider.svelte +21 -0
  344. package/dist/svelte/Slider.svelte.d.ts +5 -0
  345. package/dist/svelte/Snackbar.svelte +20 -0
  346. package/dist/svelte/Snackbar.svelte.d.ts +5 -0
  347. package/dist/svelte/SplitButton.svelte +19 -0
  348. package/dist/svelte/SplitButton.svelte.d.ts +5 -0
  349. package/dist/svelte/Textfield.svelte +20 -0
  350. package/dist/svelte/Textfield.svelte.d.ts +5 -0
  351. package/dist/svelte/Timepicker.svelte +20 -0
  352. package/dist/svelte/Timepicker.svelte.d.ts +5 -0
  353. package/dist/svelte/Tooltip.svelte +20 -0
  354. package/dist/svelte/Tooltip.svelte.d.ts +5 -0
  355. package/dist/svelte/TopAppBar.svelte +19 -0
  356. package/dist/svelte/TopAppBar.svelte.d.ts +5 -0
  357. package/dist/svelte/index.d.ts +41 -0
  358. package/dist/svelte/index.js +70 -0
  359. package/dist/themes/autumn.css +1 -1
  360. package/dist/themes/baseline.css +1 -1
  361. package/dist/themes/brownbeige.css +1 -1
  362. package/dist/themes/browngreen.css +1 -1
  363. package/dist/themes/desert.css +1 -1
  364. package/dist/themes/forest.css +1 -1
  365. package/dist/themes/highcontrast.css +1 -1
  366. package/dist/themes/legacy.css +1 -1
  367. package/dist/themes/material.css +1 -1
  368. package/dist/themes/ocean.css +1 -1
  369. package/dist/themes/sageivory.css +1 -1
  370. package/dist/themes/spring.css +1 -1
  371. package/dist/themes/summer.css +1 -1
  372. package/dist/themes/sunset.css +1 -1
  373. package/dist/themes/tealcaramel.css +1 -1
  374. package/dist/themes/winter.css +1 -1
  375. package/dist/vue/index.d.ts +71 -1
  376. package/dist/vue/index.js +71 -1
  377. package/package.json +1 -1
  378. package/src/styles/components/_bottom-sheet.scss +42 -2
  379. package/src/styles/components/_datepicker.scss +52 -14
  380. package/src/styles/components/_dialog.scss +52 -0
  381. package/src/styles/components/_drawer.scss +42 -0
  382. package/src/styles/components/_menu.scss +9 -0
  383. package/src/styles/components/_radios.scss +2 -3
  384. package/src/styles/components/_search.scss +278 -273
  385. package/src/styles/components/_side-sheet.scss +41 -0
  386. package/src/styles/components/_snackbar.scss +12 -0
  387. package/src/styles/components/_switch.scss +2 -2
  388. package/src/styles/components/_textfield.scss +122 -70
  389. package/src/styles/components/_timepicker.scss +264 -198
  390. package/src/styles/components/_tooltip.scss +11 -0
  391. package/dist/components/timepicker/clockdial.d.ts +0 -34
  392. package/dist/components/timepicker/clockdial.js +0 -276
package/README.md CHANGED
@@ -136,23 +136,44 @@ Button progress and card actions load on demand; enable code splitting in your b
136
136
 
137
137
  ## Web components and frameworks (experimental)
138
138
 
139
- From `0.10.0-next.1` (`npm install mtrl@next`), components also ship as custom elements, with thin adapters for React, Vue, Svelte and Solid. Button, switch and tabs are available so far; more follow through 0.10.x. The API may still change.
139
+ From the 0.10.0 prereleases (`npm install mtrl@next`), every component also ships as a custom element, with thin adapters for React, Vue, Svelte and Solid. The API may still change before 1.0.
140
+
141
+ | Kind | Elements | Framework components (Vue: `M` prefix, e.g. `MDialog`) |
142
+ |------|----------|--------------------------------------------------------|
143
+ | Actions | `<m-button>`, `<m-icon-button>`, `<m-fab>`, `<m-extended-fab>`, `<m-split-button>`, `<m-button-group>` + `<m-button-group-item>` | `Button`, `IconButton`, `Fab`, `ExtendedFab`, `SplitButton`, `ButtonGroup`, `ButtonGroupItem` |
144
+ | Form controls | `<m-checkbox>`, `<m-switch>`, `<m-radios>` + `<m-radio>`, `<m-slider>`, `<m-textfield>`, `<m-select>` + `<m-select-option>`, `<m-search>` + `<m-search-suggestion>`, `<m-datepicker>`, `<m-timepicker>` | `Checkbox`, `Switch`, `Radios`, `Radio`, `Slider`, `Textfield`, `Select`, `SelectOption`, `Search`, `SearchSuggestion`, `Datepicker`, `Timepicker` |
145
+ | Selection | `<m-chips>` + `<m-chip>`, `<m-tabs>` + `<m-tab>`, `<m-list>` + `<m-list-item>` | `Chips`, `Chip`, `Tabs`, `Tab`, `List`, `ListItem` |
146
+ | Navigation | `<m-navigation-rail>` + `<m-navigation-rail-item>`, `<m-drawer>` + `<m-drawer-item>`, `<m-top-app-bar>`, `<m-bottom-app-bar>` | `NavigationRail`, `NavigationRailItem`, `Drawer`, `DrawerItem`, `TopAppBar`, `BottomAppBar` |
147
+ | Overlays (top layer) | `<m-menu>` + `<m-menu-item>`, `<m-dialog>`, `<m-bottom-sheet>`, `<m-side-sheet>`, `<m-tooltip>`, `<m-snackbar>` | `Menu`, `MenuItem`, `Dialog`, `BottomSheet`, `SideSheet`, `Tooltip`, `Snackbar` |
148
+ | Containment | `<m-card>`, `<m-carousel>` + `<m-carousel-item>` | `Card`, `Carousel`, `CarouselItem` |
149
+ | Indicators | `<m-progress>`, `<m-loading-indicator>`, `<m-badge>`, `<m-divider>` | `Progress`, `LoadingIndicator`, `Badge`, `Divider` |
150
+
151
+ Overlays open in the browser's top layer (`showModal()` or `popover`) from inside their own shadow root, so they keep their styles, trap focus and make the page inert where M3 says so. Form controls are form-associated: `name`, the form value, reset, validation, `<label for>` and back-navigation restore work as on native controls.
140
152
 
141
153
  ```html
142
154
  <script type="module">
143
155
  import 'mtrl/elements/css'; // the elements' styles
144
156
  import { defineAll } from 'mtrl/elements';
145
- defineAll(); // registers <m-button>, <m-switch>, <m-tabs>, <m-tab>
157
+ defineAll(); // registers every element (<m-button>, <m-switch>, …)
146
158
  </script>
147
159
 
148
- <m-switch name="wifi" checked>Wi-Fi</m-switch>
160
+ <form>
161
+ <m-textfield name="email" type="email" label="Email" required></m-textfield>
162
+ <m-radios name="plan" value="free" aria-label="Plan">
163
+ <m-radio value="free">Free</m-radio>
164
+ <m-radio value="pro">Pro</m-radio>
165
+ </m-radios>
166
+ <m-switch name="news" checked>Newsletter</m-switch>
167
+ <m-button type="submit">Sign up</m-button>
168
+ </form>
169
+
149
170
  <m-tabs value="songs">
150
171
  <m-tab value="songs">Songs</m-tab>
151
172
  <m-tab value="albums">Albums</m-tab>
152
173
  </m-tabs>
153
174
  ```
154
175
 
155
- The page still loads mtrl's base styles and a theme (see [Styles](#styles)); the elements render in shadow DOM and pick up the theme's tokens. Attributes are defaults and properties the live state, as on native controls; form controls take part in forms, reset and validation.
176
+ The page still loads mtrl's base styles and a theme (see [Styles](#styles)); the elements render in shadow DOM and pick up the theme's tokens. Attributes are defaults and properties the live state, as on native controls: a `checked` or `value` attribute sets the state until the user or a script changes it, and `form.reset()` goes back to it. Form controls take part in forms (their host's `name`), reset, validation and back-navigation restore.
156
177
 
157
178
  | Framework | Import | Two-way binding |
158
179
  |-----------|--------|-----------------|
@@ -161,7 +182,7 @@ The page still loads mtrl's base styles and a theme (see [Styles](#styles)); the
161
182
  | Svelte 5 | `import { Switch } from 'mtrl/svelte'` | `bind:checked` |
162
183
  | Solid | `import { Switch } from 'mtrl/solid'` | `checked` + `onChange` |
163
184
 
164
- Each framework is an optional peer dependency; mtrl installs none of them. All adapters render on the server and hydrate.
185
+ Each framework is an optional peer dependency; mtrl installs none of them. All adapters render on the server and hydrate. Angular apps use the elements directly, with `CUSTOM_ELEMENTS_SCHEMA`.
165
186
 
166
187
  ## Building your own components
167
188
 
package/dist/README.md CHANGED
@@ -136,23 +136,44 @@ Button progress and card actions load on demand; enable code splitting in your b
136
136
 
137
137
  ## Web components and frameworks (experimental)
138
138
 
139
- From `0.10.0-next.1` (`npm install mtrl@next`), components also ship as custom elements, with thin adapters for React, Vue, Svelte and Solid. Button, switch and tabs are available so far; more follow through 0.10.x. The API may still change.
139
+ From the 0.10.0 prereleases (`npm install mtrl@next`), every component also ships as a custom element, with thin adapters for React, Vue, Svelte and Solid. The API may still change before 1.0.
140
+
141
+ | Kind | Elements | Framework components (Vue: `M` prefix, e.g. `MDialog`) |
142
+ |------|----------|--------------------------------------------------------|
143
+ | Actions | `<m-button>`, `<m-icon-button>`, `<m-fab>`, `<m-extended-fab>`, `<m-split-button>`, `<m-button-group>` + `<m-button-group-item>` | `Button`, `IconButton`, `Fab`, `ExtendedFab`, `SplitButton`, `ButtonGroup`, `ButtonGroupItem` |
144
+ | Form controls | `<m-checkbox>`, `<m-switch>`, `<m-radios>` + `<m-radio>`, `<m-slider>`, `<m-textfield>`, `<m-select>` + `<m-select-option>`, `<m-search>` + `<m-search-suggestion>`, `<m-datepicker>`, `<m-timepicker>` | `Checkbox`, `Switch`, `Radios`, `Radio`, `Slider`, `Textfield`, `Select`, `SelectOption`, `Search`, `SearchSuggestion`, `Datepicker`, `Timepicker` |
145
+ | Selection | `<m-chips>` + `<m-chip>`, `<m-tabs>` + `<m-tab>`, `<m-list>` + `<m-list-item>` | `Chips`, `Chip`, `Tabs`, `Tab`, `List`, `ListItem` |
146
+ | Navigation | `<m-navigation-rail>` + `<m-navigation-rail-item>`, `<m-drawer>` + `<m-drawer-item>`, `<m-top-app-bar>`, `<m-bottom-app-bar>` | `NavigationRail`, `NavigationRailItem`, `Drawer`, `DrawerItem`, `TopAppBar`, `BottomAppBar` |
147
+ | Overlays (top layer) | `<m-menu>` + `<m-menu-item>`, `<m-dialog>`, `<m-bottom-sheet>`, `<m-side-sheet>`, `<m-tooltip>`, `<m-snackbar>` | `Menu`, `MenuItem`, `Dialog`, `BottomSheet`, `SideSheet`, `Tooltip`, `Snackbar` |
148
+ | Containment | `<m-card>`, `<m-carousel>` + `<m-carousel-item>` | `Card`, `Carousel`, `CarouselItem` |
149
+ | Indicators | `<m-progress>`, `<m-loading-indicator>`, `<m-badge>`, `<m-divider>` | `Progress`, `LoadingIndicator`, `Badge`, `Divider` |
150
+
151
+ Overlays open in the browser's top layer (`showModal()` or `popover`) from inside their own shadow root, so they keep their styles, trap focus and make the page inert where M3 says so. Form controls are form-associated: `name`, the form value, reset, validation, `<label for>` and back-navigation restore work as on native controls.
140
152
 
141
153
  ```html
142
154
  <script type="module">
143
155
  import 'mtrl/elements/css'; // the elements' styles
144
156
  import { defineAll } from 'mtrl/elements';
145
- defineAll(); // registers <m-button>, <m-switch>, <m-tabs>, <m-tab>
157
+ defineAll(); // registers every element (<m-button>, <m-switch>, …)
146
158
  </script>
147
159
 
148
- <m-switch name="wifi" checked>Wi-Fi</m-switch>
160
+ <form>
161
+ <m-textfield name="email" type="email" label="Email" required></m-textfield>
162
+ <m-radios name="plan" value="free" aria-label="Plan">
163
+ <m-radio value="free">Free</m-radio>
164
+ <m-radio value="pro">Pro</m-radio>
165
+ </m-radios>
166
+ <m-switch name="news" checked>Newsletter</m-switch>
167
+ <m-button type="submit">Sign up</m-button>
168
+ </form>
169
+
149
170
  <m-tabs value="songs">
150
171
  <m-tab value="songs">Songs</m-tab>
151
172
  <m-tab value="albums">Albums</m-tab>
152
173
  </m-tabs>
153
174
  ```
154
175
 
155
- The page still loads mtrl's base styles and a theme (see [Styles](#styles)); the elements render in shadow DOM and pick up the theme's tokens. Attributes are defaults and properties the live state, as on native controls; form controls take part in forms, reset and validation.
176
+ The page still loads mtrl's base styles and a theme (see [Styles](#styles)); the elements render in shadow DOM and pick up the theme's tokens. Attributes are defaults and properties the live state, as on native controls: a `checked` or `value` attribute sets the state until the user or a script changes it, and `form.reset()` goes back to it. Form controls take part in forms (their host's `name`), reset, validation and back-navigation restore.
156
177
 
157
178
  | Framework | Import | Two-way binding |
158
179
  |-----------|--------|-----------------|
@@ -161,7 +182,7 @@ The page still loads mtrl's base styles and a theme (see [Styles](#styles)); the
161
182
  | Svelte 5 | `import { Switch } from 'mtrl/svelte'` | `bind:checked` |
162
183
  | Solid | `import { Switch } from 'mtrl/solid'` | `checked` + `onChange` |
163
184
 
164
- Each framework is an optional peer dependency; mtrl installs none of them. All adapters render on the server and hydrate.
185
+ Each framework is an optional peer dependency; mtrl installs none of them. All adapters render on the server and hydrate. Angular apps use the elements directly, with `CUSTOM_ELEMENTS_SCHEMA`.
165
186
 
166
187
  ## Building your own components
167
188
 
@@ -1,17 +1,24 @@
1
1
  import { createComponentConfig, createElementConfig, } from "../../core/config/component.js";
2
- import { BOTTOM_SHEET_DEFAULTS, BOTTOM_SHEET_STATES, } from "./constants.js";
2
+ import { BOTTOM_SHEET_DEFAULTS, BOTTOM_SHEET_STATES, BOTTOM_SHEET_VARIANTS, } from "./constants.js";
3
+ import { supportsTopLayer } from "../../core/dom/layer.js";
3
4
  export const defaultConfig = {
4
5
  variant: BOTTOM_SHEET_DEFAULTS.VARIANT,
5
6
  dragHandle: BOTTOM_SHEET_DEFAULTS.DRAG_HANDLE,
6
- peekHeight: BOTTOM_SHEET_DEFAULTS.PEEK_HEIGHT,
7
7
  maxWidth: BOTTOM_SHEET_DEFAULTS.MAX_WIDTH,
8
8
  initialState: BOTTOM_SHEET_STATES.HIDDEN,
9
9
  closeOnScrimClick: BOTTOM_SHEET_DEFAULTS.CLOSE_ON_SCRIM_CLICK,
10
10
  closeOnEscape: BOTTOM_SHEET_DEFAULTS.CLOSE_ON_ESCAPE,
11
11
  };
12
- export const createBaseConfig = (config = {}) => createComponentConfig(defaultConfig, config, "bottom-sheet");
12
+ export const createBaseConfig = (config = {}) => {
13
+ const merged = createComponentConfig(defaultConfig, config, "bottom-sheet");
14
+ if (merged.layer === "top" &&
15
+ (merged.variant !== BOTTOM_SHEET_VARIANTS.MODAL || !supportsTopLayer("modal"))) {
16
+ merged.layer = undefined;
17
+ }
18
+ return merged;
19
+ };
13
20
  export const getElementConfig = (config) => createElementConfig(config, {
14
- tag: "div",
21
+ tag: config.layer === "top" ? "dialog" : "div",
15
22
  className: [config.class].filter(Boolean),
16
23
  attributes: {
17
24
  "aria-hidden": "true",
@@ -25,8 +25,6 @@ export declare const BOTTOM_SHEET_STATES: {
25
25
  */
26
26
  export declare const BOTTOM_SHEET_DEFAULTS: {
27
27
  readonly VARIANT: "modal";
28
- /** BottomSheetDefaults.SheetPeekHeight */
29
- readonly PEEK_HEIGHT: 56;
30
28
  /** BottomSheetDefaults.SheetMaxWidth; wider viewports centre the sheet */
31
29
  readonly MAX_WIDTH: 640;
32
30
  /** A drag handle is part of the anatomy, so it is on unless asked otherwise */
@@ -9,7 +9,6 @@ export const BOTTOM_SHEET_STATES = {
9
9
  };
10
10
  export const BOTTOM_SHEET_DEFAULTS = {
11
11
  VARIANT: BOTTOM_SHEET_VARIANTS.MODAL,
12
- PEEK_HEIGHT: 56,
13
12
  MAX_WIDTH: 640,
14
13
  DRAG_HANDLE: true,
15
14
  CLOSE_ON_SCRIM_CLICK: true,
@@ -1,8 +1,11 @@
1
1
  import { BOTTOM_SHEET_CLASSES, BOTTOM_SHEET_EVENTS, BOTTOM_SHEET_STATES, BOTTOM_SHEET_VARIANTS, } from "../constants.js";
2
+ import { deepActiveElement, wrapTab } from "../../../core/dom/focus.js";
3
+ import { hideFromTopLayer, onTopLayerClose, showInTopLayer, } from "../../../core/dom/layer.js";
2
4
  export const withState = (config) => (component) => {
3
5
  const { element, getClass, structure } = component;
4
6
  const root = getClass(BOTTOM_SHEET_CLASSES.ROOT);
5
7
  const isModal = config.variant === BOTTOM_SHEET_VARIANTS.MODAL;
8
+ const top = config.layer === "top";
6
9
  let state = config.initialState ?? BOTTOM_SHEET_STATES.HIDDEN;
7
10
  let previouslyFocused = null;
8
11
  const applyState = (next) => {
@@ -35,12 +38,27 @@ export const withState = (config) => (component) => {
35
38
  if (config.closeOnScrimClick)
36
39
  close();
37
40
  };
41
+ const handleBackdropClick = (event) => {
42
+ if (event.target === element)
43
+ handleScrimClick();
44
+ };
45
+ const handleTab = (event) => wrapTab(element, event);
46
+ const handleCancel = (event) => {
47
+ event.preventDefault();
48
+ if (config.closeOnEscape)
49
+ close();
50
+ };
38
51
  function open(to = BOTTOM_SHEET_STATES.PARTIAL) {
39
52
  if (state === to)
40
53
  return;
41
54
  const wasHidden = state === BOTTOM_SHEET_STATES.HIDDEN;
42
55
  if (wasHidden && isModal) {
43
- previouslyFocused = document.activeElement;
56
+ previouslyFocused = deepActiveElement();
57
+ }
58
+ if (wasHidden && top) {
59
+ if (!showInTopLayer(element, { kind: "modal" }))
60
+ element.setAttribute("open", "");
61
+ void element.offsetWidth;
44
62
  }
45
63
  setState(to);
46
64
  if (wasHidden) {
@@ -53,6 +71,8 @@ export const withState = (config) => (component) => {
53
71
  if (state === BOTTOM_SHEET_STATES.HIDDEN)
54
72
  return;
55
73
  setState(BOTTOM_SHEET_STATES.HIDDEN);
74
+ if (top)
75
+ hideFromTopLayer(element);
56
76
  if (isModal && previouslyFocused?.isConnected) {
57
77
  previouslyFocused.focus();
58
78
  previouslyFocused = null;
@@ -60,8 +80,19 @@ export const withState = (config) => (component) => {
60
80
  component.emit(BOTTOM_SHEET_EVENTS.CLOSE);
61
81
  }
62
82
  applyState(state);
63
- if (config.closeOnEscape)
83
+ let stopCloses = null;
84
+ if (top) {
85
+ element.addEventListener("cancel", handleCancel);
86
+ element.addEventListener("click", handleBackdropClick);
87
+ element.addEventListener("keydown", handleTab);
88
+ stopCloses = onTopLayerClose(element, close);
89
+ if (state !== BOTTOM_SHEET_STATES.HIDDEN && element.isConnected) {
90
+ showInTopLayer(element, { kind: "modal" });
91
+ }
92
+ }
93
+ else if (config.closeOnEscape) {
64
94
  document.addEventListener("keydown", handleKeydown);
95
+ }
65
96
  structure.scrim?.addEventListener("click", handleScrimClick);
66
97
  return {
67
98
  ...component,
@@ -76,6 +107,12 @@ export const withState = (config) => (component) => {
76
107
  release: () => {
77
108
  document.removeEventListener("keydown", handleKeydown);
78
109
  structure.scrim?.removeEventListener("click", handleScrimClick);
110
+ element.removeEventListener("cancel", handleCancel);
111
+ element.removeEventListener("click", handleBackdropClick);
112
+ element.removeEventListener("keydown", handleTab);
113
+ stopCloses?.();
114
+ if (top)
115
+ hideFromTopLayer(element);
79
116
  },
80
117
  },
81
118
  };
@@ -1,4 +1,5 @@
1
1
  import { BOTTOM_SHEET_CLASSES, BOTTOM_SHEET_VARIANTS } from "../constants.js";
2
+ import { PREFIX } from "../../../core/config.js";
2
3
  import { setHTML } from "../../../core/dom/html.js";
3
4
  export const withStructure = (config) => (component) => {
4
5
  const { element, getClass } = component;
@@ -6,7 +7,7 @@ export const withStructure = (config) => (component) => {
6
7
  element.classList.add(getClass(BOTTOM_SHEET_CLASSES.ROOT));
7
8
  element.classList.add(`${getClass(BOTTOM_SHEET_CLASSES.ROOT)}--${config.variant}`);
8
9
  let scrim = null;
9
- if (isModal) {
10
+ if (isModal && config.layer !== "top") {
10
11
  scrim = document.createElement("div");
11
12
  scrim.className = getClass(BOTTOM_SHEET_CLASSES.SCRIM);
12
13
  element.appendChild(scrim);
@@ -20,6 +21,9 @@ export const withStructure = (config) => (component) => {
20
21
  if (config.maxWidth) {
21
22
  container.style.maxWidth = `${config.maxWidth}px`;
22
23
  }
24
+ if (config.peekHeight) {
25
+ container.style.setProperty(`--${PREFIX}-bottom-sheet-peek-height`, `${config.peekHeight}px`);
26
+ }
23
27
  let handle = null;
24
28
  if (config.dragHandle) {
25
29
  handle = document.createElement("div");
@@ -44,8 +44,9 @@ export interface BottomSheetConfig {
44
44
  */
45
45
  dragHandle?: boolean;
46
46
  /**
47
- * Height of the partially expanded state, in pixels.
48
- * @default 56
47
+ * Height of the partially expanded state, in pixels: a sheet with more
48
+ * content shows this much of it until it is expanded. Unset, the partial
49
+ * sheet shows its content up to half the screen.
49
50
  */
50
51
  peekHeight?: number;
51
52
  /**
@@ -72,6 +73,18 @@ export interface BottomSheetConfig {
72
73
  closeOnEscape?: boolean;
73
74
  /** Where to mount the sheet. Defaults to `document.body` */
74
75
  container?: HTMLElement;
76
+ /**
77
+ * `"top"` makes a modal sheet's root a `<dialog>` element, rendered in
78
+ * `container` like any sheet, and opens it with `showModal()`: in the
79
+ * browser's top layer, above every z-index and outside any clipping
80
+ * ancestor, with the rest of the page inert. The scrim is its `::backdrop`,
81
+ * and Escape reaches the sheet as the dialog's `cancel` event. The sheet
82
+ * slides as without it.
83
+ *
84
+ * A standard sheet stays in the page, and where the browser has no
85
+ * `showModal()` the sheet behaves as without it.
86
+ */
87
+ layer?: "top";
75
88
  /** Handlers registered at creation, equivalent to calling `on` for each */
76
89
  on?: BottomSheetEventHandlers;
77
90
  /** Extra classes for the root element */
@@ -1,6 +1,7 @@
1
1
  import { getCleanup } from "../../../core/compose/cleanup.js";
2
2
  import createChip from "../chip/chip.js";
3
3
  import { CHIPS_EVENTS } from "../constants.js";
4
+ import { activeElementOf } from "../../../core/dom/focus.js";
4
5
  export const withController = (config) => (component) => {
5
6
  const eventListeners = {
6
7
  change: [],
@@ -157,7 +158,7 @@ export const withController = (config) => (component) => {
157
158
  : component.chipInstances.indexOf(chipOrIndex);
158
159
  if (index >= 0 && index < component.chipInstances.length) {
159
160
  const chip = component.chipInstances[index];
160
- const hadFocus = chip.element.contains(document.activeElement);
161
+ const hadFocus = chip.element.contains(activeElementOf(chip.element));
161
162
  dispatchEvent(CHIPS_EVENTS.REMOVE, chip);
162
163
  chip.element.removeEventListener("keydown", handleKeyboardNavigation);
163
164
  chip.destroy();
@@ -101,4 +101,5 @@ export declare const DATEPICKER_ICONS: {
101
101
  readonly edit: "<svg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"m3 17.25 11.06-11.06 3.75 3.75L6.75 21H3v-3.75ZM20.71 7.04l-1.83 1.83-3.75-3.75 1.83-1.83a1 1 0 0 1 1.41 0l2.34 2.34a1 1 0 0 1 0 1.41Z\"/></svg>";
102
102
  readonly previous: "<svg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"m15.4 7.4-1.4-1.4-6 6 6 6 1.4-1.4-4.6-4.6Z\"/></svg>";
103
103
  readonly next: "<svg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"m8.6 7.4 1.4-1.4 6 6-6 6-1.4-1.4 4.6-4.6Z\"/></svg>";
104
+ readonly close: "<svg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12Z\"/></svg>";
104
105
  };
@@ -53,4 +53,5 @@ export const DATEPICKER_ICONS = {
53
53
  edit: '<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" aria-hidden="true"><path d="m3 17.25 11.06-11.06 3.75 3.75L6.75 21H3v-3.75ZM20.71 7.04l-1.83 1.83-3.75-3.75 1.83-1.83a1 1 0 0 1 1.41 0l2.34 2.34a1 1 0 0 1 0 1.41Z"/></svg>',
54
54
  previous: '<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" aria-hidden="true"><path d="m15.4 7.4-1.4-1.4-6 6 6 6 1.4-1.4-4.6-4.6Z"/></svg>',
55
55
  next: '<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" aria-hidden="true"><path d="m8.6 7.4 1.4-1.4 6 6-6 6-1.4-1.4 4.6-4.6Z"/></svg>',
56
+ close: '<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" aria-hidden="true"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12Z"/></svg>',
56
57
  };
@@ -3,6 +3,7 @@ import { createBase, withElement } from "../../core/compose/component.js";
3
3
  import { withEvents, withDisabled, withLifecycle } from "../../core/compose/features/index.js";
4
4
  import { createElement } from "../../core/dom/create.js";
5
5
  import { setHTML } from "../../core/dom/html.js";
6
+ import { activeElementOf, deepActiveElement } from "../../core/dom/focus.js";
6
7
  import { DATEPICKER_ICONS } from "./constants.js";
7
8
  import { PREFIX } from "../../core/config.js";
8
9
  import { createBaseConfig, getContainerConfig } from "./config.js";
@@ -33,10 +34,10 @@ const createDatePicker = (config = {}) => {
33
34
  const today = parseDate(new Date());
34
35
  const state = {
35
36
  id, prefix: PREFIX, label: settings.label || "Select date", selectedDate: null, rangeEndDate: null,
36
- currentView: ["month", "year"].includes(settings.initialView ?? "") ? settings.initialView : "day",
37
+ currentView: settings.variant !== "fullscreen" && ["month", "year"].includes(settings.initialView ?? "") ? settings.initialView : "day",
37
38
  currentMonth: today.getMonth(), currentYear: today.getFullYear(), focusedDate: today,
38
39
  minDate: parseDate(settings.minDate ?? null), maxDate: parseDate(settings.maxDate ?? null),
39
- dateFormat: settings.dateFormat || "MM/DD/YYYY", variant: settings.variant === "modal" || settings.variant === "modal-input" ? settings.variant : "docked",
40
+ dateFormat: settings.dateFormat || "MM/DD/YYYY", variant: settings.variant === "modal" || settings.variant === "modal-input" || settings.variant === "fullscreen" ? settings.variant : "docked",
40
41
  selectionMode: settings.selectionMode === "range" ? "range" : "single", inputMode: settings.variant === "modal-input",
41
42
  specialDates: (settings.specialDates ?? []).map(item => ({ ...item })),
42
43
  isAllowed: date => (!state.minDate || date >= state.minDate) && (!state.maxDate || date <= state.maxDate) && !state.specialDates.some(item => { const special = parseDate(item.date); return item.disabled && special && isSameDay(date, special); }),
@@ -73,6 +74,8 @@ const createDatePicker = (config = {}) => {
73
74
  dialog.id = `${id}-dialog`;
74
75
  dialog.className = cls("calendar");
75
76
  dialog.classList.add(base.getClass(`datepicker--${modal ? "modal" : "docked"}`));
77
+ if (state.variant === "fullscreen")
78
+ dialog.classList.add(base.getClass("datepicker--fullscreen"));
76
79
  dialog.setAttribute("aria-modal", String(modal));
77
80
  if (settings.animate)
78
81
  dialog.classList.add(base.getClass("datepicker--animate"));
@@ -105,11 +108,66 @@ const createDatePicker = (config = {}) => {
105
108
  const element = track();
106
109
  if (element?.clientWidth)
107
110
  element.scrollLeft = (rtl(element) ? -1 : 1) * element.clientWidth;
111
+ const years = dialog.querySelector(`.${cls("years")}`);
112
+ const year = years?.querySelector('[aria-pressed="true"]');
113
+ if (years && year)
114
+ years.scrollTop = year.offsetTop - (years.clientHeight - year.offsetHeight) / 2;
115
+ };
116
+ const monthIndex = (date) => date.getFullYear() * 12 + date.getMonth();
117
+ const fromIndex = (index) => new Date(Math.floor(index / 12), index % 12, 1);
118
+ const list = () => dialog.querySelector(`.${cls("list")}`);
119
+ const limits = () => [state.minDate ? monthIndex(state.minDate) : -Infinity, state.maxDate ? monthIndex(state.maxDate) : Infinity];
120
+ let shiftBy = null;
121
+ const ensureWindow = () => {
122
+ if (state.variant !== "fullscreen")
123
+ return;
124
+ const focus = state.currentYear * 12 + state.currentMonth;
125
+ const start = state.listStart ? monthIndex(state.listStart) : NaN;
126
+ if (start <= focus && focus < start + (state.listLength ?? 0))
127
+ return;
128
+ const [min, max] = limits();
129
+ const first = Math.max(min, focus - 12), last = Math.min(max, focus + 12);
130
+ state.listStart = fromIndex(first);
131
+ state.listLength = last - first + 1;
132
+ };
133
+ const extend = (element) => {
134
+ const [min, max] = limits();
135
+ const start = monthIndex(state.listStart), end = start + state.listLength - 1;
136
+ const margin = element.clientHeight;
137
+ if (element.scrollTop < margin && start > min) {
138
+ const first = Math.max(min, start - 12);
139
+ state.listStart = fromIndex(first);
140
+ state.listLength = end - first + 1;
141
+ shiftBy = element.scrollHeight;
142
+ }
143
+ else if (element.scrollHeight - element.scrollTop - element.clientHeight < margin && end < max) {
144
+ state.listLength = Math.min(max, end + 12) - start + 1;
145
+ }
146
+ else
147
+ return;
148
+ const focused = activeElementOf(dialog);
149
+ const active = focused instanceof HTMLElement ? focused.dataset.date : undefined;
150
+ render();
151
+ if (active)
152
+ dialog.querySelector(`[data-date="${active}"]`)?.focus({ preventScroll: true });
153
+ };
154
+ const reveal = () => {
155
+ const element = list();
156
+ const section = element?.querySelector(`[data-month-key="${state.currentYear}-${state.currentMonth}"]`);
157
+ if (element && section)
158
+ element.scrollTop = section.offsetTop;
108
159
  };
109
160
  const render = (focus = false, action) => {
110
161
  if (!opened || destroyed)
111
162
  return;
163
+ ensureWindow();
164
+ const previous = list();
165
+ const kept = previous && { top: previous.scrollTop, height: previous.scrollHeight };
112
166
  dialog.replaceChildren(renderCalendar(state), announcement);
167
+ const current = list();
168
+ if (current && kept)
169
+ current.scrollTop = kept.top + (shiftBy === null ? 0 : current.scrollHeight - kept.height);
170
+ shiftBy = null;
113
171
  centre();
114
172
  announcement.textContent = new Date(state.currentYear, state.currentMonth, 1).toLocaleDateString("en-US", { month: "long", year: "numeric" });
115
173
  if (action)
@@ -150,6 +208,7 @@ const createDatePicker = (config = {}) => {
150
208
  opened = true;
151
209
  resetDraft();
152
210
  state.inputMode = state.variant === "modal-input";
211
+ state.listStart = undefined;
153
212
  let initial = committed ?? today;
154
213
  if (state.minDate && initial < state.minDate)
155
214
  initial = state.minDate;
@@ -158,7 +217,8 @@ const createDatePicker = (config = {}) => {
158
217
  if (!navigationRequested)
159
218
  setDisplayDate(initial);
160
219
  navigationRequested = false;
161
- returnFocus = doc.activeElement instanceof HTMLElement && doc.activeElement !== doc.body ? doc.activeElement : trigger;
220
+ const opener = deepActiveElement();
221
+ returnFocus = opener instanceof HTMLElement && opener !== doc.body ? opener : trigger;
162
222
  render();
163
223
  if (modal && typeof dialog.showModal === "function" && dialog.isConnected)
164
224
  dialog.showModal();
@@ -169,6 +229,7 @@ const createDatePicker = (config = {}) => {
169
229
  if (modal)
170
230
  unlock = lockScroll(doc);
171
231
  centre();
232
+ reveal();
172
233
  trigger.setAttribute("aria-expanded", "true");
173
234
  focusCurrent();
174
235
  base.emit("open", { value: getValue() });
@@ -256,7 +317,7 @@ const createDatePicker = (config = {}) => {
256
317
  render(true);
257
318
  };
258
319
  const navigate = (amount) => {
259
- const count = state.currentView === "day" ? amount : state.currentView === "month" ? amount * 12 : amount * 12 * 21;
320
+ const count = state.currentView === "day" ? amount : amount * 12;
260
321
  const anchor = new Date(state.currentYear, state.currentMonth, 1);
261
322
  anchor.setDate(Math.min(state.focusedDate.getDate(), new Date(state.currentYear, state.currentMonth + 1, 0).getDate()));
262
323
  setDisplayDate(addMonths(anchor, count));
@@ -349,11 +410,12 @@ const createDatePicker = (config = {}) => {
349
410
  if (event.key === "Tab" && modal) {
350
411
  const elements = Array.from(dialog.querySelectorAll('button:not(:disabled):not([tabindex="-1"]), input:not(:disabled)'));
351
412
  const first = elements[0], last = elements.at(-1);
352
- if (event.shiftKey && doc.activeElement === first) {
413
+ const focused = activeElementOf(dialog);
414
+ if (event.shiftKey && focused === first) {
353
415
  event.preventDefault();
354
416
  last?.focus();
355
417
  }
356
- else if (!event.shiftKey && doc.activeElement === last) {
418
+ else if (!event.shiftKey && focused === last) {
357
419
  event.preventDefault();
358
420
  first?.focus();
359
421
  }
@@ -363,10 +425,11 @@ const createDatePicker = (config = {}) => {
363
425
  if (target?.dataset.date) {
364
426
  const date = parseDate(target.dataset.date);
365
427
  let next;
428
+ const ahead = rtl(dialog) ? -1 : 1;
366
429
  if (event.key === "ArrowLeft")
367
- next = addDays(date, -1);
430
+ next = addDays(date, -ahead);
368
431
  else if (event.key === "ArrowRight")
369
- next = addDays(date, 1);
432
+ next = addDays(date, ahead);
370
433
  else if (event.key === "ArrowUp")
371
434
  next = addDays(date, -7);
372
435
  else if (event.key === "ArrowDown")
@@ -395,7 +458,8 @@ const createDatePicker = (config = {}) => {
395
458
  else if (target?.hasAttribute('data-month') || target?.hasAttribute('data-year')) {
396
459
  const buttons = Array.from(dialog.querySelectorAll('[data-month], [data-year]'));
397
460
  let index = buttons.indexOf(target);
398
- const delta = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -3, ArrowDown: 3 }[event.key];
461
+ const ahead = rtl(dialog) ? -1 : 1;
462
+ const delta = { ArrowLeft: -ahead, ArrowRight: ahead, ArrowUp: -3, ArrowDown: 3 }[event.key];
399
463
  if (event.key === "Home")
400
464
  index = 0;
401
465
  else if (event.key === "End")
@@ -425,11 +489,16 @@ const createDatePicker = (config = {}) => {
425
489
  const page = Math.round(Math.abs(element.scrollLeft) / element.clientWidth) - 1;
426
490
  if (!page)
427
491
  return;
428
- const active = doc.activeElement instanceof HTMLElement && dialog.contains(doc.activeElement) ? doc.activeElement : null;
492
+ const focused = activeElementOf(dialog);
493
+ const active = focused instanceof HTMLElement && dialog.contains(focused) ? focused : null;
429
494
  navigate(page);
430
495
  render(!!active?.dataset.date, active?.dataset.action);
431
496
  };
432
497
  const onScroll = (event) => {
498
+ if (event.type === "scroll" && event.target instanceof HTMLElement && event.target === list()) {
499
+ extend(event.target);
500
+ return;
501
+ }
433
502
  if (!(event.target instanceof HTMLElement) || event.target !== track())
434
503
  return;
435
504
  if (event.type === "scrollend") {