@shortfuse/materialdesignweb 0.8.0 → 0.9.1

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 (405) hide show
  1. package/README.md +50 -198
  2. package/bin/mdw-css.js +1 -1
  3. package/components/Badge.js +15 -5
  4. package/components/Body.js +7 -0
  5. package/components/BottomAppBar.js +7 -10
  6. package/components/BottomSheet.js +472 -0
  7. package/components/Box.js +11 -49
  8. package/components/Button.js +81 -82
  9. package/components/Card.js +74 -62
  10. package/components/Checkbox.js +15 -25
  11. package/components/CheckboxIcon.js +19 -31
  12. package/components/Chip.js +18 -13
  13. package/components/Dialog.js +70 -100
  14. package/components/DialogActions.js +4 -0
  15. package/components/Display.js +64 -0
  16. package/components/Divider.js +5 -0
  17. package/components/Fab.js +94 -17
  18. package/components/FabContainer.js +57 -0
  19. package/components/FilterChip.js +43 -32
  20. package/components/Grid.js +187 -0
  21. package/components/Headline.js +9 -28
  22. package/components/Icon.js +80 -71
  23. package/components/IconButton.js +77 -120
  24. package/components/Input.js +745 -86
  25. package/components/InputChip.js +193 -0
  26. package/components/Label.js +7 -0
  27. package/components/List.js +11 -5
  28. package/components/ListItem.js +92 -23
  29. package/components/ListOption.js +143 -65
  30. package/components/Listbox.js +57 -17
  31. package/components/Menu.js +39 -27
  32. package/components/MenuItem.js +49 -36
  33. package/components/NavBar.js +66 -21
  34. package/components/NavBarItem.js +5 -0
  35. package/components/NavDrawer.js +33 -16
  36. package/components/NavDrawerItem.js +7 -4
  37. package/components/NavItem.js +61 -34
  38. package/components/NavRail.js +32 -21
  39. package/components/NavRailItem.js +10 -2
  40. package/components/Page.js +119 -0
  41. package/components/Pane.js +24 -0
  42. package/components/Popup.js +23 -8
  43. package/components/Progress.js +25 -5
  44. package/components/Radio.js +8 -7
  45. package/components/RadioIcon.js +24 -15
  46. package/components/Ripple.js +25 -7
  47. package/components/Root.js +225 -0
  48. package/components/Scrim.js +95 -0
  49. package/components/Search.js +30 -25
  50. package/components/SegmentedButton.js +53 -40
  51. package/components/SegmentedButtonGroup.js +15 -12
  52. package/components/Select.js +19 -10
  53. package/components/Shape.js +10 -66
  54. package/components/SideSheet.js +337 -0
  55. package/components/Slider.js +93 -36
  56. package/components/Snackbar.js +52 -20
  57. package/components/SnackbarContainer.js +51 -0
  58. package/components/Surface.js +20 -10
  59. package/components/Switch.js +21 -18
  60. package/components/SwitchIcon.js +62 -33
  61. package/components/Tab.js +78 -38
  62. package/components/TabContent.js +33 -12
  63. package/components/TabList.js +95 -34
  64. package/components/TabPanel.js +10 -1
  65. package/components/Table.js +151 -0
  66. package/components/TextArea.js +48 -16
  67. package/components/Title.js +8 -9
  68. package/components/Tooltip.js +51 -22
  69. package/components/TopAppBar.js +71 -78
  70. package/constants/shapes.js +36 -0
  71. package/constants/typography.js +127 -0
  72. package/core/Composition.js +391 -201
  73. package/core/CompositionAdapter.js +35 -18
  74. package/core/CustomElement.js +634 -254
  75. package/core/css.js +117 -12
  76. package/core/customTypes.js +161 -49
  77. package/core/dom.js +18 -11
  78. package/core/jsonMergePatch.js +27 -11
  79. package/core/observe.js +308 -256
  80. package/core/optimizations.js +9 -9
  81. package/core/template.js +14 -57
  82. package/dist/CustomElement.min.js +2 -0
  83. package/dist/CustomElement.min.js.map +7 -0
  84. package/dist/core/CustomElement.min.js +2 -0
  85. package/dist/core/CustomElement.min.js.map +7 -0
  86. package/dist/index.min.js +85 -115
  87. package/dist/index.min.js.map +4 -4
  88. package/dist/meta.json +1 -1
  89. package/dom/HTMLOptionsCollectionProxy.js +108 -0
  90. package/{theming/themableMixinLoader.js → loaders/palette.js} +4 -3
  91. package/loaders/theme.js +12 -0
  92. package/mixins/AriaReflectorMixin.js +64 -15
  93. package/mixins/AriaToolbarMixin.js +6 -0
  94. package/mixins/ControlMixin.js +79 -33
  95. package/mixins/DelegatesFocusMixin.js +62 -0
  96. package/mixins/DensityMixin.js +7 -3
  97. package/mixins/ElevationMixin.js +61 -0
  98. package/mixins/FlexableMixin.js +87 -39
  99. package/mixins/FormAssociatedMixin.js +76 -10
  100. package/mixins/HyperlinkMixin.js +76 -0
  101. package/mixins/InputMixin.js +227 -32
  102. package/mixins/KeyboardNavMixin.js +11 -7
  103. package/mixins/NavigationListenerMixin.js +33 -0
  104. package/mixins/PopupMixin.js +216 -219
  105. package/mixins/RTLObserverMixin.js +2 -0
  106. package/mixins/ResizeObserverMixin.js +18 -4
  107. package/mixins/RippleMixin.js +11 -7
  108. package/mixins/ScrollListenerMixin.js +14 -2
  109. package/mixins/SemiStickyMixin.js +51 -98
  110. package/mixins/ShapeMaskedMixin.js +125 -0
  111. package/mixins/ShapeMixin.js +30 -203
  112. package/mixins/StateMixin.js +74 -34
  113. package/mixins/TextFieldMixin.js +128 -145
  114. package/mixins/ThemableMixin.js +57 -56
  115. package/mixins/TooltipTriggerMixin.js +305 -359
  116. package/mixins/TouchTargetMixin.js +5 -2
  117. package/mixins/TypographyMixin.js +128 -0
  118. package/package.json +125 -81
  119. package/services/rtl.js +10 -0
  120. package/services/svgAlias.js +17 -0
  121. package/{theming/index.js → services/theme.js} +25 -176
  122. package/types/bin/mdw-css.d.ts +3 -0
  123. package/types/bin/mdw-css.d.ts.map +1 -0
  124. package/types/components/Badge.d.ts +39 -0
  125. package/types/components/Badge.d.ts.map +1 -0
  126. package/types/components/Body.d.ts +29 -0
  127. package/types/components/Body.d.ts.map +1 -0
  128. package/types/components/BottomAppBar.d.ts +72 -0
  129. package/types/components/BottomAppBar.d.ts.map +1 -0
  130. package/types/components/BottomSheet.d.ts +135 -0
  131. package/types/components/BottomSheet.d.ts.map +1 -0
  132. package/types/components/Box.d.ts +16 -0
  133. package/types/components/Box.d.ts.map +1 -0
  134. package/types/components/Button.d.ts +245 -0
  135. package/types/components/Button.d.ts.map +1 -0
  136. package/types/components/Card.d.ts +147 -0
  137. package/types/components/Card.d.ts.map +1 -0
  138. package/types/components/Checkbox.d.ts +207 -0
  139. package/types/components/Checkbox.d.ts.map +1 -0
  140. package/types/components/CheckboxIcon.d.ts +44 -0
  141. package/types/components/CheckboxIcon.d.ts.map +1 -0
  142. package/types/components/Chip.d.ts +248 -0
  143. package/types/components/Chip.d.ts.map +1 -0
  144. package/types/components/Dialog.d.ts +103 -0
  145. package/types/components/Dialog.d.ts.map +1 -0
  146. package/types/components/DialogActions.d.ts +4 -0
  147. package/types/components/DialogActions.d.ts.map +1 -0
  148. package/types/components/Display.d.ts +46 -0
  149. package/types/components/Display.d.ts.map +1 -0
  150. package/types/components/Divider.d.ts +10 -0
  151. package/types/components/Divider.d.ts.map +1 -0
  152. package/types/components/Fab.d.ts +273 -0
  153. package/types/components/Fab.d.ts.map +1 -0
  154. package/types/components/FabContainer.d.ts +10 -0
  155. package/types/components/FabContainer.d.ts.map +1 -0
  156. package/types/components/FilterChip.d.ts +256 -0
  157. package/types/components/FilterChip.d.ts.map +1 -0
  158. package/types/components/Grid.d.ts +38 -0
  159. package/types/components/Grid.d.ts.map +1 -0
  160. package/types/components/Headline.d.ts +46 -0
  161. package/types/components/Headline.d.ts.map +1 -0
  162. package/types/components/Icon.d.ts +55 -0
  163. package/types/components/Icon.d.ts.map +1 -0
  164. package/types/components/IconButton.d.ts +284 -0
  165. package/types/components/IconButton.d.ts.map +1 -0
  166. package/types/components/Input.d.ts +2528 -0
  167. package/types/components/Input.d.ts.map +1 -0
  168. package/types/components/InputChip.d.ts +85 -0
  169. package/types/components/InputChip.d.ts.map +1 -0
  170. package/types/components/Label.d.ts +29 -0
  171. package/types/components/Label.d.ts.map +1 -0
  172. package/types/components/List.d.ts +35 -0
  173. package/types/components/List.d.ts.map +1 -0
  174. package/types/components/ListItem.d.ts +124 -0
  175. package/types/components/ListItem.d.ts.map +1 -0
  176. package/types/components/ListOption.d.ts +158 -0
  177. package/types/components/ListOption.d.ts.map +1 -0
  178. package/types/components/Listbox.d.ts +763 -0
  179. package/types/components/Listbox.d.ts.map +1 -0
  180. package/types/components/Menu.d.ts +130 -0
  181. package/types/components/Menu.d.ts.map +1 -0
  182. package/types/components/MenuItem.d.ts +232 -0
  183. package/types/components/MenuItem.d.ts.map +1 -0
  184. package/types/components/NavBar.d.ts +20 -0
  185. package/types/components/NavBar.d.ts.map +1 -0
  186. package/types/components/NavBarItem.d.ts +97 -0
  187. package/types/components/NavBarItem.d.ts.map +1 -0
  188. package/types/components/NavDrawer.d.ts +107 -0
  189. package/types/components/NavDrawer.d.ts.map +1 -0
  190. package/types/components/NavDrawerItem.d.ts +97 -0
  191. package/types/components/NavDrawerItem.d.ts.map +1 -0
  192. package/types/components/NavItem.d.ts +99 -0
  193. package/types/components/NavItem.d.ts.map +1 -0
  194. package/types/components/NavRail.d.ts +108 -0
  195. package/types/components/NavRail.d.ts.map +1 -0
  196. package/types/components/NavRailItem.d.ts +97 -0
  197. package/types/components/NavRailItem.d.ts.map +1 -0
  198. package/types/components/Page.d.ts +25 -0
  199. package/types/components/Page.d.ts.map +1 -0
  200. package/types/components/Pane.d.ts +44 -0
  201. package/types/components/Pane.d.ts.map +1 -0
  202. package/types/components/Popup.d.ts +78 -0
  203. package/types/components/Popup.d.ts.map +1 -0
  204. package/types/components/Progress.d.ts +21 -0
  205. package/types/components/Progress.d.ts.map +1 -0
  206. package/types/components/Radio.d.ts +201 -0
  207. package/types/components/Radio.d.ts.map +1 -0
  208. package/types/components/RadioIcon.d.ts +46 -0
  209. package/types/components/RadioIcon.d.ts.map +1 -0
  210. package/types/components/Ripple.d.ts +35 -0
  211. package/types/components/Ripple.d.ts.map +1 -0
  212. package/types/components/Root.d.ts +68 -0
  213. package/types/components/Root.d.ts.map +1 -0
  214. package/types/components/Scrim.d.ts +6 -0
  215. package/types/components/Scrim.d.ts.map +1 -0
  216. package/types/components/Search.d.ts +516 -0
  217. package/types/components/Search.d.ts.map +1 -0
  218. package/types/components/SegmentedButton.d.ts +252 -0
  219. package/types/components/SegmentedButton.d.ts.map +1 -0
  220. package/types/components/SegmentedButtonGroup.d.ts +43 -0
  221. package/types/components/SegmentedButtonGroup.d.ts.map +1 -0
  222. package/types/components/Select.d.ts +158 -0
  223. package/types/components/Select.d.ts.map +1 -0
  224. package/types/components/Shape.d.ts +10 -0
  225. package/types/components/Shape.d.ts.map +1 -0
  226. package/types/components/SideSheet.d.ts +111 -0
  227. package/types/components/SideSheet.d.ts.map +1 -0
  228. package/types/components/Slider.d.ts +203 -0
  229. package/types/components/Slider.d.ts.map +1 -0
  230. package/types/components/Snackbar.d.ts +73 -0
  231. package/types/components/Snackbar.d.ts.map +1 -0
  232. package/types/components/SnackbarContainer.d.ts +6 -0
  233. package/types/components/SnackbarContainer.d.ts.map +1 -0
  234. package/types/components/Surface.d.ts +45 -0
  235. package/types/components/Surface.d.ts.map +1 -0
  236. package/types/components/Switch.d.ts +187 -0
  237. package/types/components/Switch.d.ts.map +1 -0
  238. package/types/components/SwitchIcon.d.ts +61 -0
  239. package/types/components/SwitchIcon.d.ts.map +1 -0
  240. package/types/components/Tab.d.ts +139 -0
  241. package/types/components/Tab.d.ts.map +1 -0
  242. package/types/components/TabContent.d.ts +124 -0
  243. package/types/components/TabContent.d.ts.map +1 -0
  244. package/types/components/TabList.d.ts +1111 -0
  245. package/types/components/TabList.d.ts.map +1 -0
  246. package/types/components/TabPanel.d.ts +28 -0
  247. package/types/components/TabPanel.d.ts.map +1 -0
  248. package/types/components/Table.d.ts +25 -0
  249. package/types/components/Table.d.ts.map +1 -0
  250. package/types/components/TextArea.d.ts +201 -0
  251. package/types/components/TextArea.d.ts.map +1 -0
  252. package/types/components/Title.d.ts +46 -0
  253. package/types/components/Title.d.ts.map +1 -0
  254. package/types/components/Tooltip.d.ts +49 -0
  255. package/types/components/Tooltip.d.ts.map +1 -0
  256. package/types/components/TopAppBar.d.ts +129 -0
  257. package/types/components/TopAppBar.d.ts.map +1 -0
  258. package/types/constants/colorKeywords.d.ts +2 -0
  259. package/types/constants/colorKeywords.d.ts.map +1 -0
  260. package/types/constants/shapes.d.ts +38 -0
  261. package/types/constants/shapes.d.ts.map +1 -0
  262. package/types/constants/typography.d.ts +212 -0
  263. package/types/constants/typography.d.ts.map +1 -0
  264. package/types/core/Composition.d.ts +260 -0
  265. package/types/core/Composition.d.ts.map +1 -0
  266. package/types/core/CompositionAdapter.d.ts +114 -0
  267. package/types/core/CompositionAdapter.d.ts.map +1 -0
  268. package/types/core/CustomElement.d.ts +304 -0
  269. package/types/core/CustomElement.d.ts.map +1 -0
  270. package/types/core/css.d.ts +44 -0
  271. package/types/core/css.d.ts.map +1 -0
  272. package/types/core/customTypes.d.ts +22 -0
  273. package/types/core/customTypes.d.ts.map +1 -0
  274. package/types/core/dom.d.ts +32 -0
  275. package/types/core/dom.d.ts.map +1 -0
  276. package/types/core/jsonMergePatch.d.ts +31 -0
  277. package/types/core/jsonMergePatch.d.ts.map +1 -0
  278. package/types/core/observe.d.ts +114 -0
  279. package/types/core/observe.d.ts.map +1 -0
  280. package/types/core/optimizations.d.ts +7 -0
  281. package/types/core/optimizations.d.ts.map +1 -0
  282. package/types/core/template.d.ts +47 -0
  283. package/types/core/template.d.ts.map +1 -0
  284. package/types/core/uid.d.ts +6 -0
  285. package/types/core/uid.d.ts.map +1 -0
  286. package/types/dom/HTMLOptionsCollectionProxy.d.ts +18 -0
  287. package/types/dom/HTMLOptionsCollectionProxy.d.ts.map +1 -0
  288. package/types/index.d.ts +88 -0
  289. package/types/index.d.ts.map +1 -0
  290. package/types/loaders/palette.d.ts +2 -0
  291. package/types/loaders/palette.d.ts.map +1 -0
  292. package/types/loaders/theme.d.ts +2 -0
  293. package/types/loaders/theme.d.ts.map +1 -0
  294. package/types/mixins/AriaReflectorMixin.d.ts +31 -0
  295. package/types/mixins/AriaReflectorMixin.d.ts.map +1 -0
  296. package/types/mixins/AriaToolbarMixin.d.ts +34 -0
  297. package/types/mixins/AriaToolbarMixin.d.ts.map +1 -0
  298. package/types/mixins/ControlMixin.d.ts +124 -0
  299. package/types/mixins/ControlMixin.d.ts.map +1 -0
  300. package/types/mixins/DelegatesFocusMixin.d.ts +13 -0
  301. package/types/mixins/DelegatesFocusMixin.d.ts.map +1 -0
  302. package/types/mixins/DensityMixin.d.ts +8 -0
  303. package/types/mixins/DensityMixin.d.ts.map +1 -0
  304. package/types/mixins/ElevationMixin.d.ts +32 -0
  305. package/types/mixins/ElevationMixin.d.ts.map +1 -0
  306. package/types/mixins/FlexableMixin.d.ts +14 -0
  307. package/types/mixins/FlexableMixin.d.ts.map +1 -0
  308. package/types/mixins/FormAssociatedMixin.d.ts +123 -0
  309. package/types/mixins/FormAssociatedMixin.d.ts.map +1 -0
  310. package/types/mixins/HyperlinkMixin.d.ts +25 -0
  311. package/types/mixins/HyperlinkMixin.d.ts.map +1 -0
  312. package/types/mixins/InputMixin.d.ts +173 -0
  313. package/types/mixins/InputMixin.d.ts.map +1 -0
  314. package/types/mixins/KeyboardNavMixin.d.ts +46 -0
  315. package/types/mixins/KeyboardNavMixin.d.ts.map +1 -0
  316. package/types/mixins/NavigationListenerMixin.d.ts +8 -0
  317. package/types/mixins/NavigationListenerMixin.d.ts.map +1 -0
  318. package/types/mixins/PopupMixin.d.ts +98 -0
  319. package/types/mixins/PopupMixin.d.ts.map +1 -0
  320. package/types/mixins/RTLObserverMixin.d.ts +8 -0
  321. package/types/mixins/RTLObserverMixin.d.ts.map +1 -0
  322. package/types/mixins/ResizeObserverMixin.d.ts +13 -0
  323. package/types/mixins/ResizeObserverMixin.d.ts.map +1 -0
  324. package/types/mixins/RippleMixin.d.ts +94 -0
  325. package/types/mixins/RippleMixin.d.ts.map +1 -0
  326. package/types/mixins/ScrollListenerMixin.d.ts +46 -0
  327. package/types/mixins/ScrollListenerMixin.d.ts.map +1 -0
  328. package/types/mixins/SemiStickyMixin.d.ts +50 -0
  329. package/types/mixins/SemiStickyMixin.d.ts.map +1 -0
  330. package/types/mixins/ShapeMaskedMixin.d.ts +12 -0
  331. package/types/mixins/ShapeMaskedMixin.d.ts.map +1 -0
  332. package/types/mixins/ShapeMixin.d.ts +39 -0
  333. package/types/mixins/ShapeMixin.d.ts.map +1 -0
  334. package/types/mixins/StateMixin.d.ts +29 -0
  335. package/types/mixins/StateMixin.d.ts.map +1 -0
  336. package/types/mixins/TextFieldMixin.d.ts +153 -0
  337. package/types/mixins/TextFieldMixin.d.ts.map +1 -0
  338. package/types/mixins/ThemableMixin.d.ts +10 -0
  339. package/types/mixins/ThemableMixin.d.ts.map +1 -0
  340. package/types/mixins/TooltipTriggerMixin.d.ts +114 -0
  341. package/types/mixins/TooltipTriggerMixin.d.ts.map +1 -0
  342. package/types/mixins/TouchTargetMixin.d.ts +6 -0
  343. package/types/mixins/TouchTargetMixin.d.ts.map +1 -0
  344. package/types/mixins/TypographyMixin.d.ts +20 -0
  345. package/types/mixins/TypographyMixin.d.ts.map +1 -0
  346. package/types/services/rtl.d.ts +3 -0
  347. package/types/services/rtl.d.ts.map +1 -0
  348. package/types/services/svgAlias.d.ts +13 -0
  349. package/types/services/svgAlias.d.ts.map +1 -0
  350. package/types/services/theme.d.ts +45 -0
  351. package/types/services/theme.d.ts.map +1 -0
  352. package/types/utils/cli.d.ts +3 -0
  353. package/types/utils/cli.d.ts.map +1 -0
  354. package/types/utils/function.d.ts +3 -0
  355. package/types/utils/function.d.ts.map +1 -0
  356. package/types/utils/jsx-runtime.d.ts +20 -0
  357. package/types/utils/jsx-runtime.d.ts.map +1 -0
  358. package/types/utils/material-color/blend.d.ts +34 -0
  359. package/types/utils/material-color/blend.d.ts.map +1 -0
  360. package/types/utils/material-color/hct/Cam16.d.ts +142 -0
  361. package/types/utils/material-color/hct/Cam16.d.ts.map +1 -0
  362. package/types/utils/material-color/hct/Hct.d.ts +93 -0
  363. package/types/utils/material-color/hct/Hct.d.ts.map +1 -0
  364. package/types/utils/material-color/hct/ViewingConditions.d.ts +69 -0
  365. package/types/utils/material-color/hct/ViewingConditions.d.ts.map +1 -0
  366. package/types/utils/material-color/hct/hctSolver.d.ts +30 -0
  367. package/types/utils/material-color/hct/hctSolver.d.ts.map +1 -0
  368. package/types/utils/material-color/helper.d.ts +8 -0
  369. package/types/utils/material-color/helper.d.ts.map +1 -0
  370. package/types/utils/material-color/palettes/CorePalette.d.ts +75 -0
  371. package/types/utils/material-color/palettes/CorePalette.d.ts.map +1 -0
  372. package/types/utils/material-color/palettes/TonalPalette.d.ts +38 -0
  373. package/types/utils/material-color/palettes/TonalPalette.d.ts.map +1 -0
  374. package/types/utils/material-color/scheme/Scheme.d.ts +264 -0
  375. package/types/utils/material-color/scheme/Scheme.d.ts.map +1 -0
  376. package/types/utils/material-color/utils/color.d.ts +172 -0
  377. package/types/utils/material-color/utils/color.d.ts.map +1 -0
  378. package/types/utils/material-color/utils/math.d.ts +94 -0
  379. package/types/utils/material-color/utils/math.d.ts.map +1 -0
  380. package/types/utils/pixelmatch.d.ts +22 -0
  381. package/types/utils/pixelmatch.d.ts.map +1 -0
  382. package/types/utils/popup.d.ts +106 -0
  383. package/types/utils/popup.d.ts.map +1 -0
  384. package/types/utils/searchParams.d.ts +3 -0
  385. package/types/utils/searchParams.d.ts.map +1 -0
  386. package/types/utils/svg.d.ts +7 -0
  387. package/types/utils/svg.d.ts.map +1 -0
  388. package/utils/jsx-runtime.js +9 -4
  389. package/utils/material-color/scheme/Scheme.js +1 -1
  390. package/utils/pixelmatch.js +363 -0
  391. package/utils/popup.js +86 -10
  392. package/utils/searchParams.js +22 -0
  393. package/components/Button.md +0 -61
  394. package/components/ExtendedFab.js +0 -32
  395. package/components/Layout.js +0 -504
  396. package/components/Nav.js +0 -38
  397. package/core/DomAdapter.js +0 -586
  398. package/core/ICustomElement.d.ts +0 -291
  399. package/core/ICustomElement.js +0 -1
  400. package/core/test.js +0 -126
  401. package/core/typings.d.ts +0 -142
  402. package/core/typings.js +0 -1
  403. package/mixins/SurfaceMixin.js +0 -127
  404. package/theming/loader.js +0 -22
  405. /package/{utils/color_keywords.js → constants/colorKeywords.js} +0 -0
