@remix-run/ui 0.1.2 → 0.3.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 (318) hide show
  1. package/README.md +9 -7
  2. package/dist/animation/demos/animation.demo.d.ts +5 -0
  3. package/dist/animation/demos/animation.demo.js +92 -0
  4. package/dist/animation/demos/animation.demo.js.map +1 -0
  5. package/dist/animation/demos/aspect-ratio.d.ts +2 -0
  6. package/dist/animation/demos/aspect-ratio.js +69 -0
  7. package/dist/animation/demos/aspect-ratio.js.map +1 -0
  8. package/dist/animation/demos/bouncy-switch.d.ts +2 -0
  9. package/dist/animation/demos/bouncy-switch.js +36 -0
  10. package/dist/animation/demos/bouncy-switch.js.map +1 -0
  11. package/dist/animation/demos/color-interpolation.d.ts +1 -0
  12. package/dist/animation/demos/color-interpolation.js +53 -0
  13. package/dist/animation/demos/color-interpolation.js.map +1 -0
  14. package/dist/animation/demos/cube.d.ts +2 -0
  15. package/dist/animation/demos/cube.js +86 -0
  16. package/dist/animation/demos/cube.js.map +1 -0
  17. package/dist/animation/demos/default-animate.d.ts +2 -0
  18. package/dist/animation/demos/default-animate.js +89 -0
  19. package/dist/animation/demos/default-animate.js.map +1 -0
  20. package/dist/animation/demos/drag-release.d.ts +24 -0
  21. package/dist/animation/demos/drag-release.js +96 -0
  22. package/dist/animation/demos/drag-release.js.map +1 -0
  23. package/dist/animation/demos/enter.d.ts +1 -0
  24. package/dist/animation/demos/enter.js +19 -0
  25. package/dist/animation/demos/enter.js.map +1 -0
  26. package/dist/animation/demos/exit.d.ts +2 -0
  27. package/dist/animation/demos/exit.js +58 -0
  28. package/dist/animation/demos/exit.js.map +1 -0
  29. package/dist/animation/demos/flip-toggle.d.ts +2 -0
  30. package/dist/animation/demos/flip-toggle.js +37 -0
  31. package/dist/animation/demos/flip-toggle.js.map +1 -0
  32. package/dist/animation/demos/hold-to-confirm.d.ts +13 -0
  33. package/dist/animation/demos/hold-to-confirm.js +211 -0
  34. package/dist/animation/demos/hold-to-confirm.js.map +1 -0
  35. package/dist/animation/demos/html-content.d.ts +2 -0
  36. package/dist/animation/demos/html-content.js +30 -0
  37. package/dist/animation/demos/html-content.js.map +1 -0
  38. package/dist/animation/demos/interruptible-keyframes.d.ts +1 -0
  39. package/dist/animation/demos/interruptible-keyframes.js +48 -0
  40. package/dist/animation/demos/interruptible-keyframes.js.map +1 -0
  41. package/dist/animation/demos/keyframes.d.ts +1 -0
  42. package/dist/animation/demos/keyframes.js +24 -0
  43. package/dist/animation/demos/keyframes.js.map +1 -0
  44. package/dist/animation/demos/material-ripple.d.ts +2 -0
  45. package/dist/animation/demos/material-ripple.js +127 -0
  46. package/dist/animation/demos/material-ripple.js.map +1 -0
  47. package/dist/animation/demos/mixin-presence-list.d.ts +2 -0
  48. package/dist/animation/demos/mixin-presence-list.js +88 -0
  49. package/dist/animation/demos/mixin-presence-list.js.map +1 -0
  50. package/dist/animation/demos/mixin-reclaim.d.ts +2 -0
  51. package/dist/animation/demos/mixin-reclaim.js +90 -0
  52. package/dist/animation/demos/mixin-reclaim.js.map +1 -0
  53. package/dist/animation/demos/multi-state-badge.d.ts +2 -0
  54. package/dist/animation/demos/multi-state-badge.js +231 -0
  55. package/dist/animation/demos/multi-state-badge.js.map +1 -0
  56. package/dist/animation/demos/press.d.ts +2 -0
  57. package/dist/animation/demos/press.js +63 -0
  58. package/dist/animation/demos/press.js.map +1 -0
  59. package/dist/animation/demos/reordering.d.ts +2 -0
  60. package/dist/animation/demos/reordering.js +50 -0
  61. package/dist/animation/demos/reordering.js.map +1 -0
  62. package/dist/animation/demos/rolling-square.d.ts +2 -0
  63. package/dist/animation/demos/rolling-square.js +42 -0
  64. package/dist/animation/demos/rolling-square.js.map +1 -0
  65. package/dist/animation/demos/rotate.d.ts +1 -0
  66. package/dist/animation/demos/rotate.js +18 -0
  67. package/dist/animation/demos/rotate.js.map +1 -0
  68. package/dist/animation/demos/shared-layout.d.ts +2 -0
  69. package/dist/animation/demos/shared-layout.js +114 -0
  70. package/dist/animation/demos/shared-layout.js.map +1 -0
  71. package/dist/animation/demos/spring.demo.d.ts +6 -0
  72. package/dist/animation/demos/spring.demo.js +304 -0
  73. package/dist/animation/demos/spring.demo.js.map +1 -0
  74. package/dist/animation/demos/transition-options.d.ts +1 -0
  75. package/dist/animation/demos/transition-options.js +21 -0
  76. package/dist/animation/demos/transition-options.js.map +1 -0
  77. package/dist/animation/spring.d.ts +24 -31
  78. package/dist/animation/spring.js +31 -8
  79. package/dist/animation/spring.js.map +1 -1
  80. package/dist/components/accordion/accordion.d.ts +6 -6
  81. package/dist/components/accordion/accordion.js.map +1 -1
  82. package/dist/components/accordion/demos/card.demo.d.ts +1 -0
  83. package/dist/components/accordion/demos/card.demo.js +1 -0
  84. package/dist/components/accordion/demos/card.demo.js.map +1 -1
  85. package/dist/components/accordion/demos/multiple.demo.d.ts +1 -0
  86. package/dist/components/accordion/demos/multiple.demo.js +1 -0
  87. package/dist/components/accordion/demos/multiple.demo.js.map +1 -1
  88. package/dist/components/accordion/demos/overview.demo.d.ts +1 -0
  89. package/dist/components/accordion/demos/overview.demo.js +1 -0
  90. package/dist/components/accordion/demos/overview.demo.js.map +1 -1
  91. package/dist/components/anchor/anchor.d.ts +8 -1
  92. package/dist/components/anchor/anchor.js +30 -11
  93. package/dist/components/anchor/anchor.js.map +1 -1
  94. package/dist/components/breadcrumbs/{demos/basic.demo.d.ts → basic.demo.d.ts} +1 -0
  95. package/dist/components/breadcrumbs/{demos/basic.demo.js → basic.demo.js} +1 -0
  96. package/dist/components/breadcrumbs/basic.demo.js.map +1 -0
  97. package/dist/components/breadcrumbs/breadcrumbs.d.ts +1 -1
  98. package/dist/components/breadcrumbs/{demos/separator.demo.d.ts → separator.demo.d.ts} +1 -0
  99. package/dist/components/breadcrumbs/{demos/separator.demo.js → separator.demo.js} +1 -0
  100. package/dist/components/breadcrumbs/separator.demo.js.map +1 -0
  101. package/dist/components/button/button.d.ts +4 -4
  102. package/dist/components/button/button.js +3 -1
  103. package/dist/components/button/button.js.map +1 -1
  104. package/dist/components/button/demos/aliases.demo.d.ts +1 -0
  105. package/dist/components/button/demos/aliases.demo.js +1 -0
  106. package/dist/components/button/demos/aliases.demo.js.map +1 -1
  107. package/dist/components/button/demos/basic.demo.d.ts +1 -0
  108. package/dist/components/button/demos/basic.demo.js +1 -0
  109. package/dist/components/button/demos/basic.demo.js.map +1 -1
  110. package/dist/components/button/demos/states.demo.d.ts +1 -0
  111. package/dist/components/button/demos/states.demo.js +1 -0
  112. package/dist/components/button/demos/states.demo.js.map +1 -1
  113. package/dist/components/combobox/combobox.d.ts +10 -10
  114. package/dist/components/combobox/combobox.js.map +1 -1
  115. package/dist/components/combobox/{demos/overview.demo.d.ts → overview.demo.d.ts} +1 -0
  116. package/dist/components/combobox/{demos/overview.demo.js → overview.demo.js} +1 -0
  117. package/dist/components/combobox/overview.demo.js.map +1 -0
  118. package/dist/components/listbox/listbox.d.ts +3 -3
  119. package/dist/components/listbox/listbox.js.map +1 -1
  120. package/dist/components/listbox/{demos/overview.demo.d.ts → overview.demo.d.ts} +1 -0
  121. package/dist/components/listbox/{demos/overview.demo.js → overview.demo.js} +1 -0
  122. package/dist/components/listbox/overview.demo.js.map +1 -0
  123. package/dist/components/menu/demos/bubbling.demo.d.ts +1 -0
  124. package/dist/components/menu/demos/bubbling.demo.js +1 -0
  125. package/dist/components/menu/demos/bubbling.demo.js.map +1 -1
  126. package/dist/components/menu/demos/context-trigger.demo.d.ts +7 -0
  127. package/dist/components/menu/demos/context-trigger.demo.js +87 -0
  128. package/dist/components/menu/demos/context-trigger.demo.js.map +1 -0
  129. package/dist/components/menu/demos/overview.demo.d.ts +1 -0
  130. package/dist/components/menu/demos/overview.demo.js +1 -0
  131. package/dist/components/menu/demos/overview.demo.js.map +1 -1
  132. package/dist/components/menu/menu.d.ts +15 -12
  133. package/dist/components/menu/menu.js +55 -0
  134. package/dist/components/menu/menu.js.map +1 -1
  135. package/dist/components/popover/{demos/overview.demo.d.ts → overview.demo.d.ts} +1 -0
  136. package/dist/components/popover/{demos/overview.demo.js → overview.demo.js} +1 -0
  137. package/dist/components/popover/overview.demo.js.map +1 -0
  138. package/dist/components/popover/popover.d.ts +7 -7
  139. package/dist/components/popover/popover.js +11 -4
  140. package/dist/components/popover/popover.js.map +1 -1
  141. package/dist/components/select/{demos/deconstructed.demo.d.ts → deconstructed.demo.d.ts} +1 -0
  142. package/dist/components/select/{demos/deconstructed.demo.js → deconstructed.demo.js} +1 -0
  143. package/dist/components/select/deconstructed.demo.js.map +1 -0
  144. package/dist/components/select/{demos/overview.demo.d.ts → overview.demo.d.ts} +1 -0
  145. package/dist/components/select/{demos/overview.demo.js → overview.demo.js} +1 -0
  146. package/dist/components/select/overview.demo.js.map +1 -0
  147. package/dist/components/select/select.d.ts +10 -10
  148. package/dist/components/select/select.js +1 -1
  149. package/dist/components/select/select.js.map +1 -1
  150. package/dist/components/tabs/tabs.d.ts +3 -3
  151. package/dist/index.d.ts +1 -1
  152. package/dist/index.js.map +1 -1
  153. package/dist/interactions/keydown/keydown.d.ts +1 -1
  154. package/dist/interactions/outside-click/outside-click-mixin.d.ts +2 -1
  155. package/dist/interactions/typeahead/typeahead-mixin.d.ts +3 -1
  156. package/dist/runtime/client-entries.d.ts +1 -2
  157. package/dist/runtime/client-entries.js.map +1 -1
  158. package/dist/runtime/component.d.ts +20 -17
  159. package/dist/runtime/component.js +15 -6
  160. package/dist/runtime/component.js.map +1 -1
  161. package/dist/runtime/core/attributes.d.ts +3 -1
  162. package/dist/runtime/core/attributes.js +19 -1
  163. package/dist/runtime/core/attributes.js.map +1 -1
  164. package/dist/runtime/core/props.js +6 -5
  165. package/dist/runtime/core/props.js.map +1 -1
  166. package/dist/runtime/demos/basic.demo.d.ts +6 -0
  167. package/dist/runtime/demos/basic.demo.js +16 -0
  168. package/dist/runtime/demos/basic.demo.js.map +1 -0
  169. package/dist/runtime/demos/controlled-uncontrolled-values.demo.d.ts +6 -0
  170. package/dist/runtime/demos/controlled-uncontrolled-values.demo.js +77 -0
  171. package/dist/runtime/demos/controlled-uncontrolled-values.demo.js.map +1 -0
  172. package/dist/runtime/demos/keyed-list.demo.d.ts +6 -0
  173. package/dist/runtime/demos/keyed-list.demo.js +62 -0
  174. package/dist/runtime/demos/keyed-list.demo.js.map +1 -0
  175. package/dist/runtime/demos/readme.demo.d.ts +5 -0
  176. package/dist/runtime/demos/readme.demo.js +396 -0
  177. package/dist/runtime/demos/readme.demo.js.map +1 -0
  178. package/dist/runtime/diff-dom.js +194 -21
  179. package/dist/runtime/diff-dom.js.map +1 -1
  180. package/dist/runtime/dom.d.ts +16 -9
  181. package/dist/runtime/frame.d.ts +7 -0
  182. package/dist/runtime/frame.js +176 -41
  183. package/dist/runtime/frame.js.map +1 -1
  184. package/dist/runtime/jsx.d.ts +3 -4
  185. package/dist/runtime/jsx.js.map +1 -1
  186. package/dist/runtime/mixins/link-mixin.d.ts +5 -1
  187. package/dist/runtime/mixins/link-mixin.js.map +1 -1
  188. package/dist/runtime/mixins/mixin.d.ts +2 -1
  189. package/dist/runtime/mixins/mixin.js.map +1 -1
  190. package/dist/runtime/mixins/ref-mixin.d.ts +2 -1
  191. package/dist/runtime/mixins/ref-mixin.js.map +1 -1
  192. package/dist/runtime/reconcile.d.ts +1 -1
  193. package/dist/runtime/reconcile.js +19 -5
  194. package/dist/runtime/reconcile.js.map +1 -1
  195. package/dist/runtime/scheduler.d.ts +10 -10
  196. package/dist/runtime/scheduler.js +1 -1
  197. package/dist/runtime/scheduler.js.map +1 -1
  198. package/dist/runtime/svg-attributes.js +1 -0
  199. package/dist/runtime/svg-attributes.js.map +1 -1
  200. package/dist/runtime/to-vnode.js +2 -2
  201. package/dist/runtime/to-vnode.js.map +1 -1
  202. package/dist/runtime/vnode.d.ts +6 -1
  203. package/dist/runtime/vnode.js +4 -0
  204. package/dist/runtime/vnode.js.map +1 -1
  205. package/dist/server/stream.d.ts +2 -0
  206. package/dist/server/stream.js +82 -11
  207. package/dist/server/stream.js.map +1 -1
  208. package/dist/style/css-mixin.d.ts +2 -1
  209. package/dist/style/css-mixin.js.map +1 -1
  210. package/dist/style/index.d.ts +1 -1
  211. package/dist/style/style.js +27 -10
  212. package/dist/style/style.js.map +1 -1
  213. package/dist/style/stylesheet.d.ts +13 -13
  214. package/dist/style/stylesheet.js.map +1 -1
  215. package/dist/theme/contract.d.ts +24 -222
  216. package/dist/theme/contract.js +2 -2
  217. package/dist/theme/contract.js.map +1 -1
  218. package/dist/theme/glyph-contract.d.ts +1 -3
  219. package/dist/theme/glyph-contract.js.map +1 -1
  220. package/dist/theme/presets/rmx-01/index.d.ts +4 -2
  221. package/dist/theme/presets/rmx-01/index.js.map +1 -1
  222. package/dist/utils/scroll-lock.d.ts +3 -3
  223. package/dist/utils/scroll-lock.js.map +1 -1
  224. package/package.json +4 -4
  225. package/src/animation/README.md +7 -5
  226. package/src/animation/demos/animation.demo.tsx +194 -0
  227. package/src/animation/demos/aspect-ratio.tsx +120 -0
  228. package/src/animation/demos/bouncy-switch.tsx +47 -0
  229. package/src/animation/demos/color-interpolation.tsx +73 -0
  230. package/src/animation/demos/cube.tsx +116 -0
  231. package/src/animation/demos/default-animate.tsx +129 -0
  232. package/src/animation/demos/drag-release.ts +118 -0
  233. package/src/animation/demos/enter.tsx +22 -0
  234. package/src/animation/demos/exit.tsx +79 -0
  235. package/src/animation/demos/flip-toggle.tsx +46 -0
  236. package/src/animation/demos/hold-to-confirm.tsx +317 -0
  237. package/src/animation/demos/html-content.tsx +41 -0
  238. package/src/animation/demos/interruptible-keyframes.tsx +62 -0
  239. package/src/animation/demos/keyframes.tsx +29 -0
  240. package/src/animation/demos/material-ripple.tsx +158 -0
  241. package/src/animation/demos/mixin-presence-list.tsx +121 -0
  242. package/src/animation/demos/mixin-reclaim.tsx +125 -0
  243. package/src/animation/demos/multi-state-badge.tsx +426 -0
  244. package/src/animation/demos/press.tsx +67 -0
  245. package/src/animation/demos/reordering.tsx +65 -0
  246. package/src/animation/demos/rolling-square.tsx +57 -0
  247. package/src/animation/demos/rotate.tsx +21 -0
  248. package/src/animation/demos/shared-layout.tsx +211 -0
  249. package/src/animation/demos/spring.demo.tsx +385 -0
  250. package/src/animation/demos/transition-options.tsx +24 -0
  251. package/src/animation/spring.ts +43 -9
  252. package/src/components/accordion/accordion.tsx +7 -5
  253. package/src/components/accordion/demos/card.demo.tsx +1 -0
  254. package/src/components/accordion/demos/multiple.demo.tsx +1 -0
  255. package/src/components/accordion/demos/overview.demo.tsx +1 -0
  256. package/src/components/anchor/README.md +11 -3
  257. package/src/components/anchor/anchor.ts +54 -13
  258. package/src/components/breadcrumbs/{demos/basic.demo.tsx → basic.demo.tsx} +1 -0
  259. package/src/components/breadcrumbs/breadcrumbs.tsx +1 -1
  260. package/src/components/breadcrumbs/{demos/separator.demo.tsx → separator.demo.tsx} +1 -0
  261. package/src/components/button/button.tsx +30 -18
  262. package/src/components/button/demos/aliases.demo.tsx +1 -0
  263. package/src/components/button/demos/basic.demo.tsx +1 -0
  264. package/src/components/button/demos/states.demo.tsx +1 -0
  265. package/src/components/combobox/README.md +1 -1
  266. package/src/components/combobox/combobox.tsx +30 -12
  267. package/src/components/combobox/{demos/overview.demo.tsx → overview.demo.tsx} +1 -0
  268. package/src/components/glyph/glyph.tsx +2 -2
  269. package/src/components/listbox/listbox.ts +88 -83
  270. package/src/components/listbox/{demos/overview.demo.tsx → overview.demo.tsx} +1 -0
  271. package/src/components/menu/README.md +26 -2
  272. package/src/components/menu/demos/bubbling.demo.tsx +1 -0
  273. package/src/components/menu/demos/context-trigger.demo.tsx +133 -0
  274. package/src/components/menu/demos/overview.demo.tsx +1 -0
  275. package/src/components/menu/menu.tsx +100 -16
  276. package/src/components/popover/{demos/overview.demo.tsx → overview.demo.tsx} +1 -0
  277. package/src/components/popover/popover.ts +101 -81
  278. package/src/components/select/{demos/deconstructed.demo.tsx → deconstructed.demo.tsx} +1 -0
  279. package/src/components/select/{demos/overview.demo.tsx → overview.demo.tsx} +1 -0
  280. package/src/components/select/select.tsx +27 -10
  281. package/src/index.ts +1 -0
  282. package/src/runtime/client-entries.ts +1 -5
  283. package/src/runtime/component.ts +37 -15
  284. package/src/runtime/core/attributes.ts +21 -0
  285. package/src/runtime/core/props.ts +6 -4
  286. package/src/runtime/demos/basic.demo.tsx +21 -0
  287. package/src/runtime/demos/controlled-uncontrolled-values.demo.tsx +177 -0
  288. package/src/runtime/demos/keyed-list.demo.tsx +94 -0
  289. package/src/runtime/demos/readme.demo.tsx +702 -0
  290. package/src/runtime/diff-dom.ts +270 -20
  291. package/src/runtime/dom.ts +16 -9
  292. package/src/runtime/frame.ts +218 -41
  293. package/src/runtime/jsx.ts +7 -11
  294. package/src/runtime/mixins/link-mixin.ts +88 -86
  295. package/src/runtime/mixins/mixin.ts +9 -1
  296. package/src/runtime/mixins/ref-mixin.ts +4 -5
  297. package/src/runtime/reconcile.ts +23 -14
  298. package/src/runtime/scheduler.ts +20 -20
  299. package/src/runtime/svg-attributes.ts +1 -0
  300. package/src/runtime/to-vnode.ts +2 -2
  301. package/src/runtime/vnode.ts +10 -0
  302. package/src/server/README.md +4 -2
  303. package/src/server/stream.ts +84 -10
  304. package/src/style/css-mixin.ts +6 -1
  305. package/src/style/index.ts +1 -2
  306. package/src/style/style.ts +29 -10
  307. package/src/style/stylesheet.ts +14 -2
  308. package/src/theme/contract.ts +37 -5
  309. package/src/theme/glyph-contract.ts +3 -1
  310. package/src/theme/presets/rmx-01/index.ts +4 -2
  311. package/src/utils/scroll-lock.ts +7 -3
  312. package/dist/components/breadcrumbs/demos/basic.demo.js.map +0 -1
  313. package/dist/components/breadcrumbs/demos/separator.demo.js.map +0 -1
  314. package/dist/components/combobox/demos/overview.demo.js.map +0 -1
  315. package/dist/components/listbox/demos/overview.demo.js.map +0 -1
  316. package/dist/components/popover/demos/overview.demo.js.map +0 -1
  317. package/dist/components/select/demos/deconstructed.demo.js.map +0 -1
  318. package/dist/components/select/demos/overview.demo.js.map +0 -1
