@remix-run/ui 0.0.0 → 0.1.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 (273) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +195 -2
  3. package/dist/animation/animate-layout-mixin.d.ts +12 -0
  4. package/dist/animation/animate-layout-mixin.js +214 -0
  5. package/dist/animation/animate-layout-mixin.js.map +1 -0
  6. package/dist/animation/animate-mixins.d.ts +29 -0
  7. package/dist/animation/animate-mixins.js +235 -0
  8. package/dist/animation/animate-mixins.js.map +1 -0
  9. package/dist/animation/index.d.ts +6 -0
  10. package/dist/animation/index.js +5 -0
  11. package/dist/animation/index.js.map +1 -0
  12. package/dist/animation/spring.d.ts +75 -0
  13. package/dist/animation/spring.js +183 -0
  14. package/dist/animation/spring.js.map +1 -0
  15. package/dist/animation/tween.d.ts +70 -0
  16. package/dist/animation/tween.js +93 -0
  17. package/dist/animation/tween.js.map +1 -0
  18. package/dist/components/accordion/accordion.d.ts +92 -0
  19. package/dist/components/accordion/accordion.js +337 -0
  20. package/dist/components/accordion/accordion.js.map +1 -0
  21. package/dist/components/anchor/anchor.d.ts +13 -0
  22. package/dist/components/anchor/anchor.js +558 -0
  23. package/dist/components/anchor/anchor.js.map +1 -0
  24. package/dist/components/breadcrumbs/breadcrumbs.d.ts +11 -0
  25. package/dist/components/breadcrumbs/breadcrumbs.js +78 -0
  26. package/dist/components/breadcrumbs/breadcrumbs.js.map +1 -0
  27. package/dist/components/button/button.d.ts +23 -0
  28. package/dist/components/button/button.js +147 -0
  29. package/dist/components/button/button.js.map +1 -0
  30. package/dist/components/combobox/combobox.d.ts +101 -0
  31. package/dist/components/combobox/combobox.js +708 -0
  32. package/dist/components/combobox/combobox.js.map +1 -0
  33. package/dist/components/glyph/glyph.d.ts +14 -0
  34. package/dist/components/glyph/glyph.js +65 -0
  35. package/dist/components/glyph/glyph.js.map +1 -0
  36. package/dist/components/listbox/listbox.d.ts +67 -0
  37. package/dist/components/listbox/listbox.js +340 -0
  38. package/dist/components/listbox/listbox.js.map +1 -0
  39. package/dist/components/menu/hover-aim.d.ts +5 -0
  40. package/dist/components/menu/hover-aim.js +308 -0
  41. package/dist/components/menu/hover-aim.js.map +1 -0
  42. package/dist/components/menu/menu.d.ts +164 -0
  43. package/dist/components/menu/menu.js +1106 -0
  44. package/dist/components/menu/menu.js.map +1 -0
  45. package/dist/components/popover/popover.d.ts +35 -0
  46. package/dist/components/popover/popover.js +138 -0
  47. package/dist/components/popover/popover.js.map +1 -0
  48. package/dist/components/select/select.d.ts +67 -0
  49. package/dist/components/select/select.js +397 -0
  50. package/dist/components/select/select.js.map +1 -0
  51. package/dist/components/separator/separator.d.ts +2 -0
  52. package/dist/components/separator/separator.js +15 -0
  53. package/dist/components/separator/separator.js.map +1 -0
  54. package/dist/components/tabs/tabs.d.ts +78 -0
  55. package/dist/components/tabs/tabs.js +341 -0
  56. package/dist/components/tabs/tabs.js.map +1 -0
  57. package/dist/index.d.ts +28 -0
  58. package/dist/index.js +21 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/interactions/keydown/keydown.d.ts +1 -0
  61. package/dist/interactions/keydown/keydown.js +8 -0
  62. package/dist/interactions/keydown/keydown.js.map +1 -0
  63. package/dist/interactions/outside-click/outside-click-mixin.d.ts +2 -0
  64. package/dist/interactions/outside-click/outside-click-mixin.js +29 -0
  65. package/dist/interactions/outside-click/outside-click-mixin.js.map +1 -0
  66. package/dist/interactions/typeahead/typeahead-mixin.d.ts +8 -0
  67. package/dist/interactions/typeahead/typeahead-mixin.js +59 -0
  68. package/dist/interactions/typeahead/typeahead-mixin.js.map +1 -0
  69. package/dist/jsx-runtime.d.ts +2 -0
  70. package/dist/jsx-runtime.js +3 -0
  71. package/dist/jsx-runtime.js.map +1 -0
  72. package/dist/runtime/client-entries.d.ts +91 -0
  73. package/dist/runtime/client-entries.js +40 -0
  74. package/dist/runtime/client-entries.js.map +1 -0
  75. package/dist/runtime/component.d.ts +223 -0
  76. package/dist/runtime/component.js +152 -0
  77. package/dist/runtime/component.js.map +1 -0
  78. package/dist/runtime/create-element.d.ts +10 -0
  79. package/dist/runtime/create-element.js +29 -0
  80. package/dist/runtime/create-element.js.map +1 -0
  81. package/dist/runtime/diff-dom.d.ts +2 -0
  82. package/dist/runtime/diff-dom.js +364 -0
  83. package/dist/runtime/diff-dom.js.map +1 -0
  84. package/dist/runtime/diff-props.d.ts +9 -0
  85. package/dist/runtime/diff-props.js +195 -0
  86. package/dist/runtime/diff-props.js.map +1 -0
  87. package/dist/runtime/document-state.d.ts +11 -0
  88. package/dist/runtime/document-state.js +106 -0
  89. package/dist/runtime/document-state.js.map +1 -0
  90. package/dist/runtime/dom.d.ts +3189 -0
  91. package/dist/runtime/dom.js +2 -0
  92. package/dist/runtime/dom.js.map +1 -0
  93. package/dist/runtime/error-event.d.ts +20 -0
  94. package/dist/runtime/error-event.js +19 -0
  95. package/dist/runtime/error-event.js.map +1 -0
  96. package/dist/runtime/event-listeners.d.ts +50 -0
  97. package/dist/runtime/event-listeners.js +31 -0
  98. package/dist/runtime/event-listeners.js.map +1 -0
  99. package/dist/runtime/frame.d.ts +119 -0
  100. package/dist/runtime/frame.js +915 -0
  101. package/dist/runtime/frame.js.map +1 -0
  102. package/dist/runtime/invariant.d.ts +23 -0
  103. package/dist/runtime/invariant.js +33 -0
  104. package/dist/runtime/invariant.js.map +1 -0
  105. package/dist/runtime/jsx.d.ts +320 -0
  106. package/dist/runtime/jsx.js +31 -0
  107. package/dist/runtime/jsx.js.map +1 -0
  108. package/dist/runtime/mixins/attrs-mixin.d.ts +9 -0
  109. package/dist/runtime/mixins/attrs-mixin.js +23 -0
  110. package/dist/runtime/mixins/attrs-mixin.js.map +1 -0
  111. package/dist/runtime/mixins/link-mixin.d.ts +17 -0
  112. package/dist/runtime/mixins/link-mixin.js +101 -0
  113. package/dist/runtime/mixins/link-mixin.js.map +1 -0
  114. package/dist/runtime/mixins/mixin.d.ts +145 -0
  115. package/dist/runtime/mixins/mixin.js +570 -0
  116. package/dist/runtime/mixins/mixin.js.map +1 -0
  117. package/dist/runtime/mixins/on-mixin.d.ts +16 -0
  118. package/dist/runtime/mixins/on-mixin.js +49 -0
  119. package/dist/runtime/mixins/on-mixin.js.map +1 -0
  120. package/dist/runtime/mixins/ref-mixin.d.ts +9 -0
  121. package/dist/runtime/mixins/ref-mixin.js +21 -0
  122. package/dist/runtime/mixins/ref-mixin.js.map +1 -0
  123. package/dist/runtime/navigation.d.ts +22 -0
  124. package/dist/runtime/navigation.js +102 -0
  125. package/dist/runtime/navigation.js.map +1 -0
  126. package/dist/runtime/reconcile.d.ts +11 -0
  127. package/dist/runtime/reconcile.js +1240 -0
  128. package/dist/runtime/reconcile.js.map +1 -0
  129. package/dist/runtime/render.d.ts +54 -0
  130. package/dist/runtime/render.js +50 -0
  131. package/dist/runtime/render.js.map +1 -0
  132. package/dist/runtime/run.d.ts +45 -0
  133. package/dist/runtime/run.js +68 -0
  134. package/dist/runtime/run.js.map +1 -0
  135. package/dist/runtime/scheduler.d.ts +30 -0
  136. package/dist/runtime/scheduler.js +185 -0
  137. package/dist/runtime/scheduler.js.map +1 -0
  138. package/dist/runtime/svg-attributes.d.ts +5 -0
  139. package/dist/runtime/svg-attributes.js +124 -0
  140. package/dist/runtime/svg-attributes.js.map +1 -0
  141. package/dist/runtime/to-vnode.d.ts +3 -0
  142. package/dist/runtime/to-vnode.js +43 -0
  143. package/dist/runtime/to-vnode.js.map +1 -0
  144. package/dist/runtime/typed-event-target.d.ts +50 -0
  145. package/dist/runtime/typed-event-target.js +6 -0
  146. package/dist/runtime/typed-event-target.js.map +1 -0
  147. package/dist/runtime/vdom.d.ts +55 -0
  148. package/dist/runtime/vdom.js +198 -0
  149. package/dist/runtime/vdom.js.map +1 -0
  150. package/dist/runtime/vnode.d.ts +79 -0
  151. package/dist/runtime/vnode.js +38 -0
  152. package/dist/runtime/vnode.js.map +1 -0
  153. package/dist/server/stream.d.ts +57 -0
  154. package/dist/server/stream.js +1007 -0
  155. package/dist/server/stream.js.map +1 -0
  156. package/dist/style/css-mixin.d.ts +8 -0
  157. package/dist/style/css-mixin.js +54 -0
  158. package/dist/style/css-mixin.js.map +1 -0
  159. package/dist/style/index.d.ts +5 -0
  160. package/dist/style/index.js +4 -0
  161. package/dist/style/index.js.map +1 -0
  162. package/dist/style/style.d.ts +24 -0
  163. package/dist/style/style.js +269 -0
  164. package/dist/style/style.js.map +1 -0
  165. package/dist/style/stylesheet.d.ts +11 -0
  166. package/dist/style/stylesheet.js +174 -0
  167. package/dist/style/stylesheet.js.map +1 -0
  168. package/dist/test.d.ts +2 -0
  169. package/dist/test.js +2 -0
  170. package/dist/test.js.map +1 -0
  171. package/dist/theme/contract.d.ts +259 -0
  172. package/dist/theme/contract.js +131 -0
  173. package/dist/theme/contract.js.map +1 -0
  174. package/dist/theme/glyph-contract.d.ts +13 -0
  175. package/dist/theme/glyph-contract.js +34 -0
  176. package/dist/theme/glyph-contract.js.map +1 -0
  177. package/dist/theme/presets/rmx-01/glyphs.d.ts +2 -0
  178. package/dist/theme/presets/rmx-01/glyphs.js +252 -0
  179. package/dist/theme/presets/rmx-01/glyphs.js.map +1 -0
  180. package/dist/theme/presets/rmx-01/index.d.ts +2 -0
  181. package/dist/theme/presets/rmx-01/index.js +119 -0
  182. package/dist/theme/presets/rmx-01/index.js.map +1 -0
  183. package/dist/theme/runtime.d.ts +2 -0
  184. package/dist/theme/runtime.js +81 -0
  185. package/dist/theme/runtime.js.map +1 -0
  186. package/dist/theme/theme.d.ts +6 -0
  187. package/dist/theme/theme.js +5 -0
  188. package/dist/theme/theme.js.map +1 -0
  189. package/dist/utils/flash-attribute.d.ts +1 -0
  190. package/dist/utils/flash-attribute.js +11 -0
  191. package/dist/utils/flash-attribute.js.map +1 -0
  192. package/dist/utils/scroll-lock.d.ts +3 -0
  193. package/dist/utils/scroll-lock.js +69 -0
  194. package/dist/utils/scroll-lock.js.map +1 -0
  195. package/dist/utils/wait-for-css-transition.d.ts +1 -0
  196. package/dist/utils/wait-for-css-transition.js +58 -0
  197. package/dist/utils/wait-for-css-transition.js.map +1 -0
  198. package/dist/utils/wait.d.ts +1 -0
  199. package/dist/utils/wait.js +6 -0
  200. package/dist/utils/wait.js.map +1 -0
  201. package/package.json +125 -5
  202. package/src/animation/animate-layout-mixin.ts +266 -0
  203. package/src/animation/animate-mixins.ts +292 -0
  204. package/src/animation/index.ts +6 -0
  205. package/src/animation/spring.ts +299 -0
  206. package/src/animation/tween.ts +134 -0
  207. package/src/components/accordion/accordion.tsx +565 -0
  208. package/src/components/anchor/anchor.ts +904 -0
  209. package/src/components/breadcrumbs/breadcrumbs.tsx +124 -0
  210. package/src/components/button/README.md +44 -0
  211. package/src/components/button/button.tsx +188 -0
  212. package/src/components/combobox/README.md +145 -0
  213. package/src/components/combobox/combobox.tsx +1014 -0
  214. package/src/components/glyph/glyph.tsx +110 -0
  215. package/src/components/listbox/listbox.ts +447 -0
  216. package/src/components/menu/hover-aim.ts +428 -0
  217. package/src/components/menu/menu.tsx +1547 -0
  218. package/src/components/popover/README.md +122 -0
  219. package/src/components/popover/popover.ts +201 -0
  220. package/src/components/select/select.tsx +585 -0
  221. package/src/components/separator/separator.ts +19 -0
  222. package/src/components/tabs/README.md +105 -0
  223. package/src/components/tabs/tabs.tsx +499 -0
  224. package/src/index.ts +72 -0
  225. package/src/interactions/keydown/keydown.ts +14 -0
  226. package/src/interactions/outside-click/outside-click-mixin.ts +55 -0
  227. package/src/interactions/typeahead/typeahead-mixin.ts +89 -0
  228. package/src/jsx-runtime.ts +2 -0
  229. package/src/runtime/client-entries.ts +137 -0
  230. package/src/runtime/component.ts +402 -0
  231. package/src/runtime/create-element.ts +38 -0
  232. package/src/runtime/diff-dom.ts +404 -0
  233. package/src/runtime/diff-props.ts +209 -0
  234. package/src/runtime/document-state.ts +130 -0
  235. package/src/runtime/dom.ts +3971 -0
  236. package/src/runtime/error-event.ts +26 -0
  237. package/src/runtime/event-listeners.ts +171 -0
  238. package/src/runtime/frame.ts +1211 -0
  239. package/src/runtime/invariant.ts +31 -0
  240. package/src/runtime/jsx.ts +398 -0
  241. package/src/runtime/mixins/attrs-mixin.ts +35 -0
  242. package/src/runtime/mixins/link-mixin.ts +131 -0
  243. package/src/runtime/mixins/mixin.ts +908 -0
  244. package/src/runtime/mixins/on-mixin.ts +89 -0
  245. package/src/runtime/mixins/ref-mixin.ts +32 -0
  246. package/src/runtime/navigation.ts +136 -0
  247. package/src/runtime/reconcile.ts +1722 -0
  248. package/src/runtime/render.ts +101 -0
  249. package/src/runtime/run.ts +100 -0
  250. package/src/runtime/scheduler.ts +262 -0
  251. package/src/runtime/svg-attributes.ts +133 -0
  252. package/src/runtime/to-vnode.ts +50 -0
  253. package/src/runtime/typed-event-target.ts +67 -0
  254. package/src/runtime/vdom.ts +295 -0
  255. package/src/runtime/vnode.ts +137 -0
  256. package/src/server/stream.ts +1342 -0
  257. package/src/style/css-mixin.ts +82 -0
  258. package/src/style/index.ts +10 -0
  259. package/src/style/style.ts +312 -0
  260. package/src/style/stylesheet.ts +209 -0
  261. package/src/test/setup.ts +7 -0
  262. package/src/test/utils.ts +45 -0
  263. package/src/test.ts +2 -0
  264. package/src/theme/contract.ts +183 -0
  265. package/src/theme/glyph-contract.ts +57 -0
  266. package/src/theme/presets/rmx-01/glyphs.tsx +308 -0
  267. package/src/theme/presets/rmx-01/index.ts +121 -0
  268. package/src/theme/runtime.ts +116 -0
  269. package/src/theme/theme.ts +15 -0
  270. package/src/utils/flash-attribute.ts +11 -0
  271. package/src/utils/scroll-lock.ts +97 -0
  272. package/src/utils/wait-for-css-transition.ts +66 -0
  273. package/src/utils/wait.ts +5 -0
