@shortfuse/materialdesignweb 0.4.0 → 0.7.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 (452) hide show
  1. package/README.md +155 -79
  2. package/bin/generate-css.js +12 -0
  3. package/components/Badge.css +30 -0
  4. package/components/Badge.js +15 -0
  5. package/components/Body.css +14 -0
  6. package/components/Body.js +7 -0
  7. package/components/BottomAppBar.css +23 -0
  8. package/components/BottomAppBar.js +25 -0
  9. package/components/Box.css +31 -0
  10. package/components/Box.js +24 -0
  11. package/components/Button.css +146 -0
  12. package/components/Button.js +95 -0
  13. package/components/Button.md +61 -0
  14. package/components/Card.css +109 -0
  15. package/components/Card.js +82 -0
  16. package/components/Checkbox.css +77 -0
  17. package/components/Checkbox.js +59 -0
  18. package/components/CheckboxIcon.css +89 -0
  19. package/components/CheckboxIcon.js +41 -0
  20. package/components/Chip.css +35 -0
  21. package/components/Chip.js +22 -0
  22. package/components/Dialog.css +235 -0
  23. package/components/Dialog.js +327 -0
  24. package/components/DialogActions.js +13 -0
  25. package/components/Divider.css +41 -0
  26. package/components/Divider.js +13 -0
  27. package/components/ExtendedFab.css +24 -0
  28. package/components/ExtendedFab.js +11 -0
  29. package/components/Fab.css +23 -0
  30. package/components/Fab.js +26 -0
  31. package/components/FilterChip.css +80 -0
  32. package/components/FilterChip.js +51 -0
  33. package/components/Headline.css +14 -0
  34. package/components/Headline.js +33 -0
  35. package/components/Icon.css +76 -0
  36. package/components/Icon.js +174 -0
  37. package/components/IconButton.css +151 -0
  38. package/components/IconButton.js +65 -0
  39. package/components/Input.js +16 -0
  40. package/components/Label.css +14 -0
  41. package/components/Label.js +7 -0
  42. package/components/Layout.css +19 -0
  43. package/components/Layout.js +12 -0
  44. package/components/List.css +12 -0
  45. package/components/List.js +17 -0
  46. package/components/ListItem.css +224 -0
  47. package/components/ListItem.js +112 -0
  48. package/components/ListOption.css +34 -0
  49. package/components/ListOption.js +122 -0
  50. package/components/ListSelect.css +9 -0
  51. package/components/ListSelect.js +206 -0
  52. package/components/Menu.css +171 -0
  53. package/components/Menu.js +470 -0
  54. package/components/MenuItem.css +53 -0
  55. package/components/MenuItem.js +215 -0
  56. package/components/Nav.css +17 -0
  57. package/components/Nav.js +23 -0
  58. package/components/NavBar.css +34 -0
  59. package/components/NavBar.js +88 -0
  60. package/components/NavBarItem.css +41 -0
  61. package/components/NavBarItem.js +7 -0
  62. package/components/NavDrawer.css +31 -0
  63. package/components/NavDrawer.js +13 -0
  64. package/components/NavDrawerItem.css +42 -0
  65. package/components/NavDrawerItem.js +12 -0
  66. package/components/NavItem.css +181 -0
  67. package/components/NavItem.js +83 -0
  68. package/components/NavRail.css +47 -0
  69. package/components/NavRail.js +17 -0
  70. package/components/NavRailItem.css +25 -0
  71. package/components/NavRailItem.js +7 -0
  72. package/components/Option.js +91 -0
  73. package/components/Outline.css +138 -0
  74. package/components/Pane.css +261 -0
  75. package/components/Pane.js +21 -0
  76. package/components/Progress.css +74 -0
  77. package/components/Progress.js +67 -0
  78. package/components/ProgressCircle.css +226 -0
  79. package/components/ProgressLine.css +155 -0
  80. package/components/Radio.css +83 -0
  81. package/components/Radio.js +42 -0
  82. package/components/RadioIcon.css +73 -0
  83. package/components/RadioIcon.js +37 -0
  84. package/components/Ripple.css +74 -0
  85. package/components/Ripple.js +114 -0
  86. package/components/SegmentedButton.css +94 -0
  87. package/components/SegmentedButton.js +49 -0
  88. package/components/SegmentedButtonGroup.css +12 -0
  89. package/components/SegmentedButtonGroup.js +44 -0
  90. package/components/Select.css +52 -0
  91. package/components/Select.js +71 -0
  92. package/components/Shape.css +132 -0
  93. package/components/Shape.js +25 -0
  94. package/components/Slider.css +306 -0
  95. package/components/Slider.js +206 -0
  96. package/components/Snackbar.css +80 -0
  97. package/components/Snackbar.js +75 -0
  98. package/components/Surface.css +10 -0
  99. package/components/Surface.js +23 -0
  100. package/components/Switch.css +63 -0
  101. package/components/Switch.js +127 -0
  102. package/components/SwitchIcon.css +177 -0
  103. package/components/SwitchIcon.js +89 -0
  104. package/components/SwitchIconAnimations.css +89 -0
  105. package/components/Tab.css +85 -0
  106. package/components/Tab.js +103 -0
  107. package/components/TabContent.js +151 -0
  108. package/components/TabList.css +129 -0
  109. package/components/TabList.js +309 -0
  110. package/components/TabPanel.js +37 -0
  111. package/components/TextArea.css +93 -0
  112. package/components/TextArea.js +229 -0
  113. package/components/Title.css +14 -0
  114. package/components/Title.js +15 -0
  115. package/components/Tooltip.css +40 -0
  116. package/components/Tooltip.js +22 -0
  117. package/components/TopAppBar.css +209 -0
  118. package/components/TopAppBar.js +201 -0
  119. package/core/Composition.js +988 -0
  120. package/core/CustomElement.js +844 -0
  121. package/core/ICustomElement.d.ts +288 -0
  122. package/core/ICustomElement.js +1 -0
  123. package/core/css.js +51 -0
  124. package/core/customTypes.js +125 -0
  125. package/core/dom.js +56 -245
  126. package/core/identify.js +40 -0
  127. package/core/observe.js +410 -0
  128. package/core/template.js +121 -0
  129. package/core/typings.d.ts +135 -0
  130. package/core/typings.js +1 -0
  131. package/mixins/AriaReflectorMixin.js +42 -0
  132. package/mixins/AriaToolbarMixin.js +13 -0
  133. package/mixins/ControlMixin.css +57 -0
  134. package/mixins/ControlMixin.js +212 -0
  135. package/mixins/DensityMixin.css +40 -0
  136. package/mixins/DensityMixin.js +11 -0
  137. package/mixins/FlexableMixin.css +79 -0
  138. package/mixins/FlexableMixin.js +32 -0
  139. package/mixins/FormAssociatedMixin.js +170 -0
  140. package/mixins/InputMixin.js +335 -0
  141. package/mixins/KeyboardNavMixin.js +244 -0
  142. package/mixins/RTLObserverMixin.js +35 -0
  143. package/mixins/ResizeObserverMixin.js +38 -0
  144. package/mixins/RippleMixin.css +12 -0
  145. package/mixins/RippleMixin.js +115 -0
  146. package/mixins/ScrollListenerMixin.js +100 -0
  147. package/mixins/ShapeMixin.css +135 -0
  148. package/mixins/ShapeMixin.js +31 -0
  149. package/mixins/StateMixin.css +82 -0
  150. package/mixins/StateMixin.js +114 -0
  151. package/mixins/SurfaceMixin.css +150 -0
  152. package/mixins/SurfaceMixin.js +32 -0
  153. package/mixins/TextFieldMixin.css +657 -0
  154. package/mixins/TextFieldMixin.js +121 -0
  155. package/mixins/ThemableMixin.css +204 -0
  156. package/mixins/ThemableMixin.js +16 -0
  157. package/mixins/TooltipTriggerMixin.css +27 -0
  158. package/mixins/TooltipTriggerMixin.js +366 -0
  159. package/mixins/TouchTargetMixin.css +26 -0
  160. package/mixins/TouchTargetMixin.js +9 -0
  161. package/package.json +57 -41
  162. package/theming/index.js +594 -0
  163. package/theming/loader.js +24 -0
  164. package/utils/cli.js +11 -0
  165. package/utils/color_keywords.js +151 -0
  166. package/utils/hct/Cam16.js +298 -0
  167. package/utils/hct/CorePalette.js +84 -0
  168. package/utils/hct/Hct.js +172 -0
  169. package/utils/hct/Scheme.js +587 -0
  170. package/utils/hct/TonalPalette.js +68 -0
  171. package/utils/hct/ViewingConditions.js +136 -0
  172. package/utils/hct/blend.js +93 -0
  173. package/utils/hct/colorUtils.js +302 -0
  174. package/utils/hct/hctSolver.js +559 -0
  175. package/utils/hct/helper.js +182 -0
  176. package/utils/hct/mathUtils.js +153 -0
  177. package/utils/jsonMergePatch.js +100 -0
  178. package/utils/jsx-runtime.js +101 -0
  179. package/utils/popup.js +117 -0
  180. package/utils/svg.js +129 -0
  181. package/.browserslistrc +0 -3
  182. package/.eslintrc.json +0 -153
  183. package/.stylelintrc.json +0 -600
  184. package/.vscode/launch.json +0 -31
  185. package/.vscode/settings.json +0 -3
  186. package/.vscode/tasks.json +0 -32
  187. package/CHANGELOG.md +0 -24
  188. package/CODE_OF_CONDUCT.md +0 -46
  189. package/adapters/datatable/column.js +0 -203
  190. package/adapters/datatable/index.js +0 -972
  191. package/adapters/dom/index.js +0 -601
  192. package/adapters/list/index.js +0 -69
  193. package/adapters/search/index.js +0 -521
  194. package/components/appbar/_spec.scss +0 -225
  195. package/components/appbar/_theme.scss +0 -0
  196. package/components/appbar/index.scss +0 -2
  197. package/components/banner/_spec.scss +0 -118
  198. package/components/banner/_theme.scss +0 -0
  199. package/components/banner/index.scss +0 -2
  200. package/components/bottomnav/README.md +0 -85
  201. package/components/bottomnav/_spec.scss +0 -157
  202. package/components/bottomnav/_theme.scss +0 -0
  203. package/components/bottomnav/index.js +0 -122
  204. package/components/bottomnav/index.scss +0 -2
  205. package/components/bottomnav/item.js +0 -89
  206. package/components/button/README.md +0 -61
  207. package/components/button/_spec.scss +0 -161
  208. package/components/button/_theme.scss +0 -65
  209. package/components/button/index.eta +0 -32
  210. package/components/button/index.js +0 -43
  211. package/components/button/index.pug +0 -18
  212. package/components/button/index.scss +0 -2
  213. package/components/card/_spec.scss +0 -249
  214. package/components/card/_theme.scss +0 -0
  215. package/components/card/index.scss +0 -2
  216. package/components/chip/_spec.scss +0 -134
  217. package/components/chip/_theme.scss +0 -177
  218. package/components/chip/index.js +0 -21
  219. package/components/chip/index.scss +0 -2
  220. package/components/chip/item.js +0 -20
  221. package/components/datatable/_spec.scss +0 -288
  222. package/components/datatable/_theme.scss +0 -154
  223. package/components/datatable/cell.js +0 -45
  224. package/components/datatable/columnheader.js +0 -47
  225. package/components/datatable/index.js +0 -388
  226. package/components/datatable/index.scss +0 -2
  227. package/components/datatable/row.js +0 -49
  228. package/components/datatable/rowheader.js +0 -18
  229. package/components/dialog/_spec.scss +0 -213
  230. package/components/dialog/_theme.scss +0 -0
  231. package/components/dialog/index.js +0 -627
  232. package/components/dialog/index.scss +0 -2
  233. package/components/divider/_spec.scss +0 -13
  234. package/components/divider/_theme.scss +0 -0
  235. package/components/divider/index.scss +0 -2
  236. package/components/elevation/_spec.scss +0 -9
  237. package/components/elevation/_theme.scss +0 -0
  238. package/components/elevation/index.scss +0 -2
  239. package/components/fab/_spec.scss +0 -222
  240. package/components/fab/_theme.scss +0 -0
  241. package/components/fab/index.js +0 -103
  242. package/components/fab/index.scss +0 -2
  243. package/components/grid/_spec.scss +0 -312
  244. package/components/grid/_theme.scss +0 -0
  245. package/components/grid/index.scss +0 -2
  246. package/components/layout/_mixins.scss +0 -33
  247. package/components/layout/_spec.scss +0 -1012
  248. package/components/layout/_theme.scss +0 -44
  249. package/components/layout/index.js +0 -464
  250. package/components/layout/index.scss +0 -2
  251. package/components/list/_spec.scss +0 -397
  252. package/components/list/_theme.scss +0 -111
  253. package/components/list/content.js +0 -110
  254. package/components/list/index.js +0 -260
  255. package/components/list/index.scss +0 -2
  256. package/components/list/item.js +0 -170
  257. package/components/list/secondary.js +0 -46
  258. package/components/menu/_spec.scss +0 -362
  259. package/components/menu/_theme.scss +0 -0
  260. package/components/menu/index.js +0 -721
  261. package/components/menu/index.scss +0 -2
  262. package/components/menu/item.js +0 -239
  263. package/components/progress/_spec.scss +0 -147
  264. package/components/progress/_theme.scss +0 -0
  265. package/components/progress/index.js +0 -36
  266. package/components/progress/index.scss +0 -2
  267. package/components/selection/_spec.scss +0 -386
  268. package/components/selection/_theme.scss +0 -166
  269. package/components/selection/index.eta +0 -60
  270. package/components/selection/index.js +0 -76
  271. package/components/selection/index.pug +0 -30
  272. package/components/selection/index.scss +0 -2
  273. package/components/selection/input.js +0 -56
  274. package/components/selection/radiogroup.js +0 -47
  275. package/components/slider/_spec.scss +0 -64
  276. package/components/slider/_theme.scss +0 -0
  277. package/components/slider/index.scss +0 -2
  278. package/components/snackbar/_spec.scss +0 -195
  279. package/components/snackbar/_theme.scss +0 -0
  280. package/components/snackbar/index.js +0 -344
  281. package/components/snackbar/index.scss +0 -2
  282. package/components/tab/_spec.scss +0 -235
  283. package/components/tab/_theme.scss +0 -0
  284. package/components/tab/content.js +0 -205
  285. package/components/tab/index.js +0 -260
  286. package/components/tab/index.scss +0 -2
  287. package/components/tab/item.js +0 -89
  288. package/components/tab/list.js +0 -210
  289. package/components/tab/panel.js +0 -54
  290. package/components/template/_theme.scss +0 -27
  291. package/components/textfield/README.md +0 -179
  292. package/components/textfield/_mixins.scss +0 -52
  293. package/components/textfield/_spec.scss +0 -809
  294. package/components/textfield/_theme.scss +0 -299
  295. package/components/textfield/index.eta +0 -74
  296. package/components/textfield/index.js +0 -168
  297. package/components/textfield/index.pug +0 -30
  298. package/components/textfield/index.scss +0 -2
  299. package/components/tooltip/_spec.scss +0 -188
  300. package/components/tooltip/_theme.scss +0 -0
  301. package/components/tooltip/index.scss +0 -2
  302. package/components/type/_spec.scss +0 -224
  303. package/components/type/_theme.scss +0 -0
  304. package/components/type/index.scss +0 -2
  305. package/core/_breakpoint.scss +0 -189
  306. package/core/_elevation.scss +0 -38
  307. package/core/_length.scss +0 -9
  308. package/core/_motion.scss +0 -31
  309. package/core/_platform.scss +0 -34
  310. package/core/_type.scss +0 -127
  311. package/core/aria/attributes.js +0 -141
  312. package/core/aria/button.js +0 -50
  313. package/core/aria/keyboard.js +0 -93
  314. package/core/aria/rovingtabindex.js +0 -178
  315. package/core/aria/tab.js +0 -60
  316. package/core/color/_spec.scss +0 -0
  317. package/core/color/_theme.scss +0 -390
  318. package/core/color/index.scss +0 -2
  319. package/core/document/index.js +0 -39
  320. package/core/overlay/_spec.scss +0 -31
  321. package/core/overlay/_theme.scss +0 -171
  322. package/core/overlay/index.js +0 -108
  323. package/core/overlay/index.scss +0 -2
  324. package/core/ripple/_spec.scss +0 -197
  325. package/core/ripple/_theme.scss +0 -40
  326. package/core/ripple/index.js +0 -294
  327. package/core/ripple/index.scss +0 -2
  328. package/core/theme/_config.scss +0 -2
  329. package/core/theme/_mixins.scss +0 -172
  330. package/core/theme/_palettes.scss +0 -406
  331. package/core/theme/_variables.scss +0 -24
  332. package/core/theme/index.js +0 -50
  333. package/core/throttler.js +0 -42
  334. package/core/transition/index.js +0 -468
  335. package/docs/_flex.scss +0 -22
  336. package/docs/_menuoptions.js +0 -183
  337. package/docs/_mixins.pug +0 -155
  338. package/docs/_partials/_androidnavbar.eta +0 -5
  339. package/docs/_partials/_androidstatusbar.eta +0 -13
  340. package/docs/_partials/_appbar.eta +0 -29
  341. package/docs/_partials/_buttontest.eta +0 -31
  342. package/docs/_partials/_header.eta +0 -149
  343. package/docs/_partials/_navlistitem.eta +0 -16
  344. package/docs/_partials/_target.eta +0 -1
  345. package/docs/_sample-utils.js +0 -93
  346. package/docs/_storage.js +0 -33
  347. package/docs/docs.scss +0 -295
  348. package/docs/index.eta +0 -16
  349. package/docs/index.js +0 -0
  350. package/docs/pages/appbar.eta +0 -114
  351. package/docs/pages/appbar.js +0 -0
  352. package/docs/pages/appbar.pug +0 -78
  353. package/docs/pages/bottomnav.eta +0 -188
  354. package/docs/pages/bottomnav.js +0 -115
  355. package/docs/pages/bottomnav.pug +0 -137
  356. package/docs/pages/button.eta +0 -124
  357. package/docs/pages/button.js +0 -224
  358. package/docs/pages/button.pug +0 -121
  359. package/docs/pages/card.eta +0 -90
  360. package/docs/pages/card.js +0 -177
  361. package/docs/pages/card.pug +0 -74
  362. package/docs/pages/chip.eta +0 -122
  363. package/docs/pages/chip.js +0 -82
  364. package/docs/pages/chip.pug +0 -91
  365. package/docs/pages/color.eta +0 -143
  366. package/docs/pages/color.js +0 -262
  367. package/docs/pages/color.pug +0 -121
  368. package/docs/pages/datatable.eta +0 -323
  369. package/docs/pages/datatable.js +0 -164
  370. package/docs/pages/datatable.pug +0 -283
  371. package/docs/pages/dialog.eta +0 -186
  372. package/docs/pages/dialog.js +0 -177
  373. package/docs/pages/dialog.pug +0 -132
  374. package/docs/pages/dom.eta +0 -26
  375. package/docs/pages/dom.js +0 -143
  376. package/docs/pages/dom.pug +0 -22
  377. package/docs/pages/elevation.eta +0 -35
  378. package/docs/pages/elevation.js +0 -0
  379. package/docs/pages/elevation.pug +0 -25
  380. package/docs/pages/fab.eta +0 -99
  381. package/docs/pages/fab.js +0 -44
  382. package/docs/pages/fab.pug +0 -66
  383. package/docs/pages/grid.eta +0 -135
  384. package/docs/pages/grid.js +0 -128
  385. package/docs/pages/grid.pug +0 -95
  386. package/docs/pages/layout.eta +0 -8
  387. package/docs/pages/layout.js +0 -0
  388. package/docs/pages/layout.pug +0 -7
  389. package/docs/pages/list.eta +0 -465
  390. package/docs/pages/list.js +0 -9
  391. package/docs/pages/list.pug +0 -326
  392. package/docs/pages/menu.eta +0 -276
  393. package/docs/pages/menu.js +0 -217
  394. package/docs/pages/menu.pug +0 -205
  395. package/docs/pages/overlay.eta +0 -69
  396. package/docs/pages/overlay.js +0 -4
  397. package/docs/pages/overlay.pug +0 -55
  398. package/docs/pages/progress.eta +0 -23
  399. package/docs/pages/progress.js +0 -12
  400. package/docs/pages/progress.pug +0 -16
  401. package/docs/pages/ripple.eta +0 -27
  402. package/docs/pages/ripple.js +0 -4
  403. package/docs/pages/ripple.pug +0 -21
  404. package/docs/pages/search.eta +0 -246
  405. package/docs/pages/search.js +0 -243
  406. package/docs/pages/search.pug +0 -165
  407. package/docs/pages/selection.eta +0 -111
  408. package/docs/pages/selection.js +0 -13
  409. package/docs/pages/selection.pug +0 -74
  410. package/docs/pages/slider.eta +0 -23
  411. package/docs/pages/slider.js +0 -0
  412. package/docs/pages/slider.pug +0 -17
  413. package/docs/pages/snackbar.eta +0 -83
  414. package/docs/pages/snackbar.js +0 -158
  415. package/docs/pages/snackbar.pug +0 -60
  416. package/docs/pages/tab.eta +0 -421
  417. package/docs/pages/tab.js +0 -151
  418. package/docs/pages/tab.pug +0 -304
  419. package/docs/pages/textfield.eta +0 -486
  420. package/docs/pages/textfield.js +0 -254
  421. package/docs/pages/textfield.pug +0 -360
  422. package/docs/pages/tooltip.eta +0 -94
  423. package/docs/pages/tooltip.js +0 -0
  424. package/docs/pages/tooltip.pug +0 -78
  425. package/docs/pages/transition.eta +0 -117
  426. package/docs/pages/transition.js +0 -54
  427. package/docs/pages/transition.pug +0 -76
  428. package/docs/pages/type.eta +0 -31
  429. package/docs/pages/type.js +0 -0
  430. package/docs/pages/type.pug +0 -29
  431. package/docs/postrender.js +0 -39
  432. package/docs/prerender.js +0 -16
  433. package/docs/pwa/_dialogs.eta +0 -143
  434. package/docs/pwa/_dialogs.pug +0 -96
  435. package/docs/pwa/_menus.eta +0 -16
  436. package/docs/pwa/_menus.pug +0 -11
  437. package/docs/pwa/pwa-prerender.js +0 -3
  438. package/docs/pwa/pwa.eta +0 -480
  439. package/docs/pwa/pwa.js +0 -306
  440. package/docs/pwa/pwa.pug +0 -325
  441. package/docs/pwa/pwa.scss +0 -26
  442. package/docs/spec.scss +0 -26
  443. package/docs/themes/_component-themes.scss +0 -26
  444. package/docs/themes/theme-colored-fallbacks.scss +0 -17
  445. package/docs/themes/theme-colored.scss +0 -17
  446. package/docs/themes/theme-default-fallbacks.scss +0 -17
  447. package/docs/themes/theme-default.scss +0 -17
  448. package/jsconfig.json +0 -12
  449. package/scripts/deploy-docs.sh +0 -9
  450. package/templates/index.eta +0 -2
  451. package/templates/index.pug +0 -3
  452. package/webpack.config.cjs +0 -257