@@ -129,6 +129,7 @@ export type FrameContext = {
129
129
  namedFrames: Map<string, FrameHandle>
130
130
  regionTailRef?: ChildNode | null
131
131
  regionParent?: ParentNode | null
132
+ signal?: AbortSignal
132
133
  }
133
134
 
134
135
  type FrameInit = {
@@ -153,6 +154,15 @@ export type Frame = {
153
154
  render: (content: InternalFrameContent, options?: RenderOptions) => Promise<void>
154
155
  ready: () => Promise<void>
155
156
  flush: () => void
157
+ clearPendingTemplateWatch: () => void
158
+ isDisplayingResolvedContent: () => boolean
159
+ startInheritedReload: (signal?: AbortSignal) => void
160
+ updateMarker: (marker: FrameMarkerData, options?: RenderOptions) => Promise<void>
161
+ renderMarkerContent: (
162
+ marker: FrameMarkerData,
163
+ content: InternalFrameContent,
164
+ options?: RenderOptions,
165
+ ) => Promise<void>
156
166
  dispose: () => void
157
167
  handle: FrameHandle
158
168
  }
@@ -161,15 +171,21 @@ type RenderOptions = {
161
171
  flushKind?: FlushKind
162
172
  initialHydrationTracker?: InitialHydrationTracker
163
173
  signal?: AbortSignal
174
+ contentStatus?: 'pending' | 'resolved'
164
175
  }
165
176
 
166
177
  export function createFrame(root: FrameRoot, init: FrameInit): Frame {
167
178
  let container = createContainer(root)
168
- let observers: MutationObserver[] = []
169
- let subscriptions: Array<() => void> = []
170
179
  let contentRoot: VirtualRoot | undefined
171
180
  let reloadController: AbortController | undefined
172
181
  let styleManager = init.styleManager ?? createStyleManager()
182
+ let currentMarker = init.marker
183
+ let displayedContentStatus: 'pending' | 'resolved' = init.marker?.status ?? 'resolved'
184
+ let pendingTemplateMarkerId: string | undefined
185
+ let pendingTemplateObserver: MutationObserver | undefined
186
+ let pendingTemplateUnsubscribe: (() => void) | undefined
187
+ let inheritedReloadPending = false
188
+ let inheritedReloadAbortUnsubscribe: (() => void) | undefined
173
189
 
174
190
  // Merge any rmx-data found in the current document once at startup.
175
191
  mergeRmxDataFromDocument(init.data, container.doc)
@@ -184,6 +200,7 @@ export function createFrame(root: FrameRoot, init: FrameInit): Frame {
184
200
  let controller = new AbortController()
185
201
  reloadController = controller
186
202
  frame.dispatchEvent(new Event('reloadStart'))
203
+ startSubFrameInheritedReloads(getContentNodes(), controller.signal)
187
204
  try {
188
205
  let content = await init.resolveFrame(frame.src, controller.signal, frameName)
189
206
  if (reloadController !== controller || controller.signal.aborted) return controller.signal
@@ -250,6 +267,7 @@ export function createFrame(root: FrameRoot, init: FrameInit): Frame {
250
267
 
251
268
  if (options?.signal?.aborted) return
252
269
  contentRoot.render(content)
270
+ displayedContentStatus = options?.contentStatus ?? 'resolved'
253
271
  return
254
272
  }
255
273
 
@@ -290,17 +308,20 @@ export function createFrame(root: FrameRoot, init: FrameInit): Frame {
290
308
  ...context,
291
309
  regionParent: container.doc.documentElement,
292
310
  regionTailRef: null,
311
+ signal: options?.signal,
293
312
  })
294
313
  diffNodes([container.doc.body], [parsed.body], {
295
314
  ...context,
296
315
  regionParent: container.doc.documentElement,
297
316
  regionTailRef: null,
317
+ signal: options?.signal,
298
318
  })
299
319
 
300
320
  let bodyContainer = createElementContainer(container.doc.body)
301
321
  if (options?.signal?.aborted) return
302
322
  scheduleHydrationInContainer(bodyContainer, context, options?.initialHydrationTracker)
303
- createSubFrames(bodyContainer.childNodes, context)
323
+ await createSubFrames(bodyContainer.childNodes, context, options)
324
+ displayedContentStatus = options?.contentStatus ?? 'resolved'
304
325
  return
305
326
  }
306
327
 
@@ -320,11 +341,13 @@ export function createFrame(root: FrameRoot, init: FrameInit): Frame {
320
341
  ...context,
321
342
  regionTailRef: container.regionTailRef,
322
343
  regionParent: container.regionParent,
344
+ signal: options?.signal,
323
345
  })
324
346
 
325
347
  if (options?.signal?.aborted) return
326
348
  scheduleHydrationInContainer(container, context, options?.initialHydrationTracker)
327
- createSubFrames(container.childNodes, context)
349
+ await createSubFrames(container.childNodes, context, options)
350
+ displayedContentStatus = options?.contentStatus ?? 'resolved'
328
351
  }
