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