@@ -22,12 +22,99 @@ import { generateUID } from './uid.js';
22
22
  /**
23
23
  * @template T
24
24
  * @typedef {Object} RenderOptions
25
+ * @prop {T} [defaults] what
25
26
  * @prop {T} [store] what
26
- * @prop {DocumentFragment|ShadowRoot|HTMLElement|Element} [target] where
27
+ * @prop {DocumentFragment|HTMLElement|Element} [target] where
28
+ * @prop {ShadowRoot} [shadowRoot] where
27
29
  * @prop {any} [context] `this` on callbacks/events
28
30
  * @prop {any} [injections]
29
31
  */
30
32
 
33
+ /**
34
+ * @template T
35
+ * @typedef {{
36
+ * target: Element|DocumentFragment,
37
+ * byProp: (prop: keyof T & string, value:any, data?:Partial<T>) => void,
38
+ * state: InitializationState,
39
+ * } & ((changes:Partial<T>, data:T) => void)} RenderDraw
40
+ */
41
+
42
+ /** @typedef {HTMLElementEventMap & { input: InputEvent; } } HTMLElementEventMapFixed */
43
+
44
+ /**
45
+ * @typedef {(
46
+ * Pick<HTMLElementEventMapFixed,
47
+ * 'auxclick' |
48
+ * 'beforeinput' |
49
+ * 'click' |
50
+ * 'compositionstart' |
51
+ * 'contextmenu' |
52
+ * 'drag' |
53
+ * 'dragenter' |
54
+ * 'dragover' |
55
+ * 'dragstart' |
56
+ * 'drop' |
57
+ * 'invalid' |
58
+ * 'keydown' |
59
+ * 'keypress' |
60
+ * 'keyup' |
61
+ * 'mousedown' |
62
+ * 'mousemove' |
63
+ * 'mouseout' |
64
+ * 'mouseover' |
65
+ * 'mouseup' |
66
+ * 'pointerdown' |
67
+ * 'pointermove' |
68
+ * 'pointerout' |
69
+ * 'pointerover' |
70
+ * 'pointerup' |
71
+ * 'reset' |
72
+ * 'selectstart' |
73
+ * 'submit' |
74
+ * 'touchend' |
75
+ * 'touchmove' |
76
+ * 'touchstart' |
77
+ * 'wheel'
78
+ * >
79
+ * )} HTMLElementCancellableEventMap
80
+ */
81
+
82
+ /**
83
+ * @typedef {(
84
+ * HTMLElementEventMapFixed
85
+ * & {[P in keyof HTMLElementCancellableEventMap as `~${P}`]: HTMLElementCancellableEventMap[P]}
86
+ * & Record<string, Event|CustomEvent<any>>
87
+ * )} CompositionEventMap
88
+ */
89
+
90
+ /**
91
+ * @template {any} T
92
+ * @template {keyof CompositionEventMap} [K = keyof CompositionEventMap]
93
+ * @typedef {{
94
+ * type?: K
95
+ * tag?: string|symbol,
96
+ * capture?: boolean;
97
+ * once?: boolean;
98
+ * passive?: boolean;
99
+ * signal?: AbortSignal;
100
+ * handleEvent?: (
101
+ * this: T,
102
+ * event: (K extends keyof CompositionEventMap ? CompositionEventMap[K] : Event) & {currentTarget:HTMLElement}
103
+ * ) => any;
104
+ * prop?: string;
105
+ * deepProp?: string[],
106
+ * }} CompositionEventListener
107
+ */
108
+
109
+ /**
110
+ * @template T
111
+ * @typedef {{
112
+ * [P in keyof CompositionEventMap]?: (keyof T & string)
113
+ * | ((this: T, event: CompositionEventMap[P] & {currentTarget:HTMLElement}) => any)
114
+ * | CompositionEventListener<T, P>
115
+ * }} CompositionEventListenerObject
116
+ */
117
+
31
118
  /**
32
119
  * @template {any} T
33
120
  * @typedef {Object} NodeBindEntry
@@ -41,7 +128,7 @@ import { generateUID } from './uid.js';
41
128
  * @prop {boolean} [doubleNegate]
42
129
  * @prop {Function} [expression]
43
130
  * @prop {(options: RenderOptions<?>, element: Element, changes:any, data:any) => any} [render] custom render function
44
- * @prop {import('./typings.js').CompositionEventListener<T>[]} [listeners]
131
+ * @prop {CompositionEventListener<T>[]} [listeners]
45
132
  * @prop {Composition<any>} [composition] // Sub composition templating (eg: array)
46
133
  * @prop {T} defaultValue
47
134
  */