329
352
 
330
353
  function createFrameContentRoot(): VirtualRoot {
@@ -368,30 +391,11 @@ export function createFrame(root: FrameRoot, init: FrameInit): Frame {
368
391
  let initialHydrationTracker = createInitialHydrationTracker()
369
392
 
370
393
  context.styleManager.replaceServerStyles(collectFrameServerStyleTags(container))
371
- createSubFrames(container.childNodes, context)
394
+ await createSubFrames(container.childNodes, context)
372
395
  scheduleHydrationInContainer(container, context, initialHydrationTracker)
373
396
 
374
- if (init.marker?.status === 'pending') {
375
- let markerId = init.marker.id
376
- let early = consumeFrameTemplate(markerId) ?? getEarlyFrameContent(markerId)
377
- if (early) {
378
- await render(early, { initialHydrationTracker })
379
- } else {
380
- let observer = setupTemplateObserver()
381
- let unsubscribe = subscribeFrameTemplate(markerId, async (fragment) => {
382
- unsubscribe()
383
- await render(fragment)
384
- observer.disconnect()
385
- })
386
- subscriptions.push(unsubscribe)
387
- let buffered = consumeFrameTemplate(markerId)
388
- if (buffered) {
389
- unsubscribe()
390
- await render(buffered)
391
- observer.disconnect()
392
- }
393
- observers.push(observer)
394
- }
397
+ if (currentMarker?.status === 'pending') {
398
+ await watchPendingFrameTemplate(currentMarker, initialHydrationTracker)
395
399
  }
396
400
 
397
401
  initialHydrationTracker.finalize()
@@ -403,16 +407,7 @@ export function createFrame(root: FrameRoot, init: FrameInit): Frame {
403
407
  reloadController = undefined
404
408
  contentRoot?.dispose()
405
409
  contentRoot = undefined
406
-
407
- // Disconnect any MutationObservers waiting for templates.
408
- for (let observer of observers) {
409
- observer.disconnect()
410
- }
411
- observers.length = 0
412
- for (let unsubscribe of subscriptions) {
413
- unsubscribe()
414
- }
415
- subscriptions.length = 0
410
+ clearPendingFrameTemplateWatch()
416
411
 
417
412
  // Remove hydrated virtual roots in this frame's region.
418
413
  removeVirtualRoots(container.childNodes)
@@ -434,9 +429,173 @@ export function createFrame(root: FrameRoot, init: FrameInit): Frame {
434
429
  render,
435
430
  ready: () => readyPromise,
436
431
  flush: () => context.scheduler.dequeue(),
432
+ clearPendingTemplateWatch: clearPendingFrameTemplateWatch,
433
+ isDisplayingResolvedContent: () => displayedContentStatus === 'resolved',
434
+ startInheritedReload,
435
+ updateMarker,
436
+ renderMarkerContent,
437
437
  dispose,
438
438
  handle: frame,
439
439
  }
440
+
441
+ async function updateMarker(marker: FrameMarkerData, options?: RenderOptions): Promise<void> {
442
+ if (options?.signal?.aborted) return
443
+ let previousMarker = currentMarker
444
+ let isInheritedReload = previousMarker !== undefined && previousMarker.id !== marker.id
445
+ currentMarker = marker
446
+
447
+ if (isInheritedReload) {
448
+ startInheritedReload(options?.signal)
449
+ }
450
+
451
+ if (marker.status === 'pending') {
452
+ await watchPendingFrameTemplate(
453
+ marker,
454
+ options?.initialHydrationTracker,
455
+ options?.signal,
456
+ isInheritedReload
457
+ ? () => {
458
+ completeInheritedReload()
459
+ }
460
+ : undefined,
461
+ )
462
+ } else {
463
+ clearPendingFrameTemplateWatch()
464
+ if (isInheritedReload && !options?.signal?.aborted) {
465
+ completeInheritedReload()
466
+ }
467
+ }
468
+ }
469
+
470
+ async function renderMarkerContent(
471
+ marker: FrameMarkerData,
472
+ content: InternalFrameContent,
473
+ options?: RenderOptions,
474
+ ): Promise<void> {
475
+ if (options?.signal?.aborted) return
476
+ let previousMarker = currentMarker
477
+ let isInheritedReload = previousMarker !== undefined && previousMarker.id !== marker.id
478
+ currentMarker = marker
479
+
480
+ if (isInheritedReload) {
481
+ startInheritedReload(options?.signal)
482
+ }
483
+
484
+ clearPendingFrameTemplateWatch()
485
+ await render(content, { ...options, contentStatus: 'resolved' })
486
+
487
+ if (isInheritedReload && !options?.signal?.aborted) {
488
+ completeInheritedReload()
489
+ }
490
+ }
491
+
492
+ function startInheritedReload(signal?: AbortSignal): void {
493
+ if (signal?.aborted) return
494
+ if (!inheritedReloadPending) {
495
+ inheritedReloadPending = true
496
+ frame.dispatchEvent(new Event('reloadStart'))
497
+ startSubFrameInheritedReloads(getContentNodes(), signal)
498
+ }
499
+
500
+ inheritedReloadAbortUnsubscribe?.()
501
+ inheritedReloadAbortUnsubscribe = undefined
502
+ if (signal) {
503
+ let abort = () => completeInheritedReload()
504
+ signal.addEventListener('abort', abort, { once: true })
505
+ inheritedReloadAbortUnsubscribe = () => {
506
+ signal.removeEventListener('abort', abort)
507
+ }
508
+ }
509
+ }
510
+
511
+ function completeInheritedReload(): void {
512
+ if (!inheritedReloadPending) return
513
+ inheritedReloadPending = false
514
+ inheritedReloadAbortUnsubscribe?.()
515
+ inheritedReloadAbortUnsubscribe = undefined
516
+ frame.dispatchEvent(new Event('reloadComplete'))
517
+ }
518
+
519
+ function startSubFrameInheritedReloads(nodes: Node[], signal?: AbortSignal): void {
520
+ for (let i = 0; i < nodes.length; i++) {
521
+ if (signal?.aborted) break
522
+
523
+ let node = nodes[i]
524
+
525
+ if (isFrameStart(node)) {
526
+ let end = findEndMarker(node, isFrameStart, isFrameEnd)
527
+ context.frameInstances.get(node)?.startInheritedReload(signal)
528
+ i = nodes.indexOf(end)
529
+ continue
530
+ }
531
+
532
+ if (node.childNodes && node.childNodes.length > 0) {
533
+ startSubFrameInheritedReloads(Array.from(node.childNodes), signal)
534
+ }
535
+ }
536
+ }
537
+
538
+ function clearPendingFrameTemplateWatch(): void {
539
+ pendingTemplateUnsubscribe?.()
540
+ pendingTemplateUnsubscribe = undefined
541
+ pendingTemplateObserver?.disconnect()
542
+ pendingTemplateObserver = undefined
543
+ pendingTemplateMarkerId = undefined
544
+ }
545
+
546
+ async function watchPendingFrameTemplate(
547
+ marker: FrameMarkerData,
548
+ initialHydrationTracker?: InitialHydrationTracker,
549
+ signal?: AbortSignal,
550
+ onResolved?: () => void,
551
+ ): Promise<void> {
552
+ if (signal?.aborted) return
553
+ if (pendingTemplateMarkerId === marker.id) return
554
+
555
+ clearPendingFrameTemplateWatch()
556
+ pendingTemplateMarkerId = marker.id
557
+
558
+ let early = consumeFrameTemplate(marker.id) ?? getEarlyFrameContent(marker.id)
559
+ if (early) {
560
+ clearPendingFrameTemplateWatch()
561
+ await render(early, { initialHydrationTracker, signal, contentStatus: 'resolved' })
562
+ if (!signal?.aborted) onResolved?.()
563
+ return
564
+ }
565
+
566
+ if (signal?.aborted) {
567
+ clearPendingFrameTemplateWatch()
568
+ return
569
+ }
570
+
571
+ let observer = setupTemplateObserver()
572
+ pendingTemplateObserver = observer
573
+ let unsubscribe = subscribeFrameTemplate(marker.id, async (fragment) => {
574
+ if (signal?.aborted) return
575
+ if (pendingTemplateMarkerId !== marker.id) return
576
+ clearPendingFrameTemplateWatch()
577
+ await render(fragment, { signal, contentStatus: 'resolved' })
578
+ if (!signal?.aborted) onResolved?.()
579
+ })
580
+ pendingTemplateUnsubscribe = unsubscribe
581
+
582
+ signal?.addEventListener(
583
+ 'abort',
584
+ () => {
585
+ if (pendingTemplateMarkerId === marker.id) {
586
+ clearPendingFrameTemplateWatch()
587
+ }
588
+ },
589
+ { once: true },
590
+ )
591
+
592
+ let buffered = consumeFrameTemplate(marker.id)
593
+ if (buffered) {
594
+ clearPendingFrameTemplateWatch()
595
+ await render(buffered, { initialHydrationTracker, signal, contentStatus: 'resolved' })
596
+ if (!signal?.aborted) onResolved?.()
597
+ }
598
+ }
440
599
  }
441
600
 
442
601
  export function createFrameRuntime(init: {
@@ -752,16 +911,32 @@ function hydrateRegion(
752
911
  root.render(vElement)
753
912
  }
754
913
 
755
- function createSubFrames(nodes: Node[], context: FrameContext) {
914
+ async function createSubFrames(
915
+ nodes: Node[],
916
+ context: FrameContext,
917
+ options?: RenderOptions,
918
+ ): Promise<void> {
919
+ let tasks: Promise<void>[] = []
920
+
756
921
  for (let i = 0; i < nodes.length; i++) {
922
+ if (options?.signal?.aborted) break
923
+
757
924
  let node = nodes[i]
758
925
 
759
926
  if (isFrameStart(node)) {
760
927
  let end = findEndMarker(node, isFrameStart, isFrameEnd)
928
+ let existingFrame = context.frameInstances.get(node)
929
+ let id = getFrameId(node)
930
+ let marker = context.data.f?.[id]
761
931
 
762
- if (!context.frameInstances.has(node)) {
763
- let id = getFrameId(node)
764
- let marker = context.data.f?.[id]
932
+ if (existingFrame) {
933
+ if (marker) {
934
+ let frameMarker: FrameMarkerData = { ...marker, id }
935
+ tasks.push(existingFrame.updateMarker(frameMarker, options))
936
+ } else {
937
+ existingFrame.clearPendingTemplateWatch()
938
+ }
939
+ } else {
765
940
  if (marker) {
766
941
  let frameMarker: FrameMarkerData = { ...marker, id }
767
942
  let subFrame = createFrame([node, end], {
@@ -788,9 +963,11 @@ function createSubFrames(nodes: Node[], context: FrameContext) {
788
963
  }
789
964
 
790
965
  if (node.childNodes && node.childNodes.length > 0) {
791
- createSubFrames(Array.from(node.childNodes), context)
966
+ tasks.push(createSubFrames(Array.from(node.childNodes), context, options))
792
967
  }
793
968
  }
969
+
970
+ await Promise.all(tasks)
794
971
  }
795
972
 
796
973
  function isHydrationMarkerLive(marker: HydrationMarker, context: FrameContext): boolean {
@@ -44,7 +44,7 @@ export type Renderable = RemixElement | string | number | bigint | boolean | nul
44
44
  * Particularly useful for `props.children`.
45
45
  *
46
46
  * ```tsx
47
- * function MyComponent({ children }: { children: RemixNode }) {}
47
+ * function MyComponent(handle: Handle<{ children: RemixNode }>) {}
48
48
  * ```
49
49
  */
50
50
  export type RemixNode = Renderable | RemixNode[]
@@ -77,10 +77,6 @@ type MixinElementType = ((
77
77
  __rmxMixinElementType: string
78
78
  }
79
79
 
80
- type MergeComponentProps<handleProps, renderProps> = [handleProps] extends [Record<string, never>]
81
- ? renderProps
82
- : handleProps & renderProps
83
-
84
80
  /**
85
81
  * Get the props for a specific element type.
86
82
  *
@@ -120,7 +116,7 @@ declare global {
120
116
  // host elements
121
117
  | keyof IntrinsicElements
122
118
  // Factory component
123
- | ((handle: Handle<any, any>) => RenderFn<any>)
119
+ | ((handle: Handle<any, any>) => RenderFn)
124
120
  // Mixin element used internally by mixin render callbacks
125
121
  | MixinElementType
126
122
 
@@ -134,11 +130,11 @@ declare global {
134
130
 
135
131
  type LibraryManagedAttributes<component, props> = component extends MixinElementType
136
132
  ? ExpandMixProp<Parameters<ReturnType<component>>[0]>
137
- : component extends (handle: Handle<infer P, any>) => RenderFn<infer R>
138
- ? // It's a ComponentFactory - infer props from the handle
139
- ExpandMixProp<MergeComponentProps<P, R>>
140
- : component extends () => RenderFn<infer P>
141
- ? ExpandMixProp<P>
133
+ : component extends () => RenderFn
134
+ ? ExpandMixProp<Record<string, never>>
135
+ : component extends (handle: Handle<infer P, any>) => RenderFn
136
+ ? // It's a ComponentFactory - infer props from the handle
137
+ ExpandMixProp<P>
142
138
  : // Otherwise use props as-is (simple function component)
143
139
  ExpandMixProp<props>
144
140
 
@@ -1,4 +1,4 @@
1
- import { createMixin, renderMixinElement } from './mixin.ts'
1
+ import { createMixin, renderMixinElement, type MixinFactory } from './mixin.ts'
2
2
  import { navigate } from '../navigation.ts'
3
3
  import { on } from './on-mixin.ts'
4
4
  import type { ElementProps } from '../jsx.ts'
@@ -21,108 +21,110 @@ const nativeLinkHostTypes = new Set(['a', 'area'])
21
21
  /**
22
22
  * Adds client-side navigation behavior to anchor-like elements.
23
23
  */
24
- export const link = createMixin<
24
+ export const link: MixinFactory<
25
25
  HTMLElement,
26
26
  [href: string, options?: NavigationOptions],
27
27
  LinkCurrentProps
28
- >((handle, hostType) => {
29
- let suppressKeyboardClick = false
28
+ > = createMixin<HTMLElement, [href: string, options?: NavigationOptions], LinkCurrentProps>(
29
+ (handle, hostType) => {
30
+ let suppressKeyboardClick = false
30
31
 
31
- return (href, options, props: LinkCurrentProps) => {
32
- if (nativeLinkHostTypes.has(hostType)) {
33
- return renderMixinElement(handle.element, {
34
- ...(props ?? {}),
35
- href,
36
- ...(options?.target == null ? {} : { 'rmx-target': options.target }),
37
- ...(options?.src == null ? {} : { 'rmx-src': options.src }),
38
- ...(options?.resetScroll === false ? { 'rmx-reset-scroll': 'false' } : {}),
39
- })
40
- }
41
-
42
- let nextProps = { ...props }
43
- if (nextProps.role == null) {
44
- nextProps.role = 'link'
45
- }
46
- if (nextProps.disabled === true && nextProps['aria-disabled'] == null) {
47
- nextProps['aria-disabled'] = 'true'
48
- }
49
- if (hostType === 'button' && nextProps.type == null) {
50
- nextProps.type = 'button'
51
- }
52
- if (
53
- hostType !== 'button' &&
54
- nextProps.tabIndex == null &&
55
- nextProps.tabindex == null &&
56
- nextProps.contentEditable == null &&
57
- nextProps.contenteditable == null
58
- ) {
59
- nextProps.tabIndex = 0
60
- }
32
+ return (href, options, props: LinkCurrentProps) => {
33
+ if (nativeLinkHostTypes.has(hostType)) {
34
+ return renderMixinElement(handle.element, {
35
+ ...(props ?? {}),
36
+ href,
37
+ ...(options?.target == null ? {} : { 'rmx-target': options.target }),
38
+ ...(options?.src == null ? {} : { 'rmx-src': options.src }),
39
+ ...(options?.resetScroll === false ? { 'rmx-reset-scroll': 'false' } : {}),
40
+ })
41
+ }
61
42
 
62
- return renderMixinElement(handle.element, {
63
- ...nextProps,
64
- mix: [
65
- on('click', (event) => {
66
- if (event.detail === 0 && suppressKeyboardClick) {
67
- suppressKeyboardClick = false
68
- event.preventDefault()
69
- return
70
- }
43
+ let nextProps = { ...props }
44
+ if (nextProps.role == null) {
45
+ nextProps.role = 'link'
46
+ }
47
+ if (nextProps.disabled === true && nextProps['aria-disabled'] == null) {
48
+ nextProps['aria-disabled'] = 'true'
49
+ }
50
+ if (hostType === 'button' && nextProps.type == null) {
51
+ nextProps.type = 'button'
52
+ }
53
+ if (
54
+ hostType !== 'button' &&
55
+ nextProps.tabIndex == null &&
56
+ nextProps.tabindex == null &&
57
+ nextProps.contentEditable == null &&
58
+ nextProps.contenteditable == null
59
+ ) {
60
+ nextProps.tabIndex = 0
61
+ }
71
62
 
72
- suppressKeyboardClick = false
73
- if (isDisabledElement(event.currentTarget)) {
74
- event.preventDefault()
75
- return
76
- }
77
- if (event.button !== 0) return
63
+ return renderMixinElement(handle.element, {
64
+ ...nextProps,
65
+ mix: [
66
+ on('click', (event) => {
67
+ if (event.detail === 0 && suppressKeyboardClick) {
68
+ suppressKeyboardClick = false
69
+ event.preventDefault()
70
+ return
71
+ }
78
72
 
79
- event.preventDefault()
80
- if (event.metaKey || event.ctrlKey) {
81
- globalThis.open(href, '_blank')
82
- return
83
- }
73
+ suppressKeyboardClick = false
74
+ if (isDisabledElement(event.currentTarget)) {
75
+ event.preventDefault()
76
+ return
77
+ }
78
+ if (event.button !== 0) return
84
79
 
85
- void navigate(href, options)
86
- }),
87
- on('auxclick', (event) => {
88
- suppressKeyboardClick = false
89
- if (isDisabledElement(event.currentTarget)) {
90
80
  event.preventDefault()
91
- return
92
- }
93
- if (event.button !== 1) return
81
+ if (event.metaKey || event.ctrlKey) {
82
+ globalThis.open(href, '_blank')
83
+ return
84
+ }
94
85
 
95
- event.preventDefault()
96
- globalThis.open(href, '_blank')
97
- }),
98
- on('keydown', (event) => {
99
- if (event.key === 'Enter') {
100
- if (event.repeat) return
86
+ void navigate(href, options)
87
+ }),
88
+ on('auxclick', (event) => {
89
+ suppressKeyboardClick = false
101
90
  if (isDisabledElement(event.currentTarget)) {
102
91
  event.preventDefault()
103
92
  return
104
93
  }
94
+ if (event.button !== 1) return
105
95
 
106
- suppressKeyboardClick = hostType === 'button'
107
96
  event.preventDefault()
108
- void navigate(href, options)
109
- return
110
- }
97
+ globalThis.open(href, '_blank')
98
+ }),
99
+ on('keydown', (event) => {
100
+ if (event.key === 'Enter') {
101
+ if (event.repeat) return
102
+ if (isDisabledElement(event.currentTarget)) {
103
+ event.preventDefault()
104
+ return
105
+ }
111
106
 
112
- if (hostType === 'button' && event.key === ' ') {
113
- suppressKeyboardClick = true
114
- event.preventDefault()
115
- }
116
- }),
117
- on('keyup', (event) => {
118
- if (hostType === 'button' && event.key === ' ') {
119
- event.preventDefault()
120
- }
121
- }),
122
- ],
123
- })
124
- }
125
- })
107
+ suppressKeyboardClick = hostType === 'button'
108
+ event.preventDefault()
109
+ void navigate(href, options)
110
+ return
111
+ }
112
+
113
+ if (hostType === 'button' && event.key === ' ') {
114
+ suppressKeyboardClick = true
115
+ event.preventDefault()
116
+ }
117
+ }),
118
+ on('keyup', (event) => {
119
+ if (hostType === 'button' && event.key === ' ') {
120
+ event.preventDefault()
121
+ }
122
+ }),
123
+ ],
124
+ })
125
+ }
126
+ },
127
+ )
126
128
 
127
129
  function isDisabledElement(node: Element) {
128
130
  return (
@@ -130,6 +130,14 @@ export type MixinDescriptor<
130
130
  readonly __node?: (node: node) => void
131
131
  }
132
132
 
133
+ export type MixinFactory<
134
+ node extends EventTarget = Element,
135
+ args extends unknown[] = [],
136
+ props extends ElementProps = ElementProps,
137
+ > = <boundNode extends node = node>(
138
+ ...args: RebindTuple<args, node, boundNode>
139
+ ) => MixinDescriptor<boundNode, RebindTuple<args, node, boundNode>, props>
140
+
133
141
  type PreviousMixDepth = [0, 0, 1, 2, 3, 4]
134
142
  type FalsyMixValue = false | 0 | 0n | '' | null | undefined
135
143
  type NullableMixValue<descriptor> = descriptor | FalsyMixValue
@@ -244,7 +252,7 @@ export function createMixin<
244
252
  node extends EventTarget = Element,
245
253
  args extends unknown[] = [],
246
254
  props extends ElementProps = ElementProps,
247
- >(type: MixinType<node, args, props>) {
255
+ >(type: MixinType<node, args, props>): MixinFactory<node, args, props> {
248
256
  return <boundNode extends node = node>(
249
257
  ...args: RebindTuple<args, node, boundNode>
250
258
  ): MixinDescriptor<boundNode, RebindTuple<args, node, boundNode>, props> => ({
@@ -1,4 +1,4 @@
1
- import { createMixin } from './mixin.ts'
1
+ import { createMixin, type MixinFactory } from './mixin.ts'
2
2
  import type { ElementProps } from '../jsx.ts'
3
3
 
4
4
  /**
@@ -9,8 +9,8 @@ export type RefCallback<node extends EventTarget> = (node: node, signal: AbortSi
9
9
  /**
10
10
  * Calls a callback when an element is inserted and aborts it when removed.
11
11
  */
12
- export const ref = createMixin<Element, [callback: RefCallback<Element>], ElementProps>(
13
- (handle) => {
12
+ export const ref: MixinFactory<Element, [callback: RefCallback<Element>], ElementProps> =
13
+ createMixin<Element, [callback: RefCallback<Element>], ElementProps>((handle) => {
14
14
  let controller: AbortController | undefined
15
15
 
16
16
  handle.addEventListener('insert', (event) => {
@@ -28,5 +28,4 @@ export const ref = createMixin<Element, [callback: RefCallback<Element>], Elemen
28
28
  callback = nextCallback
29
29
  return handle.element
30
30
  }
31
- },
32
- )
31
+ })