@remix-run/ui 0.1.1 → 0.2.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.
- package/README.md +33 -10
- package/dist/animation/animate-layout-mixin.js +66 -66
- package/dist/animation/animate-layout-mixin.js.map +1 -1
- package/dist/animation/spring.d.ts +6 -35
- package/dist/animation/spring.js +9 -8
- package/dist/animation/spring.js.map +1 -1
- package/dist/components/accordion/accordion.d.ts +7 -7
- package/dist/components/accordion/accordion.js +0 -2
- package/dist/components/accordion/accordion.js.map +1 -1
- package/dist/components/accordion/demos/card.demo.d.ts +6 -0
- package/dist/components/accordion/demos/card.demo.js +58 -0
- package/dist/components/accordion/demos/card.demo.js.map +1 -0
- package/dist/components/accordion/demos/multiple.demo.d.ts +6 -0
- package/dist/components/accordion/demos/multiple.demo.js +19 -0
- package/dist/components/accordion/demos/multiple.demo.js.map +1 -0
- package/dist/components/accordion/demos/overview.demo.d.ts +6 -0
- package/dist/components/accordion/demos/overview.demo.js +19 -0
- package/dist/components/accordion/demos/overview.demo.js.map +1 -0
- package/dist/components/breadcrumbs/breadcrumbs.d.ts +1 -1
- package/dist/components/breadcrumbs/breadcrumbs.js +0 -2
- package/dist/components/breadcrumbs/breadcrumbs.js.map +1 -1
- package/dist/components/breadcrumbs/demos/basic.demo.d.ts +5 -0
- package/dist/components/breadcrumbs/demos/basic.demo.js +14 -0
- package/dist/components/breadcrumbs/demos/basic.demo.js.map +1 -0
- package/dist/components/breadcrumbs/demos/separator.demo.d.ts +5 -0
- package/dist/components/breadcrumbs/demos/separator.demo.js +14 -0
- package/dist/components/breadcrumbs/demos/separator.demo.js.map +1 -0
- package/dist/components/button/button.d.ts +86 -4
- package/dist/components/button/button.js +63 -3
- package/dist/components/button/button.js.map +1 -1
- package/dist/components/button/demos/aliases.demo.d.ts +6 -0
- package/dist/components/button/demos/aliases.demo.js +18 -0
- package/dist/components/button/demos/aliases.demo.js.map +1 -0
- package/dist/components/button/demos/basic.demo.d.ts +6 -0
- package/dist/components/button/demos/basic.demo.js +19 -0
- package/dist/components/button/demos/basic.demo.js.map +1 -0
- package/dist/components/button/demos/states.demo.d.ts +6 -0
- package/dist/components/button/demos/states.demo.js +29 -0
- package/dist/components/button/demos/states.demo.js.map +1 -0
- package/dist/components/combobox/combobox.d.ts +11 -11
- package/dist/components/combobox/combobox.js +0 -2
- package/dist/components/combobox/combobox.js.map +1 -1
- package/dist/components/combobox/demos/overview.demo.d.ts +6 -0
- package/dist/components/combobox/demos/overview.demo.js +153 -0
- package/dist/components/combobox/demos/overview.demo.js.map +1 -0
- package/dist/components/listbox/demos/overview.demo.d.ts +6 -0
- package/dist/components/listbox/demos/overview.demo.js +64 -0
- package/dist/components/listbox/demos/overview.demo.js.map +1 -0
- package/dist/components/listbox/listbox.d.ts +4 -9
- package/dist/components/listbox/listbox.js +21 -33
- package/dist/components/listbox/listbox.js.map +1 -1
- package/dist/components/menu/demos/bubbling.demo.d.ts +5 -0
- package/dist/components/menu/demos/bubbling.demo.js +15 -0
- package/dist/components/menu/demos/bubbling.demo.js.map +1 -0
- package/dist/components/menu/demos/overview.demo.d.ts +6 -0
- package/dist/components/menu/demos/overview.demo.js +32 -0
- package/dist/components/menu/demos/overview.demo.js.map +1 -0
- package/dist/components/menu/menu.d.ts +15 -24
- package/dist/components/menu/menu.js +23 -38
- package/dist/components/menu/menu.js.map +1 -1
- package/dist/components/popover/demos/overview.demo.d.ts +6 -0
- package/dist/components/popover/demos/overview.demo.js +64 -0
- package/dist/components/popover/demos/overview.demo.js.map +1 -0
- package/dist/components/popover/popover.d.ts +5 -5
- package/dist/components/popover/popover.js.map +1 -1
- package/dist/components/select/demos/deconstructed.demo.d.ts +6 -0
- package/dist/components/select/demos/deconstructed.demo.js +53 -0
- package/dist/components/select/demos/deconstructed.demo.js.map +1 -0
- package/dist/components/select/demos/overview.demo.d.ts +5 -0
- package/dist/components/select/demos/overview.demo.js +27 -0
- package/dist/components/select/demos/overview.demo.js.map +1 -0
- package/dist/components/select/select.d.ts +11 -11
- package/dist/components/select/select.js +17 -26
- package/dist/components/select/select.js.map +1 -1
- package/dist/components/tabs/tabs.d.ts +6 -6
- package/dist/components/tabs/tabs.js +0 -2
- package/dist/components/tabs/tabs.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/dist/interactions/keydown/keydown.d.ts +1 -1
- package/dist/interactions/outside-click/outside-click-mixin.d.ts +2 -1
- package/dist/interactions/typeahead/typeahead-mixin.d.ts +3 -1
- package/dist/runtime/component.d.ts +18 -12
- package/dist/runtime/component.js +93 -80
- package/dist/runtime/component.js.map +1 -1
- package/dist/runtime/core/attributes.d.ts +14 -0
- package/dist/runtime/core/attributes.js +103 -0
- package/dist/runtime/core/attributes.js.map +1 -0
- package/dist/runtime/core/children.d.ts +7 -0
- package/dist/runtime/core/children.js +30 -0
- package/dist/runtime/core/children.js.map +1 -0
- package/dist/runtime/core/props.d.ts +9 -0
- package/dist/runtime/core/props.js +170 -0
- package/dist/runtime/core/props.js.map +1 -0
- package/dist/runtime/core/vnode.d.ts +3 -0
- package/dist/runtime/core/vnode.js +39 -0
- package/dist/runtime/core/vnode.js.map +1 -0
- package/dist/runtime/create-element.js +2 -1
- package/dist/runtime/create-element.js.map +1 -1
- package/dist/runtime/diff-dom.js +47 -14
- package/dist/runtime/diff-dom.js.map +1 -1
- package/dist/runtime/diff-props.d.ts +1 -2
- package/dist/runtime/diff-props.js +2 -181
- package/dist/runtime/diff-props.js.map +1 -1
- package/dist/runtime/dom.d.ts +20 -9
- package/dist/runtime/frame.d.ts +23 -2
- package/dist/runtime/frame.js +41 -47
- package/dist/runtime/frame.js.map +1 -1
- package/dist/runtime/jsx.d.ts +1 -1
- package/dist/runtime/jsx.js +2 -28
- package/dist/runtime/jsx.js.map +1 -1
- package/dist/runtime/mixins/link-mixin.d.ts +5 -1
- package/dist/runtime/mixins/link-mixin.js.map +1 -1
- package/dist/runtime/mixins/mixin.d.ts +2 -1
- package/dist/runtime/mixins/mixin.js +9 -4
- package/dist/runtime/mixins/mixin.js.map +1 -1
- package/dist/runtime/mixins/on-mixin.d.ts +15 -4
- package/dist/runtime/mixins/on-mixin.js +9 -2
- package/dist/runtime/mixins/on-mixin.js.map +1 -1
- package/dist/runtime/mixins/ref-mixin.d.ts +2 -1
- package/dist/runtime/mixins/ref-mixin.js.map +1 -1
- package/dist/runtime/navigation.js +9 -1
- package/dist/runtime/navigation.js.map +1 -1
- package/dist/runtime/reconcile.d.ts +1 -1
- package/dist/runtime/reconcile.js +452 -197
- package/dist/runtime/reconcile.js.map +1 -1
- package/dist/runtime/run.d.ts +17 -2
- package/dist/runtime/run.js.map +1 -1
- package/dist/runtime/scheduler.d.ts +10 -10
- package/dist/runtime/scheduler.js +1 -1
- package/dist/runtime/scheduler.js.map +1 -1
- package/dist/runtime/stream-protocol.d.ts +8 -0
- package/dist/runtime/stream-protocol.js +20 -0
- package/dist/runtime/stream-protocol.js.map +1 -0
- package/dist/runtime/svg-attributes.js +1 -0
- package/dist/runtime/svg-attributes.js.map +1 -1
- package/dist/runtime/to-vnode.js +7 -11
- package/dist/runtime/to-vnode.js.map +1 -1
- package/dist/runtime/vnode.d.ts +11 -5
- package/dist/runtime/vnode.js +6 -3
- package/dist/runtime/vnode.js.map +1 -1
- package/dist/server/stream.d.ts +2 -0
- package/dist/server/stream.js +154 -114
- package/dist/server/stream.js.map +1 -1
- package/dist/style/css-mixin.d.ts +2 -1
- package/dist/style/css-mixin.js.map +1 -1
- package/dist/style/index.d.ts +1 -1
- package/dist/style/layers.d.ts +2 -0
- package/dist/style/layers.js +3 -0
- package/dist/style/layers.js.map +1 -0
- package/dist/style/stylesheet.d.ts +13 -11
- package/dist/style/stylesheet.js +69 -8
- package/dist/style/stylesheet.js.map +1 -1
- package/dist/theme/contract.d.ts +24 -222
- package/dist/theme/contract.js +2 -2
- package/dist/theme/contract.js.map +1 -1
- package/dist/theme/glyph-contract.d.ts +1 -3
- package/dist/theme/glyph-contract.js.map +1 -1
- package/dist/theme/presets/rmx-01/index.d.ts +4 -2
- package/dist/theme/presets/rmx-01/index.js.map +1 -1
- package/dist/theme/runtime.js +4 -1
- package/dist/theme/runtime.js.map +1 -1
- package/dist/utils/scroll-lock.d.ts +3 -3
- package/dist/utils/scroll-lock.js.map +1 -1
- package/package.json +3 -3
- package/src/animation/README.md +316 -0
- package/src/animation/animate-layout-mixin.ts +79 -71
- package/src/animation/spring.ts +21 -9
- package/src/components/accordion/README.md +166 -0
- package/src/components/accordion/accordion.tsx +14 -14
- package/src/components/accordion/demos/card.demo.tsx +113 -0
- package/src/components/accordion/demos/multiple.demo.tsx +56 -0
- package/src/components/accordion/demos/overview.demo.tsx +56 -0
- package/src/components/anchor/README.md +153 -0
- package/src/components/anchor/anchor.ts +1 -1
- package/src/components/breadcrumbs/README.md +55 -0
- package/src/components/breadcrumbs/breadcrumbs.tsx +1 -3
- package/src/components/breadcrumbs/demos/basic.demo.tsx +17 -0
- package/src/components/breadcrumbs/demos/separator.demo.tsx +18 -0
- package/src/components/button/README.md +4 -4
- package/src/components/button/button.tsx +118 -20
- package/src/components/button/demos/aliases.demo.tsx +27 -0
- package/src/components/button/demos/basic.demo.tsx +30 -0
- package/src/components/button/demos/states.demo.tsx +52 -0
- package/src/components/combobox/README.md +1 -1
- package/src/components/combobox/combobox.tsx +37 -21
- package/src/components/combobox/demos/overview.demo.tsx +199 -0
- package/src/components/glyph/README.md +72 -0
- package/src/components/glyph/glyph.tsx +2 -2
- package/src/components/listbox/README.md +115 -0
- package/src/components/listbox/demos/overview.demo.tsx +101 -0
- package/src/components/listbox/listbox.ts +106 -110
- package/src/components/menu/README.md +96 -0
- package/src/components/menu/demos/bubbling.demo.tsx +34 -0
- package/src/components/menu/demos/overview.demo.tsx +76 -0
- package/src/components/menu/menu.tsx +61 -55
- package/src/components/popover/README.md +3 -3
- package/src/components/popover/demos/overview.demo.tsx +143 -0
- package/src/components/popover/popover.ts +90 -79
- package/src/components/select/README.md +107 -0
- package/src/components/select/demos/deconstructed.demo.tsx +95 -0
- package/src/components/select/demos/overview.demo.tsx +51 -0
- package/src/components/select/select.tsx +53 -36
- package/src/components/tabs/tabs.tsx +10 -5
- package/src/index.ts +2 -1
- package/src/runtime/component.ts +122 -88
- package/src/runtime/core/attributes.ts +118 -0
- package/src/runtime/core/children.ts +42 -0
- package/src/runtime/core/props.ts +187 -0
- package/src/runtime/core/vnode.ts +48 -0
- package/src/runtime/create-element.ts +2 -1
- package/src/runtime/diff-dom.ts +59 -12
- package/src/runtime/diff-props.ts +2 -187
- package/src/runtime/dom.ts +20 -9
- package/src/runtime/frame.ts +71 -55
- package/src/runtime/jsx.ts +3 -30
- package/src/runtime/mixins/link-mixin.ts +88 -86
- package/src/runtime/mixins/mixin.ts +18 -5
- package/src/runtime/mixins/on-mixin.ts +20 -12
- package/src/runtime/mixins/ref-mixin.ts +4 -5
- package/src/runtime/navigation.ts +10 -1
- package/src/runtime/reconcile.ts +563 -223
- package/src/runtime/run.ts +17 -1
- package/src/runtime/scheduler.ts +20 -20
- package/src/runtime/stream-protocol.ts +27 -0
- package/src/runtime/svg-attributes.ts +1 -0
- package/src/runtime/to-vnode.ts +7 -10
- package/src/runtime/vnode.ts +16 -9
- package/src/server/README.md +90 -0
- package/src/server/stream.ts +176 -124
- package/src/style/css-mixin.ts +6 -1
- package/src/style/index.ts +1 -2
- package/src/style/layers.ts +2 -0
- package/src/style/stylesheet.ts +88 -11
- package/src/test/README.md +107 -0
- package/src/test/utils.ts +3 -1
- package/src/theme/README.md +103 -0
- package/src/theme/contract.ts +37 -5
- package/src/theme/glyph-contract.ts +3 -1
- package/src/theme/presets/rmx-01/index.ts +4 -2
- package/src/theme/runtime.ts +4 -1
- package/src/utils/scroll-lock/README.md +33 -0
- package/src/utils/scroll-lock.ts +7 -3
package/src/runtime/run.ts
CHANGED
|
@@ -11,8 +11,21 @@ import { TypedEventTarget } from './typed-event-target.ts'
|
|
|
11
11
|
/**
|
|
12
12
|
* Options for starting the client runtime with {@link run}.
|
|
13
13
|
*/
|
|
14
|
-
export
|
|
14
|
+
export interface RunInit {
|
|
15
|
+
/**
|
|
16
|
+
* Loads the named browser module export for a hydrated `clientEntry()`.
|
|
17
|
+
*
|
|
18
|
+
* Implementations usually call dynamic `import(moduleUrl)` and return
|
|
19
|
+
* `mod[exportName]`.
|
|
20
|
+
*/
|
|
15
21
|
loadModule: LoadModule
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Resolves browser-loaded `<Frame>` content.
|
|
25
|
+
*
|
|
26
|
+
* Omit this only when the runtime never needs to load or reload frames in the
|
|
27
|
+
* browser.
|
|
28
|
+
*/
|
|
16
29
|
resolveFrame?: ResolveFrame
|
|
17
30
|
}
|
|
18
31
|
|
|
@@ -27,8 +40,11 @@ export type AppRuntimeEventMap = {
|
|
|
27
40
|
* Client runtime returned by {@link run}.
|
|
28
41
|
*/
|
|
29
42
|
export type AppRuntime = TypedEventTarget<AppRuntimeEventMap> & {
|
|
43
|
+
/** Resolves after the current document finishes hydrating. */
|
|
30
44
|
ready(): Promise<void>
|
|
45
|
+
/** Flushes any queued component updates synchronously. */
|
|
31
46
|
flush(): void
|
|
47
|
+
/** Stops runtime listeners and disposes the top-level frame. */
|
|
32
48
|
dispose(): void
|
|
33
49
|
}
|
|
34
50
|
|
package/src/runtime/scheduler.ts
CHANGED
|
@@ -17,7 +17,23 @@ type SchedulerPhaseListener = EventListenerOrEventListenerObject | null
|
|
|
17
17
|
/**
|
|
18
18
|
* Scheduler API used by the reconciler and frame runtime.
|
|
19
19
|
*/
|
|
20
|
-
export
|
|
20
|
+
export interface Scheduler {
|
|
21
|
+
enqueue(vnode: CommittedComponentNode, domParent: ParentNode): void
|
|
22
|
+
enqueueWork(newTasks: EmptyFn[]): void
|
|
23
|
+
enqueueCommitPhase(newTasks: EmptyFn[]): void
|
|
24
|
+
enqueueTasks(newTasks: EmptyFn[]): void
|
|
25
|
+
addEventListener(
|
|
26
|
+
type: SchedulerPhaseType,
|
|
27
|
+
listener: SchedulerPhaseListener,
|
|
28
|
+
options?: AddEventListenerOptions | boolean,
|
|
29
|
+
): void
|
|
30
|
+
removeEventListener(
|
|
31
|
+
type: SchedulerPhaseType,
|
|
32
|
+
listener: SchedulerPhaseListener,
|
|
33
|
+
options?: EventListenerOptions | boolean,
|
|
34
|
+
): void
|
|
35
|
+
dequeue(): void
|
|
36
|
+
}
|
|
21
37
|
|
|
22
38
|
// Protect against infinite cascading updates (e.g. handle.update() during render)
|
|
23
39
|
const MAX_CASCADING_UPDATES = 50
|
|
@@ -38,7 +54,7 @@ export function createScheduler(
|
|
|
38
54
|
doc: Document,
|
|
39
55
|
rootTarget: EventTarget,
|
|
40
56
|
styles: StyleManager = defaultStyleManager,
|
|
41
|
-
) {
|
|
57
|
+
): Scheduler {
|
|
42
58
|
let documentState = createDocumentState(doc)
|
|
43
59
|
let scheduled = new Map<CommittedComponentNode, ParentNode>()
|
|
44
60
|
let workTasks: EmptyFn[] = []
|
|
@@ -53,23 +69,7 @@ export function createScheduler(
|
|
|
53
69
|
beforeUpdate: 0,
|
|
54
70
|
commit: 0,
|
|
55
71
|
}
|
|
56
|
-
let scheduler:
|
|
57
|
-
enqueue(vnode: CommittedComponentNode, domParent: ParentNode): void
|
|
58
|
-
enqueueWork(newTasks: EmptyFn[]): void
|
|
59
|
-
enqueueCommitPhase(newTasks: EmptyFn[]): void
|
|
60
|
-
enqueueTasks(newTasks: EmptyFn[]): void
|
|
61
|
-
addEventListener(
|
|
62
|
-
type: SchedulerPhaseType,
|
|
63
|
-
listener: SchedulerPhaseListener,
|
|
64
|
-
options?: AddEventListenerOptions | boolean,
|
|
65
|
-
): void
|
|
66
|
-
removeEventListener(
|
|
67
|
-
type: SchedulerPhaseType,
|
|
68
|
-
listener: SchedulerPhaseListener,
|
|
69
|
-
options?: EventListenerOptions | boolean,
|
|
70
|
-
): void
|
|
71
|
-
dequeue(): void
|
|
72
|
-
}
|
|
72
|
+
let scheduler: Scheduler
|
|
73
73
|
|
|
74
74
|
function dispatchError(error: unknown) {
|
|
75
75
|
console.error(error)
|
|
@@ -137,7 +137,7 @@ export function createScheduler(
|
|
|
137
137
|
// Needed for fragment self-updates that add children - without this, new children
|
|
138
138
|
// would be appended after siblings. The keyed diff has placement logic, but unkeyed
|
|
139
139
|
// diff relies on anchor for correct positioning.
|
|
140
|
-
let anchor = findNextSiblingDomAnchor(vnode
|
|
140
|
+
let anchor = findNextSiblingDomAnchor(vnode) || undefined
|
|
141
141
|
try {
|
|
142
142
|
let updateStyles = getFrameStyleManager(vnode)
|
|
143
143
|
renderComponent(
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type FlushKind = 'document' | 'fragment'
|
|
2
|
+
|
|
3
|
+
const FLUSH_MARKER_PATTERN = /<!--\s*rmx:flush\s+(document|fragment)\s*-->/g
|
|
4
|
+
|
|
5
|
+
export function appendFlushMarker(html: string, kind: FlushKind): string {
|
|
6
|
+
return `${html}<!-- rmx:flush ${kind} -->`
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function stripFlushMarkers(html: string): string {
|
|
10
|
+
FLUSH_MARKER_PATTERN.lastIndex = 0
|
|
11
|
+
return html.replace(FLUSH_MARKER_PATTERN, '')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function findFlushMarker(
|
|
15
|
+
html: string,
|
|
16
|
+
startIndex: number,
|
|
17
|
+
): { index: number; endIndex: number; kind: FlushKind } | undefined {
|
|
18
|
+
FLUSH_MARKER_PATTERN.lastIndex = startIndex
|
|
19
|
+
let match = FLUSH_MARKER_PATTERN.exec(html)
|
|
20
|
+
if (!match) return undefined
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
index: match.index,
|
|
24
|
+
endIndex: FLUSH_MARKER_PATTERN.lastIndex,
|
|
25
|
+
kind: match[1] as FlushKind,
|
|
26
|
+
}
|
|
27
|
+
}
|
package/src/runtime/to-vnode.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Fragment } from './component.ts'
|
|
2
2
|
import { invariant } from './invariant.ts'
|
|
3
|
+
import { isEmptyChild, isPrimitiveChild, normalizeChildren } from './core/children.ts'
|
|
3
4
|
import type { RemixElement, RemixNode } from './jsx.ts'
|
|
4
|
-
import { isRemixElement, TEXT_NODE, type VNode } from './vnode.ts'
|
|
5
|
+
import { isRemixElement, NON_RENDER_NODE, TEXT_NODE, type VNode } from './vnode.ts'
|
|
5
6
|
|
|
6
7
|
function flatMapChildrenToVNodes(node: RemixElement): VNode[] {
|
|
7
8
|
if (!('children' in node.props)) return []
|
|
@@ -13,21 +14,17 @@ function flatMapChildrenToVNodes(node: RemixElement): VNode[] {
|
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
function flattenChildrenToVNodes(nodes: RemixNode[], out: VNode[]): void {
|
|
16
|
-
for (let child of nodes) {
|
|
17
|
-
|
|
18
|
-
flattenChildrenToVNodes(child, out)
|
|
19
|
-
} else {
|
|
20
|
-
out.push(toVNode(child))
|
|
21
|
-
}
|
|
17
|
+
for (let child of normalizeChildren(nodes)) {
|
|
18
|
+
out.push(toVNode(child))
|
|
22
19
|
}
|
|
23
20
|
}
|
|
24
21
|
|
|
25
22
|
export function toVNode(node: RemixNode): VNode {
|
|
26
|
-
if (node
|
|
27
|
-
return { type:
|
|
23
|
+
if (isEmptyChild(node)) {
|
|
24
|
+
return { type: NON_RENDER_NODE }
|
|
28
25
|
}
|
|
29
26
|
|
|
30
|
-
if (
|
|
27
|
+
if (isPrimitiveChild(node)) {
|
|
31
28
|
return { type: TEXT_NODE, _text: String(node) }
|
|
32
29
|
}
|
|
33
30
|
|
package/src/runtime/vnode.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type { ComponentHandle, Component } from './component.ts'
|
|
2
2
|
import { Fragment, Frame } from './component.ts'
|
|
3
|
-
import
|
|
3
|
+
import { isRemixElement } from './core/vnode.ts'
|
|
4
|
+
import type { ElementProps, RemixNode } from './jsx.ts'
|
|
5
|
+
|
|
6
|
+
export { isRemixElement }
|
|
4
7
|
|
|
5
8
|
export const TEXT_NODE = Symbol('TEXT_NODE')
|
|
9
|
+
export const NON_RENDER_NODE = Symbol('NON_RENDER_NODE')
|
|
6
10
|
export const ROOT_VNODE = Symbol('ROOT_VNODE')
|
|
7
11
|
|
|
8
12
|
export type VNodeType =
|
|
@@ -10,6 +14,7 @@ export type VNodeType =
|
|
|
10
14
|
| string // host element
|
|
11
15
|
| Function // component
|
|
12
16
|
| typeof TEXT_NODE
|
|
17
|
+
| typeof NON_RENDER_NODE
|
|
13
18
|
| typeof Fragment
|
|
14
19
|
| typeof Frame
|
|
15
20
|
|
|
@@ -25,6 +30,8 @@ export type VNode<T extends VNodeType = VNodeType> = {
|
|
|
25
30
|
_dom?: unknown
|
|
26
31
|
_controller?: AbortController
|
|
27
32
|
_mixState?: unknown
|
|
33
|
+
_directEventDescriptors?: unknown
|
|
34
|
+
_directEventState?: unknown
|
|
28
35
|
_controlledState?: unknown
|
|
29
36
|
_svg?: boolean
|
|
30
37
|
// Range roots render between comment boundary markers
|
|
@@ -37,10 +44,6 @@ export type VNode<T extends VNodeType = VNodeType> = {
|
|
|
37
44
|
_frameResolveController?: AbortController
|
|
38
45
|
_frameResolved?: boolean
|
|
39
46
|
|
|
40
|
-
// Internal diffing fields
|
|
41
|
-
_index?: number
|
|
42
|
-
_flags?: number
|
|
43
|
-
|
|
44
47
|
// TEXT_NODE
|
|
45
48
|
_text?: string
|
|
46
49
|
|
|
@@ -65,6 +68,10 @@ export type TextNode = VNode & {
|
|
|
65
68
|
_text: string
|
|
66
69
|
}
|
|
67
70
|
|
|
71
|
+
export type NonRenderNode = VNode & {
|
|
72
|
+
type: typeof NON_RENDER_NODE
|
|
73
|
+
}
|
|
74
|
+
|
|
68
75
|
export type CommittedTextNode = TextNode & {
|
|
69
76
|
_dom: Text
|
|
70
77
|
}
|
|
@@ -101,6 +108,10 @@ export function isTextNode(node: VNode): node is TextNode {
|
|
|
101
108
|
return node.type === TEXT_NODE
|
|
102
109
|
}
|
|
103
110
|
|
|
111
|
+
export function isNonRenderNode(node: VNode): node is NonRenderNode {
|
|
112
|
+
return node.type === NON_RENDER_NODE
|
|
113
|
+
}
|
|
114
|
+
|
|
104
115
|
export function isCommittedTextNode(node: VNode): node is CommittedTextNode {
|
|
105
116
|
return isTextNode(node) && node._dom instanceof Text
|
|
106
117
|
}
|
|
@@ -121,10 +132,6 @@ export function isCommittedComponentNode(node: VNode): node is CommittedComponen
|
|
|
121
132
|
return isComponentNode(node) && node._content !== undefined
|
|
122
133
|
}
|
|
123
134
|
|
|
124
|
-
export function isRemixElement(node: RemixNode): node is RemixElement {
|
|
125
|
-
return typeof node === 'object' && node !== null && '$rmx' in node
|
|
126
|
-
}
|
|
127
|
-
|
|
128
135
|
export function findContextFromAncestry(node: VNode, type: Component): unknown {
|
|
129
136
|
let current: VNode | undefined = node
|
|
130
137
|
while (current) {
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Server
|
|
2
|
+
|
|
3
|
+
Remix UI can render to HTML on the server using two APIs:
|
|
4
|
+
|
|
5
|
+
- `renderToString` - Returns a complete HTML string. Simple, but buffers the entire response.
|
|
6
|
+
- `renderToStream` - Returns a `ReadableStream<Uint8Array>`. Sends the initial HTML immediately and streams frame content as it resolves.
|
|
7
|
+
|
|
8
|
+
Both are exported from `remix/ui/server`.
|
|
9
|
+
|
|
10
|
+
## renderToString
|
|
11
|
+
|
|
12
|
+
Renders a component tree to a complete HTML string. Use this when you need the full output before responding (e.g., generating static pages or embedding HTML in an email).
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { renderToString } from 'remix/ui/server'
|
|
16
|
+
|
|
17
|
+
let html = await renderToString(<App />)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## renderToStream
|
|
21
|
+
|
|
22
|
+
Renders a component tree to a streaming response. The initial HTML is sent immediately. Any `<Frame>` components with a `fallback` prop will render the fallback first, then stream the resolved content as it becomes available.
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { renderToStream } from 'remix/ui/server'
|
|
26
|
+
|
|
27
|
+
let stream = renderToStream(<App />, {
|
|
28
|
+
frameSrc: request.url,
|
|
29
|
+
signal: request.signal,
|
|
30
|
+
resolveFrame(src, _target, context) {
|
|
31
|
+
let frameUrl = new URL(src, context?.currentFrameSrc ?? request.url)
|
|
32
|
+
return fetchHtml(frameUrl)
|
|
33
|
+
},
|
|
34
|
+
onError(error) {
|
|
35
|
+
console.error(error)
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
return new Response(stream, {
|
|
40
|
+
headers: { 'Content-Type': 'text/html; charset=utf-8' },
|
|
41
|
+
})
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Options
|
|
45
|
+
|
|
46
|
+
- **`frameSrc`** - Seeds SSR frame state for the current render. When provided, server-rendered components can read `handle.frame.src` and `handle.frames.top.src` during SSR.
|
|
47
|
+
- **`topFrameSrc`** - Overrides the root frame URL used for `handle.frames.top.src`. This is mainly useful when calling `renderToStream()` from inside `resolveFrame()` for a nested frame render.
|
|
48
|
+
- **`signal`** - Cancels pending server rendering work. Pass `request.signal` so client disconnects can stop unresolved frame work without invoking `onError` for the disconnect itself.
|
|
49
|
+
- **`resolveFrame(src, target, context)`** - Called when a `<Frame>` needs its content. Return a string of HTML, a `ReadableStream<Uint8Array>`, or a promise of either. `context.currentFrameSrc` is the URL for the frame that contains the `<Frame>`, and `context.topFrameSrc` is the outer document URL. Required if your component tree contains `<Frame>` elements.
|
|
50
|
+
- **`onError(error)`** - Called when a rendering error occurs. If not provided, the stream rejects with the error.
|
|
51
|
+
|
|
52
|
+
When you render nested frame responses with `renderToStream()` inside `resolveFrame()`, pass `frameSrc` for the frame being rendered and carry `topFrameSrc` forward from the parent context. That preserves `handle.frames.top.src` across the whole SSR frame tree.
|
|
53
|
+
|
|
54
|
+
### Streaming behavior
|
|
55
|
+
|
|
56
|
+
When the stream encounters a `<Frame>` component:
|
|
57
|
+
|
|
58
|
+
- **Without `fallback`** (blocking): The frame content is awaited before the initial HTML chunk is sent. The resolved content appears inline.
|
|
59
|
+
- **With `fallback`** (non-blocking): The fallback is rendered inline in the initial chunk. Once the frame resolves, a `<template>` element containing the real content is streamed at the end of the response. The client swaps it in automatically.
|
|
60
|
+
|
|
61
|
+
This means the first chunk always contains a complete, renderable page. Slow data sources don't block the initial paint.
|
|
62
|
+
|
|
63
|
+
## Head content
|
|
64
|
+
|
|
65
|
+
To render content into the document head during SSR, use an explicit `<head>` element:
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
function ProductPage() {
|
|
69
|
+
return () => (
|
|
70
|
+
<html>
|
|
71
|
+
<head>
|
|
72
|
+
<title>Product Name</title>
|
|
73
|
+
<meta name="description" content="A great product" />
|
|
74
|
+
</head>
|
|
75
|
+
<body>
|
|
76
|
+
<h1>Product Name</h1>
|
|
77
|
+
</body>
|
|
78
|
+
</html>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## CSS
|
|
84
|
+
|
|
85
|
+
Components using the `css(...)` mixin through `mix` have their styles collected during rendering and emitted as a single `<style>` tag in the `<head>`. No client-side style injection is needed for server-rendered content.
|
|
86
|
+
|
|
87
|
+
## See Also
|
|
88
|
+
|
|
89
|
+
- [Hydration](https://github.com/remix-run/remix/blob/main/packages/ui/docs/hydration.md) - Making server-rendered components interactive on the client
|
|
90
|
+
- [Frames](https://github.com/remix-run/remix/blob/main/packages/ui/docs/frames.md) - Streaming partial server UI with `<Frame>`
|