@@ -0,0 +1,844 @@
1
+ /* eslint-disable max-classes-per-file */
2
+
3
+ import Composition from './Composition.js';
4
+ import { ICustomElement } from './ICustomElement.js';
5
+ import { attrNameFromPropName, attrValueFromDataValue } from './dom.js';
6
+ import { defineObservableProperty } from './observe.js';
7
+ import { addInlineFunction, css, html } from './template.js';
8
+
9
+ /**
10
+ * @template {abstract new (...args: any) => unknown} T
11
+ * @param {InstanceType<T>} instance
12
+ */
13
+ function superOf(instance) {
14
+ const staticContext = instance.constructor;
15
+ const superOfStatic = Object.getPrototypeOf(staticContext);
16
+ return superOfStatic.prototype;
17
+ }
18
+
19
+ const EVENT_PREFIX_REGEX = /^([*1~]+)?(.*)$/;
20
+
21
+ /**
22
+ * Web Component that can cache templates for minification or performance
23
+ */
24
+ export default class CustomElement extends ICustomElement {
25
+ /** @type {string} */
26
+ static elementName;
27
+
28
+ /** @return {Iterable<string>} */
29
+ static get observedAttributes() {
30
+ const s = new Set();
31
+ for (const config of this.propList.values()) {
32
+ if (config.reflect === true || config.reflect === 'read') {
33
+ s.add(config.attr);
34
+ }
35
+ }
36
+ return s;
37
+ }
38
+
39
+ /** @type {import('./Composition.js').Compositor<?>} */
40
+ compose() {
41
+ if (this.#composition) {
42
+ console.warn('Already composed. Generating *new* composition...');
43
+ }
44
+ this.#composition = new Composition();
45
+ return this.#composition;
46
+ }
47
+
48
+ /** @type {Composition<?>} */
49
+ static _composition = null;
50
+
51
+ /** @type {Map<string, import('./typings.js').ObserverConfiguration<?,?,?>>} */
52
+ static _props = new Map();
53
+
54
+ /** @type {Map<string, Function[]>} */
55
+ static _propChangedCallbacks = new Map();
56
+
57
+ /** @type {Map<string, Function[]>} */
58
+ static _attributeChangedCallbacks = new Map();
59
+
60
+ /** @type {typeof ICustomElement._onComposeCallbacks} */
61
+ static _onComposeCallbacks = [];
62
+
63
+ /** @type {typeof ICustomElement._onConnectedCallbacks} */
64
+ static _onConnectedCallbacks = [];
65
+
66
+ /** @type {typeof ICustomElement._onDisconnectedCallbacks} */
67
+ static _onDisconnectedCallbacks = [];
68
+
69
+ /** @type {typeof ICustomElement._onConstructedCallbacks} */
70
+ static _onConstructedCallbacks = [];
71
+
72
+ static interpolatesTemplate = true;
73
+
74
+ static supportsElementInternals = 'attachInternals' in HTMLElement.prototype;
75
+
76
+ static supportsElementInternalsRole = CustomElement.supportsElementInternals
77
+ && 'role' in ElementInternals.prototype;
78
+
79
+ /** @type {boolean} */
80
+ static templatable = null;
81
+
82
+ static defined = false;
83
+
84
+ static autoRegistration = true;
85
+
86
+ /** @type {Map<string, typeof CustomElement>} */
87
+ static registrations = new Map();
88
+
89
+ /** @type {typeof ICustomElement.expressions} */
90
+ static expressions = this.set;
91
+
92
+ /** @type {typeof ICustomElement.methods} */
93
+ static methods = this.set;
94
+
95
+ /** @type {typeof ICustomElement.overrides} */
96
+ static overrides = this.set;
97
+
98
+ /** @type {typeof ICustomElement.props} */
99
+ static props = this.observe;
100
+
101
+ /**
102
+ * @template {typeof CustomElement} T
103
+ * @this T
104
+ * @template {keyof T} K
105
+ * @param {K} collection
106
+ * @param {T[K] extends (infer R)[] ? R : never} callback
107
+ */
108
+ static _addCallback(collection, callback) {
109
+ if (!this.hasOwnProperty(collection)) {
110
+ this[collection] = [
111
+ ...this[collection],
112
+ ];
113
+ }
114
+ this[collection].push(callback);
115
+ }
116
+
117
+ /**
118
+ * Append parts to composition
119
+ * @type {typeof ICustomElement.append}
120
+ */
121
+ static append(...parts) {
122
+ this.on({
123
+ composed({ composition }) {
124
+ // console.debug('onComposed:append', ...parts);
125
+ composition.append(...parts);
126
+ },
127
+ });
128
+ // @ts-expect-error Can't cast T
129
+ return this;
130
+ }
131
+
132
+ /**
133
+ * Appends styles to composition
134
+ * @type {typeof ICustomElement.css}
135
+ */
136
+ static css(array, ...substitutions) {
137
+ if (Array.isArray(array)) {
138
+ // @ts-expect-error Complex cast
139
+ this.append(css(array, ...substitutions));
140
+ } else {
141
+ // @ts-expect-error Complex cast
142
+ this.append(array, ...substitutions);
143
+ }
144
+ // @ts-expect-error Can't cast T
145
+ return this;
146
+ }
147
+
148
+ /** @type {typeof ICustomElement['setSchema']} */
149
+ static setSchema(schema) {
150
+ this.schema = schema;
151
+ // @ts-expect-error Can't cast T
152
+ return this;
153
+ }
154
+
155
+ /**
156
+ * Registers class asynchronously at end of current event loop cycle
157
+ * via `queueMicrotask`. If class is registered before then,
158
+ * does nothing.
159
+ * @type {typeof ICustomElement['autoRegister']}
160
+ */
161
+ static autoRegister(elementName) {
162
+ if (elementName) {
163
+ this.elementName = elementName;
164
+ }
165
+ queueMicrotask(() => {
166
+ if (this.autoRegistration) {
167
+ this.register();
168
+ }
169
+ });
170
+ // @ts-expect-error Can't cast T
171
+ return this;
172
+ }
173
+
174
+ /**
175
+ * Appends DocumentFragment to composition
176
+ * @type {typeof ICustomElement.html}
177
+ */
178
+ static html(strings, ...substitutions) {
179
+ this.on({
180
+ composed({ composition }) {
181
+ // console.log('onComposed:html', strings);
182
+ composition.append(html(strings, ...substitutions));
183
+ },
184
+ });
185
+ // @ts-expect-error Can't cast T
186
+ return this;
187
+ }
188
+
189
+ /**
190
+ * Extends base class into a new class.
191
+ * Use to avoid mutating base class.
192
+ * TODO: Add constructor arguments typing
193
+ * @type {typeof ICustomElement.extend}
194
+ */
195
+ static extend() {
196
+ // @ts-expect-error Can't cast T
197
+ return class ExtendedClass extends this {};
198
+ }
199
+
200
+ /**
201
+ * Extends base class into a new class.
202
+ * Use to avoid mutating base class.
203
+ * TODO: Add constructor arguments typing
204
+ * @type {typeof ICustomElement.tsClassFix}
205
+ */
206
+ static tsClassFix() {
207
+ // @ts-expect-error Can't cast T
208
+ return this;
209
+ }
210
+
211
+ /**
212
+ * Assigns static values to class
213
+ * @type {typeof ICustomElement.setStatic}
214
+ */
215
+ static setStatic(source) {
216
+ Object.assign(this, source);
217
+ // @ts-expect-error Can't cast T
218
+ return this;
219
+ }
220
+
221
+ /**
222
+ * Assigns values directly to all instances (via prototype)
223
+ * @type {typeof ICustomElement.set}
224
+ */
225
+ static readonly(source, options) {
226
+ // @ts-expect-error Can't cast T
227
+ return this.set(source, { ...options, writable: false });
228
+ }
229
+
230
+ /**
231
+ * Assigns values directly to all instances (via prototype)
232
+ * @type {typeof ICustomElement.set}
233
+ */
234
+ static set(source, options) {
235
+ Object.defineProperties(
236
+ this.prototype,
237
+ Object.fromEntries(
238
+ Object.entries(source).map(([name, value]) => {
239
+ // Tap into .map() to avoid double iteration
240
+ // Property may be redefined observable
241
+ this.undefine(name);
242
+ return [
243
+ name,
244
+ {
245
+ enumerable: name[0] !== '_',
246
+ configurable: true,
247
+ value,
248
+ writable: true,
249
+ ...options,
250
+ },
251
+ ];
252
+ }),
253
+ ),
254
+ );
255
+ // @ts-expect-error Can't cast T
256
+ return this;
257
+ }
258
+
259
+ /**
260
+ * Returns result of calling mixin with current class
261
+ * @type {typeof ICustomElement.mixin}
262
+ */
263
+ static mixin(mixin) {
264
+ return mixin(this);
265
+ }
266
+
267
+ /**
268
+ * Registers class with window.customElements synchronously
269
+ * @type {typeof ICustomElement['register']}
270
+ */
271
+ static register(elementName, force = false) {
272
+ if (this.hasOwnProperty('defined') && this.defined && !force) {
273
+ console.warn(this.elementName, 'already registered.');
274
+ // @ts-expect-error Can't cast T
275
+ return this;
276
+ }
277
+
278
+ if (elementName) {
279
+ this.elementName = elementName;
280
+ }
281
+
282
+ customElements.define(this.elementName, this);
283
+ CustomElement.registrations.set(this.elementName, this);
284
+ this.defined = true;
285
+ // @ts-expect-error Can't cast T
286
+ return this;
287
+ }
288
+
289
+ static get propList() {
290
+ if (!this.hasOwnProperty('_props')) {
291
+ this._props = new Map(this._props);
292
+ }
293
+ return this._props;
294
+ }
295
+
296
+ static get propChangedCallbacks() {
297
+ if (!this.hasOwnProperty('_propChangedCallbacks')) {
298
+ // structuredClone()
299
+ this._propChangedCallbacks = new Map(
300
+ [
301
+ ...this._propChangedCallbacks,
302
+ ].map(([name, array]) => [name, array.slice()]),
303
+ );
304
+ }
305
+ return this._propChangedCallbacks;
306
+ }
307
+
308
+ static get attributeChangedCallbacks() {
309
+ if (!this.hasOwnProperty('_attributeChangedCallbacks')) {
310
+ this._attributeChangedCallbacks = new Map(
311
+ [
312
+ ...this._attributeChangedCallbacks,
313
+ ].map(([name, array]) => [name, array.slice()]),
314
+ );
315
+ }
316
+ return this._attributeChangedCallbacks;
317
+ }
318
+
319
+ /**
320
+ * Creates observable property on instances (via prototype)
321
+ * @template {import('./typings.js').ObserverPropertyType} [T1=null]
322
+ * @template {import('./typings.js').ObserverPropertyType} [T2=null]
323
+ * @template {any} [T3=null]
324
+ * @param {string} name
325
+ * @param {T1|import('./typings.js').ObserverOptions<T2,T3>} [typeOrOptions='string']
326
+ * @return {(
327
+ * T3 extends null ?
328
+ * T2 extends null ?
329
+ * T1 extends null ?
330
+ * string
331
+ * : import('./typings.js').ParsedObserverPropertyType<T1>
332
+ * : import('./typings.js').ParsedObserverPropertyType<T2>
333
+ * : T3
334
+ * )}
335
+ */
336
+ static prop(name, typeOrOptions) {
337
+ // TODO: Cache and save configuration for reuse (mixins)
338
+ /** @type {import('./typings.js').ObserverOptions<?,?>} */
339
+ const options = {
340
+ ...((typeof typeOrOptions === 'string') ? { type: typeOrOptions } : typeOrOptions),
341
+ };
342
+
343
+ const customCallback = options.changedCallback;
344
+
345
+ if (customCallback) {
346
+ // Move callback to later in stack for attribute-based changes as well
347
+ this.onPropChanged({ [name]: customCallback });
348
+ }
349
+
350
+ // TODO: Inspect possible closure bloat
351
+ options.changedCallback = function wrappedChangedCallback(oldValue, newValue, changes) {
352
+ this._onObserverPropertyChanged.call(this, name, oldValue, newValue, changes);
353
+ };
354
+
355
+ const config = defineObservableProperty(this.prototype, name, options);
356
+
357
+ this.propList.set(name, config);
358
+ for (const [prop, callback] of config.watchers) {
359
+ this.on(`${prop}Changed`, callback);
360
+ }
361
+
362
+ return config.INIT_SYMBOL;
363
+ }
364
+
365
+ /**
366
+ * Define properties on instances via Object.defineProperties().
367
+ * Automatically sets property non-enumerable if name begins with `_`.
368
+ * @type {typeof ICustomElement.define}
369
+ */
370
+ static define(props) {
371
+ Object.defineProperties(
372
+ this.prototype,
373
+ Object.fromEntries(
374
+ Object.entries(props).map(([name, options]) => {
375
+ // Tap into .map() to avoid double iteration
376
+ // Property may be redefined observable
377
+ this.undefine(name);
378
+ return [
379
+ name,
380
+ {
381
+ enumerable: name[0] !== '_',
382
+ configurable: true,
383
+ ...(
384
+ typeof options === 'function'
385
+ ? { get: options }
386
+ : options
387
+ ),
388
+ },
389
+ ];
390
+ }),
391
+ ),
392
+ );
393
+
394
+ // @ts-expect-error Can't cast T
395
+ return this;
396
+ }
397
+
398
+ static undefine(name) {
399
+ Reflect.deleteProperty(this.prototype, name);
400
+ const config = this.propList.get(name);
401
+ if (config && config.watchers.length) {
402
+ const propWatchers = this.propChangedCallbacks.get(name);
403
+ if (propWatchers) {
404
+ for (const watcher of config.watchers) {
405
+ const index = propWatchers.indexOf(watcher);
406
+ if (index !== -1) {
407
+ console.warn('Unwatching', name);
408
+ propWatchers.splice(index, 1);
409
+ }
410
+ }
411
+ }
412
+ }
413
+ this.propList.delete(name);
414
+ return this;
415
+ }
416
+
417
+ /**
418
+ * Creates observable properties on instances
419
+ * @type {typeof ICustomElement.observe}
420
+ */
421
+ static observe(props) {
422
+ for (const [name, typeOrOptions] of Object.entries(props ?? {})) {
423
+ if (typeof typeOrOptions === 'function') {
424
+ this.prop(name, {
425
+ reflect: false,
426
+ get: typeOrOptions,
427
+ });
428
+ } else {
429
+ this.prop(name, typeOrOptions);
430
+ }
431
+ }
432
+ // @ts-expect-error Can't cast T
433
+ return this;
434
+ }
435
+
436
+ /** @type {typeof ICustomElement.defineStatic} */
437
+ static defineStatic(props) {
438
+ for (const [name, typeOrOptions] of Object.entries(props ?? {})) {
439
+ const options = (typeof typeOrOptions === 'function')
440
+ ? { get: typeOrOptions }
441
+ : (typeof typeOrOptions === 'string'
442
+ ? { type: typeOrOptions }
443
+ : typeOrOptions);
444
+ defineObservableProperty(this, name, {
445
+ reflect: false,
446
+ ...options,
447
+ });
448
+ }
449
+ // @ts-expect-error Can't cast T
450
+ return this;
451
+ }
452
+
453
+ /** @type {typeof ICustomElement.events} */
454
+ static events(listeners, options) {
455
+ this.on({
456
+ composed({ composition }) {
457
+ for (const [key, listenerOptions] of Object.entries(listeners)) {
458
+ const [, flags, type] = key.match(EVENT_PREFIX_REGEX);
459
+ composition.addCompositionEventListener({
460
+ type,
461
+ once: flags?.includes('1'),
462
+ passive: flags?.includes('~'),
463
+ capture: flags?.includes('*'),
464
+ ...(
465
+ typeof listenerOptions === 'function'
466
+ ? { handleEvent: listenerOptions }
467
+ : (typeof listenerOptions === 'string'
468
+ ? { prop: listenerOptions }
469
+ : listenerOptions)
470
+ ),
471
+ ...(
472
+ options
473
+ )
474
+ ,
475
+ });
476
+ }
477
+ },
478
+ });
479
+
480
+ // @ts-expect-error Can't cast T
481
+ return this;
482
+ }
483
+
484
+ /** @type {typeof ICustomElement.childEvents} */
485
+ static childEvents(listenerMap, options) {
486
+ for (const [id, listeners] of Object.entries(listenerMap)) {
487
+ this.events(listeners, {
488
+ id,
489
+ ...options,
490
+ });
491
+ }
492
+
493
+ // @ts-expect-error Can't cast T
494
+ return this;
495
+ }
496
+
497
+ /** @type {typeof ICustomElement['on']} */
498
+ static on(nameOrCallbacks, callback) {
499
+ const callbacks = typeof nameOrCallbacks === 'string'
500
+ ? { [nameOrCallbacks]: callback }
501
+ : nameOrCallbacks;
502
+ for (const [name, fn] of Object.entries(callbacks)) {
503
+ /** @type {keyof (typeof CustomElement)} */
504
+ let arrayPropName;
505
+ switch (name) {
506
+ case 'composed': arrayPropName = '_onComposeCallbacks'; break;
507
+ case 'constructed': arrayPropName = '_onConstructedCallbacks'; break;
508
+ case 'connected': arrayPropName = '_onConnectedCallbacks'; break;
509
+ case 'disconnected': arrayPropName = '_onDisconnectedCallbacks'; break;
510
+ case 'props':
511
+ this.onPropChanged(fn);
512
+ continue;
513
+ case 'attrs':
514
+ this.onAttributeChanged(fn);
515
+ continue;
516
+ default:
517
+ if (name.endsWith('Changed')) {
518
+ const prop = name.slice(0, name.length - 'Changed'.length);
519
+ this.onPropChanged({ [prop]: fn });
520
+ continue;
521
+ }
522
+ throw new Error('Invalid callback name');
523
+ }
524
+ this._addCallback(arrayPropName, fn);
525
+ }
526
+
527
+ // @ts-expect-error Can't cast T
528
+ return this;
529
+ }
530
+
531
+ /** @type {typeof ICustomElement['onPropChanged']} */
532
+ static onPropChanged(options) {
533
+ for (const [prop, callback] of Object.entries(options)) {
534
+ let array = this.propChangedCallbacks.get(prop);
535
+ if (!array) {
536
+ array = [];
537
+ this.propChangedCallbacks.set(prop, array);
538
+ }
539
+ array.push(callback);
540
+ }
541
+
542
+ // @ts-expect-error Can't cast T
543
+ return this;
544
+ }
545
+
546
+ /** @type {typeof ICustomElement['onAttributeChanged']} */
547
+ static onAttributeChanged(options) {
548
+ for (const [name, callback] of Object.entries(options)) {
549
+ let array = this.attributeChangedCallbacks.get(name);
550
+ if (!array) {
551
+ array = [];
552
+ this.attributeChangedCallbacks.set(name, array);
553
+ }
554
+ array.push(callback);
555
+ }
556
+
557
+ // @ts-expect-error Can't cast T
558
+ return this;
559
+ }
560
+
561
+ /** @type {Record<string, HTMLElement>}} */
562
+ #refsProxy;
563
+
564
+ /** @type {Map<string, WeakRef<HTMLElement>>}} */
565
+ #refsCache = new Map();
566
+
567
+ /** @type {Map<string, WeakRef<HTMLElement>>}} */
568
+ #refsCompositionCache = new Map();
569
+
570
+ /** @type {Composition<?>} */
571
+ #composition;
572
+
573
+ /** @type {Map<string,null|[string,any]>} */
574
+ _propAttributeCache;
575
+
576
+ /** @type {import('./ICustomElement.js').CallbackArguments} */
577
+ _callbackArguments = null;
578
+
579
+ /** @param {any[]} args */
580
+ constructor(...args) {
581
+ super();
582
+
583
+ if (CustomElement.supportsElementInternals) {
584
+ this.elementInternals = this.attachInternals();
585
+ }
586
+
587
+ this.attachShadow({ mode: 'open', delegatesFocus: this.delegatesFocus });
588
+
589
+ this.composition.initialRender(this.shadowRoot, this);
590
+
591
+ for (const callback of this.static._onConstructedCallbacks) {
592
+ callback.call(this, this.callbackArguments);
593
+ }
594
+ }
595
+
596
+ /**
597
+ * Updates nodes based on data
598
+ * Expects data in JSON Merge Patch format
599
+ * @see https://www.rfc-editor.org/rfc/rfc7386
600
+ * @param {?} data
601
+ * @param {?} [store]
602
+ * @return {void}
603
+ */
604
+ render(data, store) {
605
+ // console.log('render', data);
606
+ this.composition.render(this.shadowRoot, data, this, store ? { ...this, store } : this);
607
+ }
608
+
609
+ /** @type {InstanceType<typeof ICustomElement>['propChangedCallback']} */
610
+ propChangedCallback(name, oldValue, newValue, changes = newValue) {
611
+ const callbacks = this.static.propChangedCallbacks.get(name);
612
+ if (callbacks) {
613
+ for (const callback of callbacks) {
614
+ callback.call(this, oldValue, newValue, changes, this);
615
+ }
616
+ }
617
+
618
+ this.render({ [name]: changes });
619
+ }
620
+
621
+ /**
622
+ * @param {string} name
623
+ * @param {string|null} oldValue
624
+ * @param {string|null} newValue
625
+ */
626
+ attributeChangedCallback(name, oldValue, newValue) {
627
+ const callbacks = this.static.attributeChangedCallbacks.get(name);
628
+ if (callbacks) {
629
+ for (const callback of callbacks) {
630
+ callback.call(this, oldValue, newValue, this);
631
+ }
632
+ }
633
+
634
+ // Array.find
635
+ for (const config of this.static.propList.values()) {
636
+ if (config.attr !== name) continue;
637
+
638
+ if (config.reflect !== true && config.reflect !== 'read') return;
639
+
640
+ if (config.attributeChangedCallback) {
641
+ config.attributeChangedCallback.call(this, name, oldValue, newValue);
642
+ return;
643
+ }
644
+
645
+ const [stringValue] = this.attributeCache.get(name) ?? [null, null];
646
+ if (stringValue === newValue) {
647
+ // Attribute was changed via data change event. Ignore.
648
+ return;
649
+ }
650
+
651
+ // @ts-expect-error any
652
+ const previousDataValue = this[config.key];
653
+ const parsedValue = newValue === null
654
+ ? config.nullParser(/** @type {null} */ (newValue))
655
+ // Avoid Boolean('') === false
656
+ : (config.type === 'boolean' ? true : config.parser(newValue));
657
+
658
+ if (parsedValue === previousDataValue) {
659
+ // No internal value change
660
+ return;
661
+ }
662
+ // "Remember" that this attrValue equates to this data value
663
+ // Avoids rewriting attribute later on data change event
664
+ this.attributeCache.set(name, [newValue, parsedValue]);
665
+ // @ts-expect-error any
666
+ this[config.key] = parsedValue;
667
+ return;
668
+ }
669
+ }
670
+
671
+ get #template() {
672
+ return this.#composition?.template;
673
+ }
674
+
675
+ /**
676
+ * @param {string} name
677
+ * @param {any} oldValue
678
+ * @param {any} newValue
679
+ * @param {any} changes
680
+ */
681
+ _onObserverPropertyChanged(name, oldValue, newValue, changes) {
682
+ const { reflect, attr } = this.static.propList.get(name);
683
+ if (attr && (reflect === true || reflect === 'write')) {
684
+ const [, dataValue] = this.attributeCache.get(attr) ?? [null, null];
685
+ // Don't change attribute if data value is equivalent
686
+ // (eg: Boolean('foo') === true; Number("1.0") === 1)
687
+ if (dataValue !== newValue) {
688
+ const attrValue = attrValueFromDataValue(newValue);
689
+ // Cache attrValue to ignore attributeChangedCallback later
690
+ this.attributeCache.set(attr, [attrValue, newValue]);
691
+ if (attrValue == null) {
692
+ this.removeAttribute(attr);
693
+ } else {
694
+ this.setAttribute(attr, attrValue);
695
+ }
696
+ }
697
+ }
698
+
699
+ // Invoke change => render
700
+ this.propChangedCallback(name, oldValue, newValue, changes);
701
+ }
702
+
703
+ /**
704
+ * Proxy object that returns shadow DOM elements by ID.
705
+ * If called before interpolation (eg: on composed), returns from template
706
+ * @return {Record<string,HTMLElement>}
707
+ */
708
+ get refs() {
709
+ // eslint-disable-next-line no-return-assign
710
+ return (this.#refsProxy ??= new Proxy({}, {
711
+ /**
712
+ * @param {any} target
713
+ * @param {string} id
714
+ * @return {Element}
715
+ */
716
+ get: (target, id) => {
717
+ if (!this.#composition) {
718
+ console.warn(this.static.name, 'Attempted to access references before composing!');
719
+ }
720
+ const composition = this.composition;
721
+ if (!composition.interpolated) {
722
+ let element = this.#refsCompositionCache.get(id)?.deref();
723
+ if (element) return element;
724
+ const formattedId = attrNameFromPropName(id);
725
+ // console.warn(this.tagName, 'Returning template reference');
726
+ element = composition.template.getElementById(formattedId);
727
+ if (!element) return null;
728
+ this.#refsCompositionCache.set(id, new WeakRef(element));
729
+ return element;
730
+ }
731
+ let element = this.#refsCache.get(id)?.deref();
732
+ if (element) {
733
+ return element;
734
+ }
735
+ const formattedId = attrNameFromPropName(id);
736
+ element = composition.getElement(this.shadowRoot, formattedId);
737
+ if (!element) return null;
738
+ this.#refsCache.set(id, new WeakRef(element));
739
+ return element;
740
+ },
741
+ }));
742
+ }
743
+
744
+ get attributeCache() {
745
+ this._propAttributeCache ??= new Map();
746
+ return this._propAttributeCache;
747
+ }
748
+
749
+ get tabIndex() {
750
+ return super.tabIndex;
751
+ }
752
+
753
+ set tabIndex(value) {
754
+ if (value === super.tabIndex && value !== -1) {
755
+ // Non -1 value already set
756
+ return;
757
+ }
758
+
759
+ if (this.delegatesFocus && document.activeElement === this) {
760
+ if (this.getAttribute('tabindex') === value.toString()) {
761
+ // Skip if possible
762
+ return;
763
+ }
764
+
765
+ // Chrome blurs on tabindex changes with delegatesFocus
766
+ // Fixed in Chrome 111
767
+ // Remove this code ~June 2023
768
+ // https://bugs.chromium.org/p/chromium/issues/detail?id=1346606
769
+ /** @type {EventListener} */
770
+ const listener = (e) => {
771
+ e.stopImmediatePropagation();
772
+ e.stopPropagation();
773
+ if (e.type === 'blur') {
774
+ console.warn('Chromium bug 1346606: Tabindex change caused blur. Giving focusing back.', this);
775
+ this.focus();
776
+ } else {
777
+ console.warn('Chromium bug 1346606: Blocking focus event.', this);
778
+ }
779
+ };
780
+ this.addEventListener('blur', listener, { capture: true, once: true });
781
+ this.addEventListener('focus', listener, { capture: true, once: true });
782
+ super.tabIndex = value;
783
+ this.removeEventListener('blur', listener, { capture: true });
784
+ this.removeEventListener('focus', listener, { capture: true });
785
+ return;
786
+ }
787
+
788
+ super.tabIndex = value;
789
+ }
790
+
791
+ get static() { return /** @type {typeof CustomElement} */ (/** @type {unknown} */ (this.constructor)); }
792
+
793
+ get unique() { return false; }
794
+
795
+ get callbackArguments() {
796
+ // eslint-disable-next-line no-return-assign
797
+ return this._callbackArguments ??= {
798
+ composition: this.#composition,
799
+ html: html.bind(this),
800
+ inline: addInlineFunction,
801
+ template: this.#template,
802
+ element: this,
803
+ };
804
+ }
805
+
806
+ /** @return {Composition<?>} */
807
+ get composition() {
808
+ if (this.#composition) return this.#composition;
809
+
810
+ if (!this.unique && this.static.hasOwnProperty('_composition')) {
811
+ this.#composition = this.static._composition;
812
+ return this.static._composition;
813
+ }
814
+
815
+ // TODO: Use Composition to track uniqueness
816
+ // console.log('composing', this.static.elementName);
817
+ this.compose();
818
+ for (const callback of this.static._onComposeCallbacks) {
819
+ // console.log(this.static.elementName, 'composition callback');
820
+ callback.call(this, this.callbackArguments);
821
+ }
822
+
823
+ if (!this.unique) {
824
+ // Cache compilation into static property
825
+ this.static._composition = this.#composition;
826
+ }
827
+
828
+ return this.#composition;
829
+ }
830
+
831
+ connectedCallback() {
832
+ for (const callbacks of this.static._onConnectedCallbacks) {
833
+ callbacks.call(this, this.callbackArguments);
834
+ }
835
+ }
836
+
837
+ disconnectedCallback() {
838
+ for (const callbacks of this.static._onDisconnectedCallbacks) {
839
+ callbacks.call(this, this.callbackArguments);
840
+ }
841
+ }
842
+ }
843
+
844
+ CustomElement.prototype.delegatesFocus = false;