@@ -0,0 +1,67 @@
1
+ /**
2
+ * An `EventTarget` subclass with typed event maps.
3
+ */
4
+ export class TypedEventTarget<eventMap> extends EventTarget {
5
+ /**
6
+ * Phantom property that carries the event map type on instances.
7
+ */
8
+ declare readonly __eventMap?: eventMap
9
+ }
10
+
11
+ /**
12
+ * Interface surface for {@link TypedEventTarget} with typed listener overloads.
13
+ */
14
+ export interface TypedEventTarget<eventMap> {
15
+ /**
16
+ * Adds a listener for a typed event name from the event map.
17
+ *
18
+ * @param type Event name to listen for.
19
+ * @param listener Listener to invoke when the event fires.
20
+ * @param options Listener registration options.
21
+ */
22
+ addEventListener<type extends Extract<keyof eventMap, string>>(
23
+ type: type,
24
+ listener: TypedEventListener<eventMap>[type],
25
+ options?: AddEventListenerOptions,
26
+ ): void
27
+ /**
28
+ * Adds a listener using the standard untyped `EventTarget` signature.
29
+ *
30
+ * @param type Event name to listen for.
31
+ * @param listener Listener to invoke when the event fires.
32
+ * @param options Listener registration options.
33
+ */
34
+ addEventListener(
35
+ type: string,
36
+ listener: EventListenerOrEventListenerObject | null,
37
+ options?: boolean | AddEventListenerOptions,
38
+ ): void
39
+ /**
40
+ * Removes a listener for a typed event name from the event map.
41
+ *
42
+ * @param type Event name to stop listening for.
43
+ * @param listener Previously registered listener.
44
+ * @param options Listener removal options.
45
+ */
46
+ removeEventListener<type extends Extract<keyof eventMap, string>>(
47
+ type: type,
48
+ listener: TypedEventListener<eventMap>[type],
49
+ options?: EventListenerOptions,
50
+ ): void
51
+ /**
52
+ * Removes a listener using the standard untyped `EventTarget` signature.
53
+ *
54
+ * @param type Event name to stop listening for.
55
+ * @param listener Previously registered listener.
56
+ * @param options Listener removal options.
57
+ */
58
+ removeEventListener(
59
+ type: string,
60
+ listener: EventListenerOrEventListenerObject | null,
61
+ options?: EventListenerOptions,
62
+ ): void
63
+ }
64
+
65
+ type TypedEventListener<eventMap> = {
66
+ [key in keyof eventMap]: (event: eventMap[key]) => void
67
+ }
@@ -0,0 +1,295 @@
1
+ import type { FrameContent, FrameHandle } from './component.ts'
2
+ import { createFrameHandle } from './component.ts'
3
+ import { invariant } from './invariant.ts'
4
+ import type { RemixNode } from './jsx.ts'
5
+ import {
6
+ createComponentErrorEvent,
7
+ getComponentError,
8
+ type ComponentErrorEvent,
9
+ } from './error-event.ts'
10
+ import { createScheduler, type Scheduler } from './scheduler.ts'
11
+ import { diffVNodes, remove as removeVNode } from './reconcile.ts'
12
+ import { toVNode } from './to-vnode.ts'
13
+ import { TypedEventTarget } from './typed-event-target.ts'
14
+ import { ROOT_VNODE, type VNode } from './vnode.ts'
15
+ import { resetStyleState, defaultStyleManager } from './diff-props.ts'
16
+ import type { StyleManager } from '../style/index.ts'
17
+
18
+ /**
19
+ * Events emitted by virtual roots.
20
+ */
21
+ export type VirtualRootEventMap = {
22
+ error: ComponentErrorEvent
23
+ }
24
+
25
+ /**
26
+ * Root controller returned by {@link createRoot} and {@link createRangeRoot}.
27
+ */
28
+ export type VirtualRoot = TypedEventTarget<VirtualRootEventMap> & {
29
+ render: (element: RemixNode) => void
30
+ dispose: () => void
31
+ flush: () => void
32
+ }
33
+
34
+ /**
35
+ * Options for creating a virtual DOM root with {@link createRoot} or {@link createRangeRoot}.
36
+ */
37
+ export type VirtualRootOptions = {
38
+ frame?: FrameHandle
39
+ scheduler?: Scheduler
40
+ styleManager?: StyleManager
41
+ frameInit?: {
42
+ src?: string
43
+ resolveFrame: (
44
+ src: string,
45
+ signal?: AbortSignal,
46
+ target?: string,
47
+ ) => Promise<FrameContent> | FrameContent
48
+ loadModule?: (moduleUrl: string, exportName: string) => Promise<Function> | Function
49
+ }
50
+ }
51
+
52
+ export { createScheduler, type Scheduler }
53
+ export { diffVNodes, toVNode }
54
+ export { resetStyleState }
55
+
56
+ function getHydrationComponentIdFromRangeStart(start: Node): string | undefined {
57
+ if (!(start instanceof Comment)) return undefined
58
+ let marker = start.data.trim()
59
+ if (!marker.startsWith('rmx:h:')) return undefined
60
+ let id = marker.slice('rmx:h:'.length)
61
+ return id.length > 0 ? id : undefined
62
+ }
63
+
64
+ /**
65
+ * Creates a virtual root bounded by two DOM nodes.
66
+ *
67
+ * @param boundaries Start and end marker nodes that define the render region.
68
+ * @param options Root configuration.
69
+ * @returns A virtual root controller.
70
+ */
71
+ export function createRangeRoot(
72
+ boundaries: [Node, Node],
73
+ options: VirtualRootOptions = {},
74
+ ): VirtualRoot {
75
+ let [start, end] = boundaries
76
+ let vroot: VNode | null = null
77
+ let styles = options.styleManager ?? defaultStyleManager
78
+
79
+ let container = end.parentNode
80
+ invariant(container, 'Expected parent node')
81
+ invariant(start.parentNode === container, 'Boundaries must share parent')
82
+ let parent = container
83
+
84
+ let hydrationCursor = start.nextSibling
85
+
86
+ let eventTarget = new TypedEventTarget<VirtualRootEventMap>()
87
+ let scheduler =
88
+ options.scheduler ?? createScheduler(parent.ownerDocument ?? document, eventTarget, styles)
89
+ let frameStub =
90
+ options.frame ??
91
+ createRootFrameHandle({
92
+ src: options.frameInit?.src,
93
+ resolveFrame: options.frameInit?.resolveFrame,
94
+ loadModule: options.frameInit?.loadModule,
95
+ errorTarget: eventTarget,
96
+ scheduler,
97
+ styleManager: styles,
98
+ })
99
+
100
+ let isErrorForwardingAttached = false
101
+ function forwardDomError(event: Event) {
102
+ eventTarget.dispatchEvent(createComponentErrorEvent(getComponentError(event)))
103
+ }
104
+ function attachDomErrorForwarding() {
105
+ if (isErrorForwardingAttached) return
106
+ parent.addEventListener('error', forwardDomError)
107
+ isErrorForwardingAttached = true
108
+ }
109
+ function detachDomErrorForwarding() {
110
+ if (!isErrorForwardingAttached) return
111
+ parent.removeEventListener('error', forwardDomError)
112
+ isErrorForwardingAttached = false
113
+ }
114
+ attachDomErrorForwarding()
115
+
116
+ return Object.assign(eventTarget, {
117
+ render(element: RemixNode) {
118
+ attachDomErrorForwarding()
119
+
120
+ let vnode = toVNode(element)
121
+ let vParent: VNode = {
122
+ type: ROOT_VNODE,
123
+ _svg: false,
124
+ _rangeStart: start,
125
+ _rangeEnd: end,
126
+ _pendingHydrationComponentId: getHydrationComponentIdFromRangeStart(start),
127
+ }
128
+ scheduler.enqueueWork([
129
+ () => {
130
+ diffVNodes(
131
+ vroot,
132
+ vnode,
133
+ parent,
134
+ frameStub,
135
+ scheduler,
136
+ styles,
137
+ vParent,
138
+ eventTarget,
139
+ end,
140
+ hydrationCursor,
141
+ )
142
+ vroot = vnode
143
+ hydrationCursor = null
144
+ },
145
+ ])
146
+ scheduler.dequeue()
147
+ },
148
+
149
+ dispose() {
150
+ detachDomErrorForwarding()
151
+
152
+ if (!vroot) return
153
+ let current = vroot
154
+ vroot = null
155
+ scheduler.enqueueWork([() => removeVNode(current, parent, scheduler, styles)])
156
+ scheduler.dequeue()
157
+ },
158
+
159
+ flush() {
160
+ scheduler.dequeue()
161
+ },
162
+ })
163
+ }
164
+
165
+ /**
166
+ * Creates a virtual root for a host container element.
167
+ *
168
+ * @param container Host element to render into.
169
+ * @param options Root configuration.
170
+ * @returns A virtual root controller.
171
+ */
172
+ export function createRoot(container: HTMLElement, options: VirtualRootOptions = {}): VirtualRoot {
173
+ let vroot: VNode | null = null
174
+ let styles = options.styleManager ?? defaultStyleManager
175
+ let hydrationCursor = container.innerHTML.trim() !== '' ? container.firstChild : undefined
176
+
177
+ let eventTarget = new TypedEventTarget<VirtualRootEventMap>()
178
+ let scheduler =
179
+ options.scheduler ?? createScheduler(container.ownerDocument ?? document, eventTarget, styles)
180
+ let frameStub =
181
+ options.frame ??
182
+ createRootFrameHandle({
183
+ src: options.frameInit?.src,
184
+ resolveFrame: options.frameInit?.resolveFrame,
185
+ loadModule: options.frameInit?.loadModule,
186
+ errorTarget: eventTarget,
187
+ scheduler,
188
+ styleManager: styles,
189
+ })
190
+
191
+ let isErrorForwardingAttached = false
192
+ function forwardDomError(event: Event) {
193
+ eventTarget.dispatchEvent(createComponentErrorEvent(getComponentError(event)))
194
+ }
195
+ function attachDomErrorForwarding() {
196
+ if (isErrorForwardingAttached) return
197
+ container.addEventListener('error', forwardDomError)
198
+ isErrorForwardingAttached = true
199
+ }
200
+ function detachDomErrorForwarding() {
201
+ if (!isErrorForwardingAttached) return
202
+ container.removeEventListener('error', forwardDomError)
203
+ isErrorForwardingAttached = false
204
+ }
205
+ attachDomErrorForwarding()
206
+
207
+ return Object.assign(eventTarget, {
208
+ render(element: RemixNode) {
209
+ attachDomErrorForwarding()
210
+
211
+ let vnode = toVNode(element)
212
+ let vParent: VNode = { type: ROOT_VNODE, _svg: false }
213
+ scheduler.enqueueWork([
214
+ () => {
215
+ diffVNodes(
216
+ vroot,
217
+ vnode,
218
+ container,
219
+ frameStub,
220
+ scheduler,
221
+ styles,
222
+ vParent,
223
+ eventTarget,
224
+ undefined,
225
+ hydrationCursor,
226
+ )
227
+ vroot = vnode
228
+ hydrationCursor = undefined
229
+ },
230
+ ])
231
+ scheduler.dequeue()
232
+ },
233
+
234
+ dispose() {
235
+ detachDomErrorForwarding()
236
+
237
+ if (!vroot) return
238
+ let current = vroot
239
+ vroot = null
240
+ scheduler.enqueueWork([() => removeVNode(current, container, scheduler, styles)])
241
+ scheduler.dequeue()
242
+ },
243
+
244
+ flush() {
245
+ scheduler.dequeue()
246
+ },
247
+ })
248
+ }
249
+
250
+ function createRootFrameHandle(init: {
251
+ src?: string
252
+ resolveFrame?: (
253
+ src: string,
254
+ signal?: AbortSignal,
255
+ target?: string,
256
+ ) => Promise<FrameContent> | FrameContent
257
+ loadModule?: (moduleUrl: string, exportName: string) => Promise<Function> | Function
258
+ errorTarget: EventTarget
259
+ scheduler: Scheduler
260
+ styleManager: StyleManager
261
+ }): FrameHandle {
262
+ let resolveFrame =
263
+ init.resolveFrame ??
264
+ (() => {
265
+ throw new Error(
266
+ 'Cannot render <Frame /> without frame runtime. Use run() or pass frameInit to createRoot/createRangeRoot.',
267
+ )
268
+ })
269
+
270
+ let frame = createFrameHandle({
271
+ src: init.src ?? '/',
272
+ $runtime: {
273
+ canResolveFrames: !!init.resolveFrame,
274
+ topFrame: undefined,
275
+ loadModule:
276
+ init.loadModule ??
277
+ (() => {
278
+ throw new Error('loadModule is required to hydrate client entries inside <Frame />')
279
+ }),
280
+ resolveFrame,
281
+ errorTarget: init.errorTarget,
282
+ pendingClientEntries: new Map(),
283
+ scheduler: init.scheduler,
284
+ styleManager: init.styleManager,
285
+ data: {},
286
+ moduleCache: new Map(),
287
+ moduleLoads: new Map(),
288
+ frameInstances: new WeakMap(),
289
+ namedFrames: new Map(),
290
+ },
291
+ })
292
+ let runtime = frame.$runtime as { topFrame?: FrameHandle } | undefined
293
+ if (runtime) runtime.topFrame = frame
294
+ return frame
295
+ }
@@ -0,0 +1,137 @@
1
+ import type { ComponentHandle, Component } from './component.ts'
2
+ import { Fragment, Frame } from './component.ts'
3
+ import type { ElementProps, RemixElement, RemixNode } from './jsx.ts'
4
+
5
+ export const TEXT_NODE = Symbol('TEXT_NODE')
6
+ export const ROOT_VNODE = Symbol('ROOT_VNODE')
7
+
8
+ export type VNodeType =
9
+ | typeof ROOT_VNODE
10
+ | string // host element
11
+ | Function // component
12
+ | typeof TEXT_NODE
13
+ | typeof Fragment
14
+ | typeof Frame
15
+
16
+ export type VNode<T extends VNodeType = VNodeType> = {
17
+ type: T
18
+ props?: ElementProps
19
+ _mixedProps?: ElementProps
20
+ key?: string
21
+
22
+ // _prefixes assigned during reconciliation
23
+ _parent?: VNode
24
+ _children?: VNode[]
25
+ _dom?: unknown
26
+ _controller?: AbortController
27
+ _mixState?: unknown
28
+ _controlledState?: unknown
29
+ _svg?: boolean
30
+ // Range roots render between comment boundary markers
31
+ _rangeStart?: Node
32
+ _rangeEnd?: Node
33
+ _pendingHydrationComponentId?: string
34
+ _frameInstance?: unknown
35
+ _frameFallbackRoot?: { render: (element: RemixNode) => void; dispose: () => void }
36
+ _frameResolveToken?: number
37
+ _frameResolveController?: AbortController
38
+ _frameResolved?: boolean
39
+
40
+ // Internal diffing fields
41
+ _index?: number
42
+ _flags?: number
43
+
44
+ // TEXT_NODE
45
+ _text?: string
46
+
47
+ // Component
48
+ _handle?: ComponentHandle
49
+ _id?: string
50
+ _content?: VNode
51
+
52
+ // Mixin-persisted node removal state
53
+ _persistedByMixins?: boolean
54
+ _persistedParentByMixins?: ParentNode
55
+ _persistedRemovalToken?: number
56
+ }
57
+
58
+ export type FragmentNode = VNode & {
59
+ type: typeof Fragment
60
+ _children: VNode[]
61
+ }
62
+
63
+ export type TextNode = VNode & {
64
+ type: typeof TEXT_NODE
65
+ _text: string
66
+ }
67
+
68
+ export type CommittedTextNode = TextNode & {
69
+ _dom: Text
70
+ }
71
+
72
+ export type HostNode = VNode & {
73
+ type: string
74
+ props: ElementProps
75
+ _children: VNode[]
76
+ }
77
+
78
+ export type CommittedHostNode = HostNode & {
79
+ _dom: Element
80
+ _controller?: AbortController
81
+ }
82
+
83
+ export type ComponentNode = VNode & {
84
+ type: Function
85
+ props: ElementProps
86
+ _handle: ComponentHandle
87
+ }
88
+
89
+ export type CommittedComponentNode = VNode & {
90
+ type: Function
91
+ props: ElementProps
92
+ _content: VNode
93
+ _handle: ComponentHandle
94
+ }
95
+
96
+ export function isFragmentNode(node: VNode): node is FragmentNode {
97
+ return node.type === Fragment
98
+ }
99
+
100
+ export function isTextNode(node: VNode): node is TextNode {
101
+ return node.type === TEXT_NODE
102
+ }
103
+
104
+ export function isCommittedTextNode(node: VNode): node is CommittedTextNode {
105
+ return isTextNode(node) && node._dom instanceof Text
106
+ }
107
+
108
+ export function isHostNode(node: VNode): node is HostNode {
109
+ return typeof node.type === 'string'
110
+ }
111
+
112
+ export function isCommittedHostNode(node: VNode): node is CommittedHostNode {
113
+ return isHostNode(node) && node._dom instanceof Element
114
+ }
115
+
116
+ export function isComponentNode(node: VNode): node is ComponentNode {
117
+ return typeof node.type === 'function' && node.type !== Frame
118
+ }
119
+
120
+ export function isCommittedComponentNode(node: VNode): node is CommittedComponentNode {
121
+ return isComponentNode(node) && node._content !== undefined
122
+ }
123
+
124
+ export function isRemixElement(node: RemixNode): node is RemixElement {
125
+ return typeof node === 'object' && node !== null && '$rmx' in node
126
+ }
127
+
128
+ export function findContextFromAncestry(node: VNode, type: Component): unknown {
129
+ let current: VNode | undefined = node
130
+ while (current) {
131
+ if (current.type === type && isComponentNode(current)) {
132
+ return current._handle.getContextValue()
133
+ }
134
+ current = current._parent
135
+ }
136
+ return undefined
137
+ }