@@ -52,9 +139,10 @@ import { generateUID } from './uid.js';
52
139
  * @typedef RenderGraphSearch
53
140
  * @prop {(state:InitializationState, changes:any, data:any) => any} invocation
54
141
  * @prop {number} cacheIndex
55
- * @prop {number} ranFlagIndex
56
- * @prop {number} dirtyIndex
142
+ * @prop {number} searchIndex
57
143
  * @prop {string | Function | string[]} query
144
+ * @prop {boolean} [negate]
145
+ * @prop {boolean} [doubleNegate]
58
146
  * @prop {Function} [expression]
59
147
  * @prop {string} prop
60
148
  * @prop {string[]} deepProp
@@ -73,16 +161,16 @@ import { generateUID } from './uid.js';
73
161
  * @prop {string} [attrName]
74
162
  * @prop {any} [defaultValue]
75
163
  * @prop {RenderGraphSearch} search
164
+ * @prop {InterpolateOptions['injections']} [injections]
76
165
  */
77
166
 
78
167
  /**
79
168
  * @type {RenderGraphAction['invocation']}
80
169
  * @this {RenderGraphAction}
81
170
  */
82
- function writeDOMAttribute(state, value) {
171
+ function writeDOMAttribute({ nodes }, value) {
83
172
  const { nodeIndex, attrName } = this;
84
- /** @type {Element} */
85
- const element = state.nodes[nodeIndex];
173
+ const element = /** @type {Element} */ (nodes[nodeIndex]);
86
174
  switch (value) {
87
175
  case undefined:
88
176
  case null:
@@ -102,43 +190,97 @@ function writeDOMAttribute(state, value) {
102
190
  * @type {RenderGraphAction['invocation']}
103
191
  * @this {RenderGraphAction}
104
192
  */
105
- function writeDOMText(state, value) {
106
- // @ts-ignore Skip cast
107
- state.nodes[this.nodeIndex].data = value;
193
+ function writeDynamicNode({ nodeStates, comments, nodes }, value) {
194
+ const { commentIndex, nodeIndex } = this;
195
+ const nodeState = nodeStates[nodeIndex];
196
+ // eslint-disable-next-line no-bitwise
197
+ const hidden = nodeState & 0b0001;
198
+ const show = value != null && value !== false;
199
+ if (!show) {
200
+ // Should be hidden
201
+ if (hidden) return;
202
+ // Replace whatever node is there with the comment
203
+ let comment = comments[commentIndex];
204
+ if (!comment) {
205
+ comment = createEmptyComment();
206
+ comments[commentIndex] = comment;
207
+ }
208
+ nodes[nodeIndex].replaceWith(comment);
209
+ // eslint-disable-next-line no-bitwise
210
+ nodeStates[nodeIndex] |= 0b0001;
211
+ return;
212
+ }
213
+ // Must be shown
214
+ // Update node first (offscreen rendering)
215
+ const node = nodes[nodeIndex];
216
+ // eslint-disable-next-line no-bitwise
217
+ const isDynamicNode = nodeState & 0b0010;
218
+
219
+ if (typeof value === 'object') {
220
+ // Not string data, need to replace
221
+ console.warn('Dynamic nodes not supported yet');
222
+ } else if (isDynamicNode) {
223
+ const textNode = new Text(value);
224
+ node.replaceWith(textNode);
225
+ nodes[nodeIndex] = textNode;
226
+ // eslint-disable-next-line no-bitwise
227
+ nodeStates[nodeIndex] &= ~0b0010;
228
+ } else {
229
+ /** @type {Text} */ (node).data = value;
230
+ }
231
+
232
+ // Updated, now set hidden state
233
+
234
+ if (hidden) {
235
+ const comment = comments[commentIndex];
236
+ comment.replaceWith(node);
237
+ // eslint-disable-next-line no-bitwise
238
+ nodeStates[nodeIndex] &= ~0b0001;
239
+ }
240
+ // Done
108
241
  }
109
242
 
110
243
  /**
111
244
  * @type {RenderGraphAction['invocation']}
112
245
  * @this {RenderGraphAction}
113
246
  */
114
- function writeDOMElementAttachedState(state, value) {
247
+ function writeDOMElementAttachedState({ nodeStates, nodes, comments }, value) {
115
248
  const { commentIndex, nodeIndex } = this;
116
- let comment = state.comments[commentIndex];
249
+ // eslint-disable-next-line no-bitwise
250
+ const hidden = nodeStates[nodeIndex] & 1;
251
+ const show = value != null && value !== false;
252
+ if (show === !hidden) return;
253
+
254
+ const element = nodes[nodeIndex];
255
+ let comment = comments[commentIndex];
117
256
  if (!comment) {
118
257
  comment = createEmptyComment();
119
- state.comments[commentIndex] = comment;
258
+ comments[commentIndex] = comment;
120
259
  }
121
- const element = state.nodes[nodeIndex];
122
- const show = value != null && value !== false;
123
260
  if (show) {
124
261
  comment.replaceWith(element);
262
+ // eslint-disable-next-line no-bitwise
263
+ nodeStates[nodeIndex] &= ~0b0001;
125
264
  } else {
126
265
  element.replaceWith(comment);
266
+ // eslint-disable-next-line no-bitwise
267
+ nodeStates[nodeIndex] |= 0b0001;
127
268
  }
128
- return show;
129
269
  }
130
270
 
131
271
  /**
132
272
  * @type {RenderGraphAction['invocation']}
133
273
  * @this {RenderGraphAction}
134
274
  */
135
- function writeDOMHideElementOnInit(state) {
275
+ function writeDOMHideNodeOnInit({ comments, nodeStates, nodes }) {
136
276
  const { commentIndex, nodeIndex } = this;
137
277
 
138
278
  const comment = createEmptyComment();
139
- state.comments[commentIndex] = comment;
279
+ comments[commentIndex] = comment;
280
+ // eslint-disable-next-line no-bitwise
281
+ nodeStates[nodeIndex] |= 1;
140
282
 
141
- state.nodes[nodeIndex].replaceWith(comment);
283
+ nodes[nodeIndex].replaceWith(comment);
142
284
  }
143
285
 
144
286
  /**
@@ -146,37 +288,50 @@ function writeDOMHideElementOnInit(state) {
146
288
  * @param {Parameters<RenderGraphSearch['invocation']>} args
147
289
  */
148
290
  function executeSearch(search, ...args) {
149
- const [state] = args;
150
- const cachedValue = state.caches[search.cacheIndex];
151
- if (state.ranFlags[search.ranFlagIndex]) {
291
+ const [{ caches, searchStates }] = args;
292
+ const { cacheIndex, searchIndex, subSearch, invocation } = search;
293
+ const cachedValue = caches[cacheIndex];
294
+ const searchState = searchStates[searchIndex];
295
+
296
+ // Ran = 0b0001
297
+ // Dirty = 0b0010
298
+ // eslint-disable-next-line no-bitwise
299
+ if (searchState & 0b0001) {
152
300
  // Return last result
153
301
  return {
154
302
  value: cachedValue,
155
- dirty: state.dirtyFlags[search.dirtyIndex],
303
+ // eslint-disable-next-line no-bitwise
304
+ dirty: ((searchState & 0b0010) === 0b0010),
156
305
  };
157
306
  }
158
- state.ranFlags[search.ranFlagIndex] = true;
307
+
308
+ // eslint-disable-next-line no-bitwise
309
+ searchStates[searchIndex] |= 0b0001;
159
310
  let result;
160
- if (search.subSearch) {
161
- const subResult = executeSearch(search.subSearch, ...args);
162
- // Use last cached value (if any)
163
- if (!subResult.dirty && cachedValue !== undefined) {
164
- state.dirtyFlags[search.dirtyIndex] = false;
165
- return { value: cachedValue, dirty: false };
311
+ if (invocation) {
312
+ if (subSearch) {
313
+ const subResult = executeSearch(subSearch, ...args);
314
+ // Use last cached value (if any)
315
+ if (!subResult.dirty && cachedValue !== undefined) {
316
+ // eslint-disable-next-line no-bitwise
317
+ searchStates[searchIndex] &= ~0b0010;
318
+ return { value: cachedValue, dirty: false };
319
+ }
320
+ // Pass from subquery
321
+ result = search.invocation(subResult.value);
322
+ } else {
323
+ result = search.invocation(...args);
324
+ }
325
+ if ((result === undefined) || (cachedValue === result)) {
326
+ // Return from cache
327
+ return { value: result, dirty: false };
166
328
  }
167
- // Pass from subquery
168
- result = search.invocation(subResult.value);
169
- } else {
170
- result = search.invocation(...args);
171
- }
172
- if ((result === undefined) || (cachedValue === result)) {
173
- // Returnf rom cache
174
- return { value: result, dirty: false };
175
329
  }
176
330
 
177
331
  // Overwrite cache and flag as dirty
178
- state.caches[search.cacheIndex] = result;
179
- state.dirtyFlags[search.dirtyIndex] = true;
332
+ caches[cacheIndex] = result;
333
+ // eslint-disable-next-line no-bitwise
334
+ searchStates[searchIndex] |= 0b0010;
180
335
  return { value: result, dirty: true };
181
336
  }
182
337
 
@@ -184,11 +339,11 @@ function executeSearch(search, ...args) {
184
339
  * @type {RenderGraphSearch['invocation']}
185
340
  * @this {RenderGraphSearch}
186
341
  */
187
- function searchWithExpression(state, changes, data) {
342
+ function searchWithExpression({ options: { context, store, injections } }, changes, data) {
188
343
  return this.expression.call(
189
- state.options.context,
190
- state.options.store ?? data,
191
- state.options.injections,
344
+ context,
345
+ store ?? data,
346
+ injections,
192
347
  );
193
348
  }
194
349
 
@@ -196,7 +351,7 @@ function searchWithExpression(state, changes, data) {
196
351
  * @type {RenderGraphSearch['invocation']}
197
352
  * @this {RenderGraphSearch}
198
353
  */
199
- function searchWithProp(state, changes, data) {
354
+ function searchWithProp(state, changes) {
200
355
  return changes[this.prop];
201
356
  }
202
357
 
@@ -216,23 +371,21 @@ function searchWithDeepProp(state, changes, data) {
216
371
 
217
372
  /**
218
373
  * @typedef InterpolateOptions
219
- * @prop {Object} [defaults] Default values to use for interpolation
220
- * @prop {{iterable:string} & Record<string,any>} [injections] Context-specific injected properties. (Experimental)
374
+ * @prop {Record<string,any>} [defaults] Default values to use for interpolation
375
+ * @prop {{iterable:string} & Record<string,any> & {index:number}} [injections] Context-specific injected properties. (Experimental)
221
376
  */
222
377
 
223
378
  /**
224
379
  * @typedef InitializationState
225
380
  * @prop {Element} lastElement
226
- * @prop {boolean} isShadowRoot
227
381
  * @prop {ChildNode} lastChildNode
228
382
  * @prop {(Element|Text)[]} nodes
229
383
  * @prop {any[]} caches
230
384
  * @prop {Comment[]} comments
231
- * @prop {boolean[]} ranFlags
232
- * @prop {boolean[]} dirtyFlags
233
- * @prop {Element[]} refs
385
+ * @prop {Uint8Array} nodeStates
386
+ * @prop {Uint8Array} searchStates
387
+ * @prop {HTMLElement[]} refs
234
388
  * @prop {number} lastChildNodeIndex
235
- * @prop {DocumentFragment} instanceFragment
236
389
  * @prop {RenderOptions<?>} options
237
390
  */
238
391
 
@@ -242,7 +395,6 @@ const STRING_INTERPOLATION_REGEX = /{([^}]*)}/g;
242
395
  /**
243
396
  * Returns event listener bound to shadow root host.
244
397
  * Use this function to avoid generating extra closures
245
- *
246
398
  * @this {HTMLElement}
247
399
  * @param {Function} fn
248
400
  */
@@ -317,13 +469,16 @@ function valueFromPropName(prop, source) {
317
469
  return value;
318
470
  }
319
471
 
472
+ const compositionCache = new Map();
473
+
320
474
  /** @template T */
321
475
  export default class Composition {
322
- #interpolationState = {
476
+ static EVENT_PREFIX_REGEX = /^([*1~]+)?(.*)$/;
477
+
478
+ _interpolationState = {
323
479
  nodeIndex: -1,
324
- ranFlagIndex: 0,
480
+ searchIndex: 0,
325
481
  cacheIndex: 0,
326
- dirtyIndex: 0,
327
482
  commentIndex: 0,
328
483
  /** @type {this['nodesToBind'][0]} */
329
484
  nodeEntry: null,
@@ -346,65 +501,49 @@ export default class Composition {
346
501
 
347
502
  /**
348
503
  * Index of searches by query (dotted notation for deep props)
349
- *
350
504
  * @type {Map<Function|string, RenderGraphSearch>}
351
505
  */
352
- searchByQuery = new Map();
506
+ searchByQuery;
353
507
 
354
508
  /**
355
509
  * Index of searches by query (dotted notation for deep props)
356
- *
357
510
  * @type {Map<string, RenderGraphAction[]>}
358
511
  */
359
- actionsByPropsUsed = new Map();
360
-
361
- /** @type {RenderGraphAction[]} */
362
- actions = [];
512
+ actionsByPropsUsed;
363
513
 
364
514
  /** @type {RenderGraphAction[]} */
365
515
  postInitActions = [];
366
516
 
367
517
  /** @type {Set<string>} */
368
- tagsWithBindings = new Set();
518
+ tagsWithBindings;
369
519
 
370
520
  /**
371
521
  * Array of element tags
372
- *
373
522
  * @type {string[]}
374
523
  */
375
524
  tags = [];
376
525
 
377
- /**
378
- * Array of property bindings sorted by tag/subnode
379
- *
380
- * @type {Set<string>}
381
- */
382
- watchedProps = new Set();
383
-
384
526
  /**
385
527
  * Data of arrays used in templates
386
528
  * Usage of a [mdw-for] will create an ArrayLike expectation based on key
387
529
  * Only store metadata, not actual data. Currently only needs length.
388
530
  * TBD if more is needed later
389
531
  * Referenced by property key (string)
390
- *
391
- * @type {CompositionAdapter}
532
+ * @type {CompositionAdapter<T>}
392
533
  */
393
534
  adapter;
394
535
 
395
536
  /**
396
537
  * Collection of events to bind.
397
538
  * Indexed by ID
398
- *
399
- * @type {Map<string|symbol, import('./typings.js').CompositionEventListener<any>[]>}
539
+ * @type {Map<string|symbol, CompositionEventListener<any>[]>}
400
540
  */
401
- events = new Map();
541
+ events;
402
542
 
403
543
  /**
404
544
  * Snapshot of composition at initial state.
405
545
  * This fragment can be cloned for first rendering, instead of calling
406
546
  * of using `render()` to construct the initial DOM tree.
407
- *
408
547
  * @type {DocumentFragment}
409
548
  */
410
549
  cloneable;
@@ -421,7 +560,6 @@ export default class Composition {
421
560
  /**
422
561
  * List of IDs used by template elements
423
562
  * May be needed to be removed when adding to non-DocumentFragment
424
- *
425
563
  * @type {string[]}
426
564
  */
427
565
  allIds = [];
@@ -430,7 +568,8 @@ export default class Composition {
430
568
  * Collection of IDs used for referencing elements
431
569
  * Not meant for live DOM. Removed before attaching to document
432
570
  */
433
- temporaryIds = new Set();
571
+ /** @type {Set<string>} */
572
+ temporaryIds;
434
573
 
435
574
  /** Flag set when template and styles have been interpolated */
436
575
  interpolated = false;
@@ -453,6 +592,24 @@ export default class Composition {
453
592
  yield this.template;
454
593
  }
455
594
 
595
+ /**
596
+ * @template T
597
+ * @param {ConstructorParameters<typeof Composition<T>>} parts
598
+ * @return {Composition<T>}
599
+ */
600
+ static compose(...parts) {
601
+ for (const [cache, comp] of compositionCache) {
602
+ if (cache.length !== parts.length) continue;
603
+ if (parts.every((part, index) => part === cache[index])) {
604
+ return comp;
605
+ }
606
+ }
607
+
608
+ const composition = new Composition(...parts);
609
+ compositionCache.set(parts, composition);
610
+ return composition;
611
+ }
612
+
456
613
  /**
457
614
  * @param {CompositionPart<T>[]} parts
458
615
  */
@@ -472,13 +629,15 @@ export default class Composition {
472
629
  return this;
473
630
  }
474
631
 
475
- /** @param {import('./typings.js').CompositionEventListener<T>} listener */
632
+ /** @param {CompositionEventListener<T>} listener */
476
633
  addCompositionEventListener(listener) {
477
634
  const key = listener.tag ?? '';
478
- if (this.events.has(key)) {
479
- this.events.get(key).push(listener);
635
+ // eslint-disable-next-line no-multi-assign
636
+ const events = (this.events ??= new Map());
637
+ if (events.has(key)) {
638
+ events.get(key).push(listener);
480
639
  } else {
481
- this.events.set(key, [listener]);
640
+ events.set(key, [listener]);
482
641
  }
483
642
  return this;
484
643
  }
@@ -490,7 +649,7 @@ export default class Composition {
490
649
  * @return {void}
491
650
  */
492
651
  #bindCompositionEventListeners(tag, target, context) {
493
- if (!this.events.has(tag)) return;
652
+ if (!this.events?.has(tag)) return;
494
653
  for (const event of this.events.get(tag)) {
495
654
  let listener;
496
655
  if (event.handleEvent) {
@@ -508,56 +667,66 @@ export default class Composition {
508
667
  * TODO: Add types and clean up closure leak
509
668
  * Updates component nodes based on data.
510
669
  * Expects data in JSON Merge Patch format
511
- *
512
670
  * @see https://www.rfc-editor.org/rfc/rfc7386
513
671
  * @template {Object} T
514
672
  * @param {Partial<T>} changes what specifically
515
673
  * @param {T} [data]
516
674
  * @param {RenderOptions<T>} [options]
517
- * @return {Function & {target:Element}} anchor
675
+ * @return {RenderDraw<T>} anchor
518
676
  */
519
677
  render(changes, data, options = {}) {
520
- // console.log('render', changes, options);
521
678
  if (!this.interpolated) {
522
- this.interpolate({ defaults: data ?? changes, injections: options?.injections });
679
+ this.interpolate({
680
+ defaults: data ?? changes,
681
+ ...options,
682
+ });
523
683
  }
524
684
 
525
685
  const instanceFragment = /** @type {DocumentFragment} */ (this.cloneable.cloneNode(true));
526
686
 
527
- const target = options.target ?? instanceFragment.firstElementChild;
528
-
529
- const isShadowRoot = target instanceof ShadowRoot;
687
+ const shadowRoot = options.shadowRoot;
688
+ const target = shadowRoot ?? options.target ?? instanceFragment.firstElementChild;
530
689
 
531
690
  /** @type {InitializationState} */
532
691
  const initState = {
533
- instanceFragment,
534
692
  lastChildNode: null,
535
693
  lastChildNodeIndex: 0,
536
694
  lastElement: null,
537
- isShadowRoot,
538
- ranFlags: [],
695
+ nodeStates: new Uint8Array(this._interpolationState.nodeIndex + 1),
696
+ searchStates: new Uint8Array(this._interpolationState.searchIndex),
539
697
  comments: [],
540
698
  nodes: [],
541
699
  caches: this.initCache.slice(),
542
- dirtyFlags: [],
543
700
  refs: [],
544
701
  options,
545
702
  };
546
703
 
547
- const nodes = initState.nodes;
704
+ const { nodes, refs, searchStates, caches } = initState;
548
705
  for (const { tag, textNodes } of this.nodesToBind) {
549
- const element = instanceFragment.getElementById(tag);
550
- initState.refs.push(element);
551
- nodes.push(element);
552
- this.#bindCompositionEventListeners(tag, element, options.context);
553
-
554
- if (!textNodes.length) continue;
706
+ /** @type {Text} */
707
+ let textNode;
708
+ if (tag === '') {
709
+ if (!textNodes.length) {
710
+ console.warn('why was root tagged?');
711
+ continue;
712
+ }
713
+ console.warn('found empty tag??');
714
+ refs.push(null);
715
+ nodes.push(null);
716
+ textNode = /** @type {Text} */ (instanceFragment.firstChild);
717
+ } else {
718
+ const element = instanceFragment.getElementById(tag);
719
+ refs.push(element);
720
+ nodes.push(element);
721
+ this.#bindCompositionEventListeners(tag, element, options.context);
722
+ if (!textNodes.length) continue;
723
+ textNode = /** @type {Text} */ (element.firstChild);
724
+ }
555
725
 
556
- let textNode = element.firstChild;
557
726
  let currentIndex = 0;
558
727
  for (const index of textNodes) {
559
728
  while (index !== currentIndex) {
560
- textNode = textNode.nextSibling;
729
+ textNode = /** @type {Text} */ (textNode.nextSibling);
561
730
  currentIndex++;
562
731
  }
563
732
  nodes.push(textNode);
@@ -570,32 +739,35 @@ export default class Composition {
570
739
  action.invocation(initState);
571
740
  }
572
741
 
742
+ /**
743
+ * @param {Partial<T>} changes
744
+ * @param {T} data
745
+ */
573
746
  const draw = (changes, data) => {
574
747
  let ranSearch = false;
575
748
  for (const prop of this.props) {
576
- if (!this.actionsByPropsUsed.has(prop)) continue;
749
+ if (!this.actionsByPropsUsed?.has(prop)) continue;
577
750
  if (!(prop in changes)) continue;
578
751
  const actions = this.actionsByPropsUsed.get(prop);
579
752
  for (const action of actions) {
580
753
  ranSearch = true;
581
- const result = executeSearch(action.search, initState, changes, data);
582
- if (result.dirty) {
583
- // console.log('dirty, updating from batch', initState.nodes[action.nodeIndex], 'with', result.value);
584
- action.invocation(initState, result.value, changes, data);
754
+ const { dirty, value } = executeSearch(action.search, initState, changes, data);
755
+ if (dirty) {
756
+ // console.log('dirty, updating from batch', initState.nodes[action.nodeIndex], 'with', value);
757
+ action.invocation(initState, value, changes, data);
585
758
  }
586
759
  }
587
760
  }
588
761
  if (!ranSearch) return;
589
- initState.ranFlags.fill(false);
590
- initState.dirtyFlags.fill(false);
762
+ searchStates.fill(0);
591
763
  };
592
764
 
593
- if (isShadowRoot) {
594
- options.context ??= target.host;
595
- if ('adoptedStyleSheets' in target) {
765
+ if (shadowRoot) {
766
+ options.context ??= shadowRoot.host;
767
+ if ('adoptedStyleSheets' in shadowRoot) {
596
768
  if (this.adoptedStyleSheets.length) {
597
- target.adoptedStyleSheets = [
598
- ...target.adoptedStyleSheets,
769
+ shadowRoot.adoptedStyleSheets = [
770
+ ...shadowRoot.adoptedStyleSheets,
599
771
  ...this.adoptedStyleSheets,
600
772
  ];
601
773
  }
@@ -611,34 +783,42 @@ export default class Composition {
611
783
  draw(changes, data);
612
784
  }
613
785
 
614
- if (isShadowRoot) {
615
- target.append(instanceFragment);
786
+ if (shadowRoot) {
787
+ shadowRoot.append(instanceFragment);
788
+ customElements.upgrade(shadowRoot);
616
789
  }
617
790
 
618
791
  draw.target = target;
792
+
793
+ /**
794
+ * @param {keyof T & string} prop
795
+ * @param {any} value
796
+ * @param {Partial<T>} [data]
797
+ */
619
798
  draw.byProp = (prop, value, data) => {
620
- if (!this.actionsByPropsUsed.has(prop)) return;
799
+ if (!this.actionsByPropsUsed?.has(prop)) return;
621
800
  let ranSearch = false;
622
801
 
623
802
  // Update search
624
- if (this.searchByQuery.has(prop)) {
803
+ if (this.searchByQuery?.has(prop)) {
625
804
  ranSearch = true;
626
805
  const search = this.searchByQuery.get(prop);
627
- const cachedValue = initState.caches[search.cacheIndex];
806
+ const cachedValue = caches[search.cacheIndex];
628
807
  if (cachedValue === value) {
629
808
  return;
630
809
  }
631
- initState.ranFlags[search.ranFlagIndex] = true;
632
- initState.caches[search.cacheIndex] = value;
633
- initState.dirtyFlags[search.dirtyIndex] = true;
810
+ caches[search.cacheIndex] = value;
811
+ searchStates[search.searchIndex] = 0b0011;
634
812
  }
635
813
 
814
+ /** @type {Partial<T>} */
636
815
  let changes;
637
816
  const actions = this.actionsByPropsUsed.get(prop);
638
817
  for (const action of actions) {
639
818
  if (action.search.query === prop) {
640
819
  action.invocation(initState, value);
641
820
  } else {
821
+ // @ts-expect-error Skip cast
642
822
  changes ??= { [prop]: value };
643
823
  data ??= changes;
644
824
  ranSearch = true;
@@ -651,8 +831,7 @@ export default class Composition {
651
831
  }
652
832
 
653
833
  if (!ranSearch) return;
654
- initState.ranFlags.fill(false);
655
- initState.dirtyFlags.fill(false);
834
+ searchStates.fill(0);
656
835
  };
657
836
  draw.state = initState;
658
837
  return draw;
@@ -660,7 +839,7 @@ export default class Composition {
660
839
 
661
840
  /**
662
841
  * @param {Attr|Text} node
663
- * @param {Element} element
842
+ * @param {Element|null} [element]
664
843
  * @param {InterpolateOptions} [options]
665
844
  * @param {string} [parsedValue]
666
845
  * @return {true|undefined} remove node
@@ -683,7 +862,7 @@ export default class Composition {
683
862
  if (!nodeValue) return;
684
863
  const trimmed = nodeValue.trim();
685
864
  if (!trimmed) return;
686
- if (attr) {
865
+ if (attr || element?.tagName === 'STYLE') {
687
866
  if (trimmed[0] !== '{') return;
688
867
  const { length } = trimmed;
689
868
  if (trimmed[length - 1] !== '}') return;
@@ -798,7 +977,7 @@ export default class Composition {
798
977
  /** @type {RenderGraphSearch} */
799
978
  let search;
800
979
 
801
- if (this.searchByQuery.has(query)) {
980
+ if (this.searchByQuery?.has(query)) {
802
981
  search = this.searchByQuery.get(query);
803
982
  } else {
804
983
  // Has subquery?
@@ -806,7 +985,7 @@ export default class Composition {
806
985
  const isSubquery = subquery !== query;
807
986
  /** @type {RenderGraphSearch} */
808
987
  let subSearch;
809
- if (isSubquery && this.searchByQuery.has(subquery)) {
988
+ if (isSubquery && this.searchByQuery?.has(subquery)) {
810
989
  subSearch = this.searchByQuery.get(subquery);
811
990
  } else {
812
991
  // Construct subsearch, even is not subquery.
@@ -846,7 +1025,7 @@ export default class Composition {
846
1025
  }
847
1026
  if (defaultValue == null && options?.injections) {
848
1027
  defaultValue = valueFromPropName(parsedValue, options.injections);
849
- console.log('default value from injection', parsedValue, { defaultValue });
1028
+ // console.log('default value from injection', parsedValue, { defaultValue });
850
1029
  }
851
1030
  }
852
1031
 
@@ -893,9 +1072,8 @@ export default class Composition {
893
1072
  inlineFunctionOptions.deepProps = deepPropsUsed;
894
1073
  }
895
1074
  subSearch = {
896
- cacheIndex: this.#interpolationState.cacheIndex++,
897
- dirtyIndex: this.#interpolationState.dirtyIndex++,
898
- ranFlagIndex: this.#interpolationState.ranFlagIndex++,
1075
+ cacheIndex: this._interpolationState.cacheIndex++,
1076
+ searchIndex: this._interpolationState.searchIndex++,
899
1077
  query: subquery,
900
1078
  defaultValue,
901
1079
  subSearch: null,
@@ -910,9 +1088,8 @@ export default class Composition {
910
1088
  }
911
1089
  if (isSubquery) {
912
1090
  search = {
913
- cacheIndex: this.#interpolationState.cacheIndex++,
914
- dirtyIndex: this.#interpolationState.dirtyIndex++,
915
- ranFlagIndex: this.#interpolationState.ranFlagIndex++,
1091
+ cacheIndex: this._interpolationState.cacheIndex++,
1092
+ searchIndex: this._interpolationState.searchIndex++,
916
1093
  query,
917
1094
  subSearch,
918
1095
  negate,
@@ -942,7 +1119,6 @@ export default class Composition {
942
1119
  let subnode = null;
943
1120
  let defaultValue = search.defaultValue;
944
1121
  if (text) {
945
- text.data = defaultValue;
946
1122
  subnode = textNodeIndex;
947
1123
  } else if (nodeName === 'mdw-if') {
948
1124
  tag = this.#tagElement(element);
@@ -960,15 +1136,15 @@ export default class Composition {
960
1136
  tag ??= this.#tagElement(element);
961
1137
 
962
1138
  // Node entry
963
- let nodeEntry = this.#interpolationState.nodeEntry;
1139
+ let nodeEntry = this._interpolationState.nodeEntry;
964
1140
  if (!nodeEntry || nodeEntry.tag !== tag) {
965
1141
  nodeEntry = {
966
1142
  tag,
967
1143
  textNodes: [],
968
1144
  };
969
- this.#interpolationState.nodeEntry = nodeEntry;
1145
+ this._interpolationState.nodeEntry = nodeEntry;
970
1146
  this.nodesToBind.push(nodeEntry);
971
- this.#interpolationState.nodeIndex++;
1147
+ this._interpolationState.nodeIndex++;
972
1148
  }
973
1149
 
974
1150
  /** @type {RenderGraphAction} */
@@ -978,25 +1154,38 @@ export default class Composition {
978
1154
  if (text) {
979
1155
  nodeEntry.textNodes.push(textNodeIndex);
980
1156
 
981
- this.#interpolationState.nodeIndex++;
1157
+ this._interpolationState.nodeIndex++;
982
1158
  action = {
983
- nodeIndex: this.#interpolationState.nodeIndex,
984
- invocation: writeDOMText,
1159
+ nodeIndex: this._interpolationState.nodeIndex,
1160
+ commentIndex: this._interpolationState.commentIndex++,
1161
+ invocation: writeDynamicNode,
985
1162
  defaultValue,
986
1163
  search,
987
1164
  };
1165
+ switch (typeof defaultValue) {
1166
+ case 'string':
1167
+ text.data = defaultValue;
1168
+ break;
1169
+ case 'number':
1170
+ // Manually coerce to string (0 will be empty otherwise)
1171
+ text.data = `${defaultValue}`;
1172
+ break;
1173
+ default:
1174
+ text.data = '';
1175
+ break;
1176
+ }
988
1177
  } else if (subnode) {
989
1178
  action = {
990
- nodeIndex: this.#interpolationState.nodeIndex,
991
- attrName: subnode,
1179
+ nodeIndex: this._interpolationState.nodeIndex,
1180
+ attrName: /** @type {string} */ (subnode),
992
1181
  defaultValue,
993
1182
  invocation: writeDOMAttribute,
994
1183
  search,
995
1184
  };
996
1185
  } else {
997
1186
  action = {
998
- nodeIndex: this.#interpolationState.nodeIndex,
999
- commentIndex: this.#interpolationState.commentIndex++,
1187
+ nodeIndex: this._interpolationState.nodeIndex,
1188
+ commentIndex: this._interpolationState.commentIndex++,
1000
1189
  defaultValue,
1001
1190
  invocation: writeDOMElementAttachedState,
1002
1191
  search,
@@ -1004,13 +1193,15 @@ export default class Composition {
1004
1193
  if (!defaultValue) {
1005
1194
  this.postInitActions.push({
1006
1195
  ...action,
1007
- invocation: writeDOMHideElementOnInit,
1196
+ invocation: writeDOMHideNodeOnInit,
1008
1197
  });
1009
1198
  }
1010
1199
  }
1011
1200
 
1012
1201
  this.addAction(action);
1013
- this.tagsWithBindings.add(tag);
1202
+ // eslint-disable-next-line no-multi-assign
1203
+ const tagsWithBindings = (this.tagsWithBindings ??= new Set());
1204
+ tagsWithBindings.add(tag);
1014
1205
  }
1015
1206
 
1016
1207
  /**
@@ -1018,6 +1209,7 @@ export default class Composition {
1018
1209
  * @return {string}
1019
1210
  */
1020
1211
  #tagElement(element) {
1212
+ if (!element) return '';
1021
1213
  let id = element.id;
1022
1214
  if (id) {
1023
1215
  if (!this.allIds.includes(id)) {
@@ -1025,7 +1217,9 @@ export default class Composition {
1025
1217
  }
1026
1218
  } else {
1027
1219
  id = generateUID();
1028
- this.temporaryIds.add(id);
1220
+ // eslint-disable-next-line no-multi-assign
1221
+ const temporaryIds = (this.temporaryIds ??= new Set());
1222
+ temporaryIds.add(id);
1029
1223
  this.allIds.push(id);
1030
1224
  element.id = id;
1031
1225
  }
@@ -1034,11 +1228,10 @@ export default class Composition {
1034
1228
 
1035
1229
  /**
1036
1230
  * TODO: Subtemplating lacks optimization, though functional.
1037
- * - Would benefit from custom type handler for arrays
1231
+ * - Would benefit from custom type handler for arrays
1038
1232
  * to avoid multi-iteration change-detection.
1039
- * - Could benefit from debounced/throttled render
1040
- * - Consider remap of {item.prop} as {array[index].prop}
1041
- *
1233
+ * - Could benefit from debounced/throttled render
1234
+ * - Consider remap of {item.prop} as {array[index].prop}
1042
1235
  * @param {Element} element
1043
1236
  * @param {InterpolateOptions} options
1044
1237
  * @return {?Composition<?>}
@@ -1066,26 +1259,26 @@ export default class Composition {
1066
1259
  element.removeAttribute('mdw-for');
1067
1260
  // Create a new composition targetting element as root
1068
1261
 
1069
- const elementAnchor = document.createElement('template');
1262
+ const elementAnchor = element.ownerDocument.createElement('template');
1070
1263
  element.replaceWith(elementAnchor);
1071
1264
  const tag = this.#tagElement(elementAnchor);
1072
1265
  // console.log('tagging placeholder element with', elementAnchor, tag);
1073
1266
 
1074
- let nodeEntry = this.#interpolationState.nodeEntry;
1267
+ let nodeEntry = this._interpolationState.nodeEntry;
1075
1268
  if (!nodeEntry || nodeEntry.tag !== tag) {
1076
1269
  nodeEntry = {
1077
1270
  tag,
1078
1271
  textNodes: [],
1079
1272
  };
1080
- this.#interpolationState.nodeEntry = nodeEntry;
1273
+ this._interpolationState.nodeEntry = nodeEntry;
1081
1274
  this.nodesToBind.push(nodeEntry);
1082
- this.#interpolationState.nodeIndex++;
1083
- console.log('adding node entry', tag, this.#interpolationState.nodeIndex);
1275
+ this._interpolationState.nodeIndex++;
1084
1276
  }
1085
1277
 
1086
1278
  const newComposition = new Composition();
1087
1279
  newComposition.template.append(element);
1088
1280
  // Move uninterpolated element to new composition template.
1281
+ /** @type {InterpolateOptions['injections']} */
1089
1282
  const injections = {
1090
1283
  ...options.injections,
1091
1284
  [valueName]: null,
@@ -1095,24 +1288,23 @@ export default class Composition {
1095
1288
  const propsUsed = [iterableName];
1096
1289
  /** @type {RenderGraphSearch} */
1097
1290
  const search = {
1098
- cacheIndex: this.#interpolationState.cacheIndex++,
1099
- dirtyIndex: this.#interpolationState.dirtyIndex++,
1100
- ranFlagIndex: this.#interpolationState.ranFlagIndex++,
1291
+ cacheIndex: this._interpolationState.cacheIndex++,
1292
+ searchIndex: this._interpolationState.searchIndex++,
1293
+ query: null,
1294
+ prop: null,
1295
+ deepProp: null,
1101
1296
  propsUsed,
1102
1297
  deepPropsUsed: [[iterableName]],
1103
1298
  defaultValue: {},
1104
- invocation(state, changes, data) {
1105
- // Return unique to always specify dirty
1106
- return {};
1107
- },
1299
+ invocation: null,
1108
1300
  };
1109
1301
 
1110
1302
  /** @type {RenderGraphAction} */
1111
1303
  const action = {
1112
1304
  defaultValue: null,
1113
- nodeIndex: this.#interpolationState.nodeIndex,
1305
+ nodeIndex: this._interpolationState.nodeIndex,
1114
1306
  search,
1115
- commentIndex: this.#interpolationState.commentIndex++,
1307
+ commentIndex: this._interpolationState.commentIndex++,
1116
1308
  injections,
1117
1309
  invocation(state, value, changes, data) {
1118
1310
  if (!newComposition.adapter) {
@@ -1120,7 +1312,7 @@ export default class Composition {
1120
1312
  const instanceAnchorElement = state.nodes[this.nodeIndex];
1121
1313
  const anchorNode = createEmptyComment();
1122
1314
  // Avoid leak
1123
- state.nodes[this.commentIndex] = anchorNode;
1315
+ state.comments[this.commentIndex] = anchorNode;
1124
1316
  instanceAnchorElement.replaceWith(anchorNode);
1125
1317
  newComposition.adapter = new CompositionAdapter({
1126
1318
  anchorNode,
@@ -1145,8 +1337,9 @@ export default class Composition {
1145
1337
  const needTargetAll = newComposition.props.some((prop) => prop !== iterableName && prop in changes);
1146
1338
 
1147
1339
  adapter.startBatch();
1148
- if (!needTargetAll && !Array.isArray(changeList)) {
1149
- const iterator = Array.isArray(changeList) ? changeList.entries() : Object.entries(changeList);
1340
+ let isArray;
1341
+ if (!needTargetAll && !(isArray = Array.isArray(changeList))) {
1342
+ const iterator = isArray ? changeList.entries() : Object.entries(changeList);
1150
1343
  // console.log('changeList render', iterator);
1151
1344
  for (const [key, change] of iterator) {
1152
1345
  if (key === 'length') continue;
@@ -1204,7 +1397,9 @@ export default class Composition {
1204
1397
  propsUsed.push(...newComposition.props);
1205
1398
  this.addSearch(search);
1206
1399
  this.addAction(action);
1207
- this.tagsWithBindings.add(tag);
1400
+ // eslint-disable-next-line no-multi-assign
1401
+ const tagsWithBindings = (this.tagsWithBindings ??= new Set());
1402
+ tagsWithBindings.add(tag);
1208
1403
  // console.log('adding', iterable, 'bind to', this);
1209
1404
  // this.addBinding(iterable, entry);
1210
1405
  return newComposition;
@@ -1232,31 +1427,23 @@ export default class Composition {
1232
1427
  switch (node.nodeType) {
1233
1428
  case Node.ELEMENT_NODE:
1234
1429
  element = /** @type {Element} */ (node);
1235
- if (element instanceof HTMLTemplateElement) {
1236
- node = treeWalker.nextSibling();
1430
+ if (element.tagName === 'TEMPLATE') {
1431
+ while (element.contains(node = treeWalker.nextNode()));
1237
1432
  continue;
1238
1433
  }
1239
- if (node instanceof HTMLStyleElement) {
1240
- // Move style elements out of cloneable
1241
- if (node.parentNode === this.cloneable) {
1242
- this.styles.push(node);
1243
- node.remove();
1244
- node = treeWalker.nextSibling();
1245
- continue;
1246
- }
1247
- console.warn('<style> element not moved');
1248
- }
1249
- if (node instanceof HTMLScriptElement) {
1434
+
1435
+ if (element.tagName === 'SCRIPT') {
1250
1436
  console.warn('<script> element found.');
1251
- node.remove();
1252
- node = treeWalker.nextSibling();
1437
+ while (element.contains(node = treeWalker.nextNode()));
1253
1438
  continue;
1254
1439
  }
1255
1440
 
1256
1441
  if (element.hasAttribute('mdw-for')) {
1257
- node = treeWalker.nextSibling();
1442
+ while (element.contains(node = treeWalker.nextNode()));
1258
1443
  this.#interpolateIterable(element, options);
1444
+ continue;
1259
1445
  } else {
1446
+ // @ts-expect-error Bad typings
1260
1447
  const idAttr = element.attributes.id;
1261
1448
  if (idAttr) {
1262
1449
  this.#interpolateNode(idAttr, element, options);
@@ -1270,14 +1457,13 @@ export default class Composition {
1270
1457
 
1271
1458
  break;
1272
1459
  case Node.TEXT_NODE:
1273
- element = node.parentNode;
1460
+ element = node.parentElement;
1274
1461
  if (this.#interpolateNode(/** @type {Text} */ (node), element, options)) {
1275
1462
  const nextNode = treeWalker.nextNode();
1276
- node.remove();
1463
+ /** @type {Text} */ (node).remove();
1277
1464
  node = nextNode;
1278
1465
  continue;
1279
1466
  }
1280
-
1281
1467
  break;
1282
1468
  default:
1283
1469
  throw new Error(`Unexpected node type: ${node.nodeType}`);
@@ -1296,10 +1482,12 @@ export default class Composition {
1296
1482
  );
1297
1483
  }
1298
1484
 
1299
- this.props = [...this.actionsByPropsUsed.keys()];
1485
+ this.props = this.actionsByPropsUsed
1486
+ ? [...this.actionsByPropsUsed.keys()]
1487
+ : [];
1300
1488
 
1301
1489
  for (const id of this.allIds) {
1302
- if (!this.tagsWithBindings.has(id)) {
1490
+ if (!this.tagsWithBindings?.has(id)) {
1303
1491
  this.nodesToBind.push({
1304
1492
  tag: id,
1305
1493
  textNodes: [],
@@ -1308,7 +1496,6 @@ export default class Composition {
1308
1496
  }
1309
1497
 
1310
1498
  this.tags = this.nodesToBind.map((n) => n.tag);
1311
-
1312
1499
  this.interpolated = true;
1313
1500
 
1314
1501
  // console.log('Cloneable', [...this.cloneable.children].map((child) => child.outerHTML).join('\n'));
@@ -1321,7 +1508,9 @@ export default class Composition {
1321
1508
  addSearch(search) {
1322
1509
  this.searches.push(search);
1323
1510
  if (search.query) {
1324
- this.searchByQuery.set(search.query, search);
1511
+ // eslint-disable-next-line no-multi-assign
1512
+ const searchByQuery = (this.searchByQuery ??= new Map());
1513
+ searchByQuery.set(search.query, search);
1325
1514
  this.initCache[search.cacheIndex] = search.defaultValue;
1326
1515
  }
1327
1516
  return search;
@@ -1332,12 +1521,13 @@ export default class Composition {
1332
1521
  * @return {RenderGraphAction}
1333
1522
  */
1334
1523
  addAction(action) {
1335
- this.actions.push(action);
1524
+ // eslint-disable-next-line no-multi-assign
1525
+ const actionsByPropsUsed = (this.actionsByPropsUsed ??= new Map());
1336
1526
  for (const prop of action.search.propsUsed) {
1337
- if (this.actionsByPropsUsed.has(prop)) {
1338
- this.actionsByPropsUsed.get(prop).push(action);
1527
+ if (actionsByPropsUsed.has(prop)) {
1528
+ actionsByPropsUsed.get(prop).push(action);
1339
1529
  } else {
1340
- this.actionsByPropsUsed.set(prop, [action]);
1530
+ actionsByPropsUsed.set(prop, [action]);
1341
1531
  }
1342
1532
  }
1343
1533
  return action;