ripple 0.3.128 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +294 -0
- package/package.json +11 -4
- package/src/jsx-runtime.d.ts +12 -2
- package/src/runtime/element.js +23 -6
- package/src/runtime/index-client.js +81 -41
- package/src/runtime/index-server.js +1 -0
- package/src/runtime/internal/client/blocks.js +173 -91
- package/src/runtime/internal/client/component.js +4 -30
- package/src/runtime/internal/client/composite.js +12 -13
- package/src/runtime/internal/client/constants.js +9 -0
- package/src/runtime/internal/client/context.js +12 -19
- package/src/runtime/internal/client/css.js +7 -9
- package/src/runtime/internal/client/events.js +178 -90
- package/src/runtime/internal/client/expression.js +45 -25
- package/src/runtime/internal/client/for.js +409 -129
- package/src/runtime/internal/client/head.js +10 -5
- package/src/runtime/internal/client/html.js +1 -0
- package/src/runtime/internal/client/hydration.js +152 -10
- package/src/runtime/internal/client/if.js +239 -50
- package/src/runtime/internal/client/index.js +4 -12
- package/src/runtime/internal/client/operations.js +40 -61
- package/src/runtime/internal/client/portal.js +163 -85
- package/src/runtime/internal/client/render.js +44 -43
- package/src/runtime/internal/client/rpc.js +3 -3
- package/src/runtime/internal/client/runtime.js +592 -433
- package/src/runtime/internal/client/selector.js +5 -2
- package/src/runtime/internal/client/template.js +133 -158
- package/src/runtime/internal/client/transport.js +38 -0
- package/src/runtime/internal/client/try.js +465 -426
- package/src/runtime/internal/client/types.d.ts +56 -23
- package/src/runtime/internal/client/utils.js +1 -3
- package/src/runtime/internal/server/dynamic.js +20 -21
- package/src/runtime/internal/server/index.js +329 -277
- package/src/runtime/internal/server/rpc.js +3 -2
- package/src/runtime/internal/server/transport.js +16 -0
- package/src/runtime/transport.js +21 -0
- package/tests/client/__snapshots__/for.test.tsrx.snap +0 -5
- package/tests/client/array/array.copy-within.test.tsrx +12 -12
- package/tests/client/array/array.derived.test.tsrx +46 -46
- package/tests/client/array/array.iteration.test.tsrx +10 -10
- package/tests/client/array/array.mutations.test.tsrx +20 -20
- package/tests/client/array/array.static.test.tsrx +11 -11
- package/tests/client/array/array.to-methods.test.tsrx +6 -6
- package/tests/client/async-suspend.test.tsrx +74 -75
- package/tests/client/basic/basic.attributes.test.tsrx +123 -83
- package/tests/client/basic/basic.collections.test.tsrx +12 -12
- package/tests/client/basic/basic.components.test.tsrx +99 -25
- package/tests/client/basic/basic.errors.test.tsrx +18 -18
- package/tests/client/basic/basic.events.test.tsrx +42 -42
- package/tests/client/basic/basic.get-set.test.tsrx +8 -8
- package/tests/client/basic/basic.reactivity.test.tsrx +73 -38
- package/tests/client/basic/basic.rendering.test.tsrx +25 -25
- package/tests/client/basic/basic.utilities.test.tsrx +3 -3
- package/tests/client/boundaries.test.tsrx +8 -8
- package/tests/client/boxed-locals.test.tsrx +452 -0
- package/tests/client/compiler/__snapshots__/compiler.assignments.test.tsrx.snap +6 -6
- package/tests/client/compiler/compiler.assignments.test.tsrx +9 -73
- package/tests/client/compiler/compiler.attributes.test.tsrx +23 -23
- package/tests/client/compiler/compiler.basic.test.tsrx +15 -60
- package/tests/client/compiler/compiler.tracked-access.test.tsrx +18 -43
- package/tests/client/compiler/compiler.try-in-function.test.tsrx +2 -2
- package/tests/client/composite/__snapshots__/composite.render.test.tsrx.snap +0 -1
- package/tests/client/composite/composite.dynamic-components.test.tsrx +77 -6
- package/tests/client/composite/composite.props.test.tsrx +149 -12
- package/tests/client/composite/composite.reactivity.test.tsrx +51 -52
- package/tests/client/composite/composite.render.test.tsrx +26 -9
- package/tests/client/css/scoped-styles.test.tsrx +8 -8
- package/tests/client/date.test.tsrx +42 -42
- package/tests/client/derived-release.test.tsrx +370 -0
- package/tests/client/dynamic-elements.test.tsrx +45 -45
- package/tests/client/events.test.tsrx +97 -71
- package/tests/client/for-selector.test.tsrx +14 -14
- package/tests/client/for-single-root-items.test.tsrx +118 -3
- package/tests/client/for.test.tsrx +332 -31
- package/tests/client/head.test.tsrx +19 -19
- package/tests/client/html.test.tsrx +11 -9
- package/tests/client/if-block.test.tsrx +80 -0
- package/tests/client/map.test.tsrx +16 -16
- package/tests/client/media-query.test.tsrx +7 -7
- package/tests/client/portal.test.tsrx +224 -26
- package/tests/client/primitive-text.test.tsrx +50 -0
- package/tests/client/prop-kind-switch.test.tsrx +141 -0
- package/tests/client/ref.test.tsrx +17 -17
- package/tests/client/return.test.tsrx +26 -3
- package/tests/client/root-boundary.test.tsrx +42 -0
- package/tests/client/scoped-flush.test.tsrx +8 -8
- package/tests/client/set.test.tsrx +6 -6
- package/tests/client/svg.test.tsrx +11 -11
- package/tests/client/switch.test.tsrx +44 -44
- package/tests/client/track-async-hydration.test.tsrx +4 -4
- package/tests/client/try.test.tsrx +151 -83
- package/tests/client/tsx.test.tsrx +43 -40
- package/tests/client/url/url.derived.test.tsrx +6 -6
- package/tests/client/url-search-params/url-search-params.derived.test.tsrx +8 -8
- package/tests/client/url-search-params/url-search-params.iteration.test.tsrx +10 -10
- package/tests/client/url-search-params/url-search-params.mutation.test.tsrx +10 -10
- package/tests/client/url-search-params/url-search-params.retrieval.test.tsrx +18 -18
- package/tests/client/url-search-params/url-search-params.serialization.test.tsrx +2 -2
- package/tests/hydration/basic.test.js +74 -1
- package/tests/hydration/build-components.js +22 -1
- package/tests/hydration/compiled/client/basic.js +1142 -713
- package/tests/hydration/compiled/client/composite.js +151 -100
- package/tests/hydration/compiled/client/events.js +184 -171
- package/tests/hydration/compiled/client/for.js +1472 -1132
- package/tests/hydration/compiled/client/fragment-cursor.js +2341 -0
- package/tests/hydration/compiled/client/head.js +254 -201
- package/tests/hydration/compiled/client/hmr.js +54 -41
- package/tests/hydration/compiled/client/html-in-template.js +54 -39
- package/tests/hydration/compiled/client/html.js +1633 -1263
- package/tests/hydration/compiled/client/if-children.js +420 -270
- package/tests/hydration/compiled/client/if-fragment-controlflow.js +363 -317
- package/tests/hydration/compiled/client/if.js +229 -219
- package/tests/hydration/compiled/client/mixed-control-flow.js +420 -404
- package/tests/hydration/compiled/client/nested-control-flow.js +1326 -1277
- package/tests/hydration/compiled/client/portal.js +87 -95
- package/tests/hydration/compiled/client/reactivity.js +135 -101
- package/tests/hydration/compiled/client/return.js +28 -19
- package/tests/hydration/compiled/client/streaming.js +390 -306
- package/tests/hydration/compiled/client/switch.js +258 -266
- package/tests/hydration/compiled/client/track-async-serialization.js +448 -280
- package/tests/hydration/compiled/client/try.js +173 -129
- package/tests/hydration/compiled/client/typed-text.js +71 -0
- package/tests/hydration/compiled/fixtures/money.js +10 -0
- package/tests/hydration/compiled/server/basic.js +123 -6
- package/tests/hydration/compiled/server/composite.js +16 -30
- package/tests/hydration/compiled/server/events.js +66 -18
- package/tests/hydration/compiled/server/for.js +117 -41
- package/tests/hydration/compiled/server/fragment-cursor.js +1687 -0
- package/tests/hydration/compiled/server/head.js +20 -20
- package/tests/hydration/compiled/server/hmr.js +2 -2
- package/tests/hydration/compiled/server/html.js +70 -238
- package/tests/hydration/compiled/server/if-children.js +107 -15
- package/tests/hydration/compiled/server/if.js +11 -15
- package/tests/hydration/compiled/server/mixed-control-flow.js +8 -8
- package/tests/hydration/compiled/server/portal.js +14 -22
- package/tests/hydration/compiled/server/reactivity.js +14 -14
- package/tests/hydration/compiled/server/streaming.js +19 -19
- package/tests/hydration/compiled/server/switch.js +8 -8
- package/tests/hydration/compiled/server/track-async-serialization.js +150 -23
- package/tests/hydration/compiled/server/try.js +8 -8
- package/tests/hydration/compiled/server/typed-text.js +17 -0
- package/tests/hydration/components/basic.tsrx +44 -8
- package/tests/hydration/components/composite.tsrx +2 -2
- package/tests/hydration/components/events.tsrx +25 -25
- package/tests/hydration/components/for.tsrx +115 -67
- package/tests/hydration/components/fragment-cursor.tsrx +650 -0
- package/tests/hydration/components/head.tsrx +16 -16
- package/tests/hydration/components/hmr.tsrx +2 -2
- package/tests/hydration/components/html.tsrx +7 -7
- package/tests/hydration/components/if-children.tsrx +72 -25
- package/tests/hydration/components/if.tsrx +18 -18
- package/tests/hydration/components/mixed-control-flow.tsrx +11 -11
- package/tests/hydration/components/portal.tsrx +3 -3
- package/tests/hydration/components/reactivity.tsrx +16 -16
- package/tests/hydration/components/streaming.tsrx +20 -20
- package/tests/hydration/components/switch.tsrx +20 -20
- package/tests/hydration/components/text-types.ts +4 -0
- package/tests/hydration/components/track-async-serialization.tsrx +80 -26
- package/tests/hydration/components/try.tsrx +8 -8
- package/tests/hydration/components/typed-text.tsrx +22 -0
- package/tests/hydration/fixtures/money.js +10 -0
- package/tests/hydration/for.test.js +36 -0
- package/tests/hydration/fragment-cursor.test.js +111 -0
- package/tests/hydration/if-children.test.js +24 -0
- package/tests/hydration/streaming.test.js +19 -0
- package/tests/hydration/track-async-serialization.test.js +87 -2
- package/tests/hydration/tsconfig.text-types.json +10 -0
- package/tests/hydration/typed-text.test.js +34 -0
- package/tests/server/await.test.tsrx +3 -3
- package/tests/server/basic.attributes.test.tsrx +70 -42
- package/tests/server/basic.components.test.tsrx +64 -8
- package/tests/server/basic.test.tsrx +34 -32
- package/tests/server/compiler.test.tsrx +1 -70
- package/tests/server/composite.props.test.tsrx +6 -6
- package/tests/server/dynamic-elements.test.tsrx +31 -8
- package/tests/server/for.test.tsrx +94 -0
- package/tests/server/head.test.tsrx +11 -11
- package/tests/server/scoped-styles.test.tsrx +2 -2
- package/tests/server/streaming-ssr.test.tsrx +48 -43
- package/tests/server/track-async-serialization.test.tsrx +204 -21
- package/tests/server/try.test.tsrx +48 -48
- package/tests/types/transport.ts +40 -0
- package/tests/utils/jsx-runtime-types.test.js +67 -2
- package/tests/utils/tracked-types.test.js +117 -0
- package/tests/utils/vite-plugin-config.test.js +23 -0
- package/types/index.d.ts +49 -9
- package/types/server.d.ts +3 -0
- package/types/transport.d.ts +15 -0
- package/src/runtime/internal/client/switch.js +0 -113
- package/src/utils/errors.js +0 -13
- package/tests/client/compiler/__snapshots__/compiler.assignments.test.rsrx.snap +0 -12
- package/tests/client/compiler/__snapshots__/compiler.typescript.test.rsrx.snap +0 -46
- package/tests/client/lazy-array.test.tsrx +0 -31
- package/tests/client/lazy-destructuring.test.tsrx +0 -438
- package/tests/client/tracked-index-access.test.tsrx +0 -113
- package/tests/server/lazy-destructuring.test.tsrx +0 -595
- package/tests/server/tracked-index-access.test.tsrx +0 -76
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
/** @import { AppendIntoAnchor } from '#client' */
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
hydrate_first_child,
|
|
5
|
+
hydrate_next_sibling,
|
|
6
|
+
hydrate_node,
|
|
7
|
+
hydrate_text_child,
|
|
8
|
+
hydrating,
|
|
9
|
+
set_hydrate_node,
|
|
10
|
+
} from './hydration.js';
|
|
5
11
|
import { get_descriptor } from '@tsrx/core/runtime/language-helpers';
|
|
6
12
|
|
|
13
|
+
export { hydrate_first_child, hydrate_next_sibling, hydrate_text_child };
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The `firstChild` / `nextSibling` getters, called directly so a traversal
|
|
17
|
+
* site never becomes a megamorphic property read across node types.
|
|
18
|
+
* @type {(() => Node | null)}
|
|
19
|
+
*/
|
|
20
|
+
export var first_child_getter;
|
|
7
21
|
/** @type {(() => Node | null)} */
|
|
8
|
-
var
|
|
9
|
-
/** @type {(() => Node | null)} */
|
|
10
|
-
var next_sibling_getter;
|
|
22
|
+
export var next_sibling_getter;
|
|
11
23
|
/** @type {(() => Node | null)} */
|
|
12
24
|
var last_child_getter;
|
|
13
25
|
|
|
@@ -37,8 +49,6 @@ export function init_operations() {
|
|
|
37
49
|
|
|
38
50
|
// the following assignments improve perf of lookups on DOM nodes
|
|
39
51
|
element_prototype.__click = undefined;
|
|
40
|
-
element_prototype.__className = undefined;
|
|
41
|
-
Text.prototype.__t = undefined;
|
|
42
52
|
event_target_prototype.__root = undefined;
|
|
43
53
|
}
|
|
44
54
|
|
|
@@ -73,28 +83,6 @@ export function first_child(node, is_text) {
|
|
|
73
83
|
return hydrate_first_child(is_text);
|
|
74
84
|
}
|
|
75
85
|
|
|
76
|
-
/**
|
|
77
|
-
* @param {boolean} [is_text]
|
|
78
|
-
* @returns {Node | null}
|
|
79
|
-
*/
|
|
80
|
-
export function hydrate_first_child(is_text) {
|
|
81
|
-
var child = get_first_child(/** @type {Node} */ (hydrate_node));
|
|
82
|
-
|
|
83
|
-
// Handles the case where we have `<p>{text}</p>`, where `text` is empty
|
|
84
|
-
if (child === null) {
|
|
85
|
-
child = /** @type {Node} */ (hydrate_node).appendChild(create_text());
|
|
86
|
-
} else if (is_text && child.nodeType !== TEXT_NODE) {
|
|
87
|
-
var text = create_text();
|
|
88
|
-
/** @type {Element | Text | Comment} */ (child)?.before(text);
|
|
89
|
-
set_hydrate_node(text);
|
|
90
|
-
return text;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
set_hydrate_node(child);
|
|
94
|
-
|
|
95
|
-
return child;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
86
|
/**
|
|
99
87
|
* Anchor sentinel for all-component children: components render directly into
|
|
100
88
|
* `parent` instead of each inserting before a placeholder comment. `append()`
|
|
@@ -115,7 +103,29 @@ export function append_into(parent) {
|
|
|
115
103
|
set_hydrate_node(child);
|
|
116
104
|
}
|
|
117
105
|
|
|
118
|
-
return { parent };
|
|
106
|
+
return { parent, into: true };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Resolves the anchor for a block that keeps inserting relative to it for its
|
|
111
|
+
* whole life (root-controlled control flow). An append-into sentinel is only
|
|
112
|
+
* valid for a one-shot append: a later branch swap or list move must land at
|
|
113
|
+
* the block's own position, not after whatever siblings were appended since,
|
|
114
|
+
* so the sentinel is materialized into a text anchor at the current end of
|
|
115
|
+
* the parent. During hydration the cursor sits on the block's SSR boundary
|
|
116
|
+
* marker, which is exactly the anchor a non-sentinel root-controlled block
|
|
117
|
+
* receives, so it serves as the anchor there.
|
|
118
|
+
* @param {Node | AppendIntoAnchor} node
|
|
119
|
+
* @returns {Node}
|
|
120
|
+
*/
|
|
121
|
+
export function resolve_anchor(node) {
|
|
122
|
+
if (/** @type {AppendIntoAnchor} */ (node).into !== true) {
|
|
123
|
+
return /** @type {Node} */ (node);
|
|
124
|
+
}
|
|
125
|
+
if (hydrating) {
|
|
126
|
+
return /** @type {Node} */ (hydrate_node);
|
|
127
|
+
}
|
|
128
|
+
return /** @type {AppendIntoAnchor} */ (node).parent.appendChild(create_text());
|
|
119
129
|
}
|
|
120
130
|
|
|
121
131
|
/**
|
|
@@ -160,37 +170,6 @@ export function next_sibling(node, is_text) {
|
|
|
160
170
|
return hydrate_next_sibling(is_text);
|
|
161
171
|
}
|
|
162
172
|
|
|
163
|
-
/**
|
|
164
|
-
* @param {boolean} [is_text]
|
|
165
|
-
* @returns {Node | null}
|
|
166
|
-
*/
|
|
167
|
-
export function hydrate_next_sibling(is_text) {
|
|
168
|
-
var next_sibling = /** @type {ChildNode | null} */ (
|
|
169
|
-
get_next_sibling(/** @type {ChildNode} */ (hydrate_node))
|
|
170
|
-
);
|
|
171
|
-
var last_sibling = next_sibling;
|
|
172
|
-
|
|
173
|
-
// if a sibling {expression} is empty during SSR, there might be no
|
|
174
|
-
// text node to hydrate — we must therefore create one
|
|
175
|
-
if (is_text && next_sibling?.nodeType !== TEXT_NODE) {
|
|
176
|
-
var text = create_text();
|
|
177
|
-
// If the next sibling is `null` and we're handling text then it's because
|
|
178
|
-
// the SSR content was empty for the text, so we need to generate a new text
|
|
179
|
-
// node and insert it after the last sibling
|
|
180
|
-
if (next_sibling === null) {
|
|
181
|
-
/** @type {ChildNode} */ (last_sibling).after(text);
|
|
182
|
-
} else {
|
|
183
|
-
/** @type {ChildNode} */ (next_sibling).before(text);
|
|
184
|
-
}
|
|
185
|
-
set_hydrate_node(text);
|
|
186
|
-
return text;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
set_hydrate_node(next_sibling);
|
|
190
|
-
|
|
191
|
-
return next_sibling;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
173
|
export function create_text(value = '') {
|
|
195
174
|
return document.createTextNode(value);
|
|
196
175
|
}
|
|
@@ -1,97 +1,175 @@
|
|
|
1
|
-
/** @import { Block } from '#client' */
|
|
1
|
+
/** @import { AppendIntoAnchor, Block } from '#client' */
|
|
2
2
|
|
|
3
|
-
import { branch, destroy_block,
|
|
4
|
-
import { UNINITIALIZED } from './constants.js';
|
|
5
|
-
import { handle_root_events } from './events.js';
|
|
6
|
-
import { create_text } from './operations.js';
|
|
3
|
+
import { branch, destroy_block, render } from './blocks.js';
|
|
4
|
+
import { DESTROYED, DETACHED_BLOCK, UNINITIALIZED } from './constants.js';
|
|
5
|
+
import { handle_root_events, release_root_events } from './events.js';
|
|
7
6
|
import { active_block } from './runtime.js';
|
|
8
|
-
import { hydrating, hydrate_node,
|
|
7
|
+
import { hydrating, hydrate_node, set_hydration, set_hydrating } from './hydration.js';
|
|
9
8
|
import { is_tsrx_element, tsrx_element } from '../../element.js';
|
|
10
9
|
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {(anchor: AppendIntoAnchor, block: Block) => void} PortalRender
|
|
12
|
+
* @typedef {{
|
|
13
|
+
* g: () => Element;
|
|
14
|
+
* r: PortalRender | null;
|
|
15
|
+
* c: () => any;
|
|
16
|
+
* t: Element | typeof UNINITIALIZED;
|
|
17
|
+
* k: any;
|
|
18
|
+
* b: Block | null;
|
|
19
|
+
* e: import('./events.js').RootTargetRef | null;
|
|
20
|
+
* p: Block | null;
|
|
21
|
+
* }} PortalState
|
|
22
|
+
* @typedef {{
|
|
23
|
+
* start: Node | null;
|
|
24
|
+
* end: Node | null;
|
|
25
|
+
* r: PortalRender | null;
|
|
26
|
+
* c: any;
|
|
27
|
+
* a: AppendIntoAnchor;
|
|
28
|
+
* b: Block;
|
|
29
|
+
* }} PortalBranchState
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @param {PortalState} s
|
|
34
|
+
*/
|
|
35
|
+
function run_portal(s) {
|
|
36
|
+
// The render block itself; lets the shared teardown tell destroy from re-run.
|
|
37
|
+
s.p = /** @type {Block} */ (active_block);
|
|
38
|
+
|
|
39
|
+
var next_target = s.g();
|
|
40
|
+
var next_children = s.c();
|
|
41
|
+
|
|
42
|
+
if (s.t === next_target && s.k === next_children) return;
|
|
43
|
+
|
|
44
|
+
if (s.t !== next_target) {
|
|
45
|
+
// The target's delegated root listeners are only released and
|
|
46
|
+
// re-acquired when the target changes — a children-only update must not
|
|
47
|
+
// touch them.
|
|
48
|
+
if (s.e !== null) {
|
|
49
|
+
release_root_events(s.e);
|
|
50
|
+
}
|
|
51
|
+
s.e = handle_root_events(next_target);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
s.t = next_target;
|
|
55
|
+
s.k = next_children;
|
|
56
|
+
|
|
57
|
+
if (s.b !== null) {
|
|
58
|
+
destroy_block(s.b);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Portal content always appends at the end of its target, so an
|
|
62
|
+
// append-into sentinel replaces a placeholder text anchor: no extra node to
|
|
63
|
+
// create, insert before, or remove on teardown. The branch is detached: its
|
|
64
|
+
// DOM is removed even when a destroyed ancestor already removed its own.
|
|
65
|
+
s.b = branch(run_portal_children, DETACHED_BLOCK, {
|
|
66
|
+
start: null,
|
|
67
|
+
end: null,
|
|
68
|
+
r: s.r,
|
|
69
|
+
c: next_children,
|
|
70
|
+
a: { parent: next_target, into: true },
|
|
71
|
+
b: /** @type {Block} */ (s.p),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return portal_teardown;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @param {PortalBranchState} state
|
|
79
|
+
*/
|
|
80
|
+
function run_portal_children(state) {
|
|
81
|
+
var render_children = state.r;
|
|
82
|
+
if (render_children !== null) {
|
|
83
|
+
render_children(state.a, state.b);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
var children = state.c;
|
|
87
|
+
if (is_tsrx_element(children)) {
|
|
88
|
+
children.render(state.a, state.b, children.p);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Only the destroy path releases the target's listeners; a re-run diffs the
|
|
94
|
+
* target itself in `run_portal`.
|
|
95
|
+
* @this {null}
|
|
96
|
+
* @param {PortalState} s
|
|
97
|
+
*/
|
|
98
|
+
function portal_teardown(s) {
|
|
99
|
+
var block = /** @type {Block} */ (s.p);
|
|
100
|
+
if ((block.f & DESTROYED) !== 0 && s.e !== null) {
|
|
101
|
+
release_root_events(s.e);
|
|
102
|
+
s.e = null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @param {() => Element} get_target
|
|
108
|
+
* @param {PortalRender | null} render_children
|
|
109
|
+
* @param {() => any} get_children
|
|
110
|
+
*/
|
|
111
|
+
function create_portal(get_target, render_children, get_children) {
|
|
112
|
+
/** @type {PortalState} */
|
|
113
|
+
var state = {
|
|
114
|
+
g: get_target,
|
|
115
|
+
r: render_children,
|
|
116
|
+
c: get_children,
|
|
117
|
+
// current target
|
|
118
|
+
t: UNINITIALIZED,
|
|
119
|
+
// current children value (component form only)
|
|
120
|
+
k: UNINITIALIZED,
|
|
121
|
+
// branch rendering the content into the target
|
|
122
|
+
b: null,
|
|
123
|
+
// delegated root listener ref for the current target
|
|
124
|
+
e: null,
|
|
125
|
+
// the render block, assigned on its first run
|
|
126
|
+
p: null,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
if (!hydrating) {
|
|
130
|
+
render(run_portal, state);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Portals are client-only and don't participate in hydration; the
|
|
135
|
+
// compiler-generated code already advanced past the SSR marker. Disable
|
|
136
|
+
// hydration for the portal content and restore the cursor afterwards.
|
|
137
|
+
var previous_hydrate_node = hydrate_node;
|
|
138
|
+
set_hydrating(false);
|
|
139
|
+
try {
|
|
140
|
+
render(run_portal, state);
|
|
141
|
+
} finally {
|
|
142
|
+
set_hydration(true, previous_hydrate_node);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function no_children() {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Compiler fast path for `<Portal target={...}>...</Portal>`: no props object,
|
|
152
|
+
* component context, or element wrapper — the children render function is
|
|
153
|
+
* fixed for the life of the portal.
|
|
154
|
+
* @param {Node} _anchor
|
|
155
|
+
* @param {() => Element} get_target
|
|
156
|
+
* @param {PortalRender} render_children
|
|
157
|
+
* @returns {void}
|
|
158
|
+
*/
|
|
159
|
+
export function portal(_anchor, get_target, render_children) {
|
|
160
|
+
create_portal(get_target, render_children, no_children);
|
|
161
|
+
}
|
|
162
|
+
|
|
11
163
|
/**
|
|
12
164
|
* @param {{ target: Element, children: import('../../element.js').TSRXElement }} props
|
|
13
165
|
* @returns {import('../../element.js').TSRXElement}
|
|
14
166
|
*/
|
|
15
167
|
export function Portal(props) {
|
|
16
168
|
return tsrx_element(function render_portal() {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
/** @type {Element | symbol} */
|
|
23
|
-
let target = UNINITIALIZED;
|
|
24
|
-
/** @type {import('../../element.js').TSRXElement | symbol} */
|
|
25
|
-
let children = UNINITIALIZED;
|
|
26
|
-
/** @type {Block | null} */
|
|
27
|
-
var b = null;
|
|
28
|
-
/** @type {Text | null} */
|
|
29
|
-
var anchor = null;
|
|
30
|
-
/** @type {Node | null} */
|
|
31
|
-
var dom_start = null;
|
|
32
|
-
/** @type {Node | null} */
|
|
33
|
-
var dom_end = null;
|
|
34
|
-
|
|
35
|
-
// Temporarily disable hydration for portal content
|
|
36
|
-
if (was_hydrating) {
|
|
37
|
-
set_hydrating(false);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
try {
|
|
41
|
-
// Root event delegation lives in its own render block so it only
|
|
42
|
-
// re-runs when `props.target` changes — a children-only update must
|
|
43
|
-
// not release and re-acquire the target's delegated listeners.
|
|
44
|
-
render(() => {
|
|
45
|
-
const cleanup_events = handle_root_events(/** @type {Element} */ (props.target));
|
|
46
|
-
return cleanup_events;
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
render(() => {
|
|
50
|
-
const next_target = props.target;
|
|
51
|
-
const next_children = props.children;
|
|
52
|
-
|
|
53
|
-
if (target === next_target && children === next_children) return;
|
|
54
|
-
|
|
55
|
-
target = next_target;
|
|
56
|
-
children = next_children;
|
|
57
|
-
|
|
58
|
-
if (b !== null) {
|
|
59
|
-
destroy_block(b);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (anchor !== null) {
|
|
63
|
-
anchor.remove();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
dom_start = dom_end = null;
|
|
67
|
-
|
|
68
|
-
anchor = create_text();
|
|
69
|
-
/** @type {Element} */ (target).append(anchor);
|
|
70
|
-
|
|
71
|
-
var block = /** @type {Block} */ (active_block);
|
|
72
|
-
|
|
73
|
-
b = branch(() => {
|
|
74
|
-
if (is_tsrx_element(children)) {
|
|
75
|
-
children.render(/** @type {Text} */ (anchor), block);
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
dom_start = b?.s?.start;
|
|
80
|
-
dom_end = b?.s?.end;
|
|
81
|
-
|
|
82
|
-
return () => {
|
|
83
|
-
/** @type {Text} */ (anchor).remove();
|
|
84
|
-
if (dom_start && dom_end) {
|
|
85
|
-
remove_block_dom(dom_start, dom_end);
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
});
|
|
89
|
-
} finally {
|
|
90
|
-
// Restore hydration state
|
|
91
|
-
if (was_hydrating) {
|
|
92
|
-
set_hydrating(true);
|
|
93
|
-
set_hydrate_node(/** @type {any} */ (previous_hydrate_node));
|
|
94
|
-
}
|
|
95
|
-
}
|
|
169
|
+
create_portal(
|
|
170
|
+
() => props.target,
|
|
171
|
+
null,
|
|
172
|
+
() => props.children,
|
|
173
|
+
);
|
|
96
174
|
});
|
|
97
175
|
}
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
get_own_property_symbols,
|
|
10
10
|
get_prototype_of,
|
|
11
11
|
} from '@tsrx/core/runtime/language-helpers';
|
|
12
|
-
import {
|
|
12
|
+
import { event_listener } from './events.js';
|
|
13
13
|
import { get_attribute_event_name, is_event_attribute } from '@tsrx/core/runtime/events';
|
|
14
14
|
import { get } from './runtime.js';
|
|
15
15
|
import { hydrating } from './hydration.js';
|
|
@@ -22,18 +22,14 @@ import { normalize_css_property_name } from '@tsrx/core/runtime/html';
|
|
|
22
22
|
* @returns {void}
|
|
23
23
|
*/
|
|
24
24
|
export function set_text(text, value) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
previous = text.__t = text.nodeValue;
|
|
32
|
-
}
|
|
33
|
-
if (str !== previous) {
|
|
34
|
-
text.__t = str;
|
|
35
|
-
text.nodeValue = str + '';
|
|
25
|
+
var str = value == null ? '' : value + '';
|
|
26
|
+
// The compiled render block compares against the value it last wrote, so
|
|
27
|
+
// nothing is cached on the node. Only server-rendered text can already
|
|
28
|
+
// hold the value; a fresh template text node never does.
|
|
29
|
+
if (hydrating && text.nodeValue === str) {
|
|
30
|
+
return;
|
|
36
31
|
}
|
|
32
|
+
text.nodeValue = str;
|
|
37
33
|
}
|
|
38
34
|
|
|
39
35
|
/** @type {Map<string, string[]>} */
|
|
@@ -72,7 +68,7 @@ function get_setters(element) {
|
|
|
72
68
|
/**
|
|
73
69
|
* @param {Element} element
|
|
74
70
|
* @param {any} value
|
|
75
|
-
* @param {Record<string, string> | undefined} prev
|
|
71
|
+
* @param {Record<string, string | number | null | undefined> | undefined} prev
|
|
76
72
|
* @returns {void}
|
|
77
73
|
*/
|
|
78
74
|
export function set_style(element, value, prev = {}) {
|
|
@@ -104,8 +100,8 @@ export function set_attribute(element, attribute, value) {
|
|
|
104
100
|
|
|
105
101
|
/**
|
|
106
102
|
* @param {HTMLElement} element
|
|
107
|
-
* @param {Record<string, string | number>} new_styles
|
|
108
|
-
* @param {Record<string, string>} prev
|
|
103
|
+
* @param {Record<string, string | number | null | undefined>} new_styles
|
|
104
|
+
* @param {Record<string, string | number | null | undefined>} prev
|
|
109
105
|
*/
|
|
110
106
|
function apply_styles(element, new_styles, prev) {
|
|
111
107
|
const style = element.style;
|
|
@@ -113,7 +109,8 @@ function apply_styles(element, new_styles, prev) {
|
|
|
113
109
|
// Apply new styles
|
|
114
110
|
for (const key in new_styles) {
|
|
115
111
|
const css_prop = normalize_css_property_name(key);
|
|
116
|
-
const
|
|
112
|
+
const raw_value = new_styles[key];
|
|
113
|
+
const value = raw_value == null ? null : String(raw_value);
|
|
117
114
|
|
|
118
115
|
if (!(key in prev) || prev[key] !== value) {
|
|
119
116
|
style.setProperty(css_prop, value);
|
|
@@ -158,7 +155,7 @@ function set_attribute_helper(element, key, value, remove_listeners, prev) {
|
|
|
158
155
|
}
|
|
159
156
|
if (value != null) {
|
|
160
157
|
const event_name = get_attribute_event_name(key, value);
|
|
161
|
-
remove_listeners[key] =
|
|
158
|
+
remove_listeners[key] = event_listener(event_name, element, value);
|
|
162
159
|
}
|
|
163
160
|
} else {
|
|
164
161
|
set_attribute(element, key, value);
|
|
@@ -183,27 +180,20 @@ export function set_class(dom, value, hash, is_html = true) {
|
|
|
183
180
|
? value + (hash ? ' ' + hash : '')
|
|
184
181
|
: clsx([value, hash]);
|
|
185
182
|
|
|
186
|
-
//
|
|
187
|
-
//
|
|
188
|
-
//
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
dom.__className = class_value;
|
|
195
|
-
|
|
196
|
-
// Removing the attribute when the value is only an empty string causes
|
|
197
|
-
// performance issues vs simply making the className an empty string. So
|
|
198
|
-
// we should only remove the class if the value is nullish.
|
|
183
|
+
// The compiled render block compares against the class it last applied,
|
|
184
|
+
// so nothing is cached on the element. Removing the attribute when the
|
|
185
|
+
// value is only an empty string costs more than an empty className, so the
|
|
186
|
+
// class is removed only for a nullish value; an element that has no class
|
|
187
|
+
// does not receive an empty one either (the server omits an empty class
|
|
188
|
+
// attribute as well).
|
|
199
189
|
if (class_value === null) {
|
|
200
190
|
dom.removeAttribute('class');
|
|
201
|
-
} else {
|
|
202
|
-
if (
|
|
191
|
+
} else if (is_html) {
|
|
192
|
+
if (class_value !== '' || dom.className !== '') {
|
|
203
193
|
dom.className = class_value;
|
|
204
|
-
} else {
|
|
205
|
-
dom.setAttribute('class', class_value);
|
|
206
194
|
}
|
|
195
|
+
} else if (class_value !== '' || dom.getAttribute('class')) {
|
|
196
|
+
dom.setAttribute('class', class_value);
|
|
207
197
|
}
|
|
208
198
|
}
|
|
209
199
|
|
|
@@ -273,10 +263,9 @@ export function set_selected(element, selected) {
|
|
|
273
263
|
/**
|
|
274
264
|
* @param {Element} element
|
|
275
265
|
* @param {() => Record<string | symbol, any>} fn
|
|
276
|
-
* @param {string} [exclude_prop]
|
|
277
266
|
* @returns {() => void}
|
|
278
267
|
*/
|
|
279
|
-
export function apply_element_spread(element, fn
|
|
268
|
+
export function apply_element_spread(element, fn) {
|
|
280
269
|
/** @type {Record<string | symbol, any>} */
|
|
281
270
|
var prev = {};
|
|
282
271
|
/** @type {Record<string | symbol, Block | undefined>} */
|
|
@@ -323,10 +312,9 @@ export function apply_element_spread(element, fn, exclude_prop) {
|
|
|
323
312
|
|
|
324
313
|
/** @type {Record<string, any>} */
|
|
325
314
|
var current_ref_props = {};
|
|
315
|
+
var keys = spread_keys(next);
|
|
326
316
|
|
|
327
|
-
for (const key
|
|
328
|
-
if (key === exclude_prop) continue;
|
|
329
|
-
|
|
317
|
+
for (const key of keys) {
|
|
330
318
|
const ref_fn = next[key];
|
|
331
319
|
if (!is_ref_prop(ref_fn)) {
|
|
332
320
|
continue;
|
|
@@ -357,14 +345,14 @@ export function apply_element_spread(element, fn, exclude_prop) {
|
|
|
357
345
|
|
|
358
346
|
for (let key in remove_listeners) {
|
|
359
347
|
// Remove event listeners that are no longer present
|
|
360
|
-
if ((!(key
|
|
348
|
+
if ((!keys.includes(key) || is_ref_prop(next[key])) && remove_listeners[key]) {
|
|
361
349
|
remove_listeners[key]();
|
|
362
350
|
remove_listeners[key] = undefined;
|
|
363
351
|
}
|
|
364
352
|
}
|
|
365
353
|
|
|
366
354
|
for (const key in prev) {
|
|
367
|
-
if (!(key
|
|
355
|
+
if (!keys.includes(key) || is_ref_prop(next[key])) {
|
|
368
356
|
if (key === '#class') {
|
|
369
357
|
continue;
|
|
370
358
|
}
|
|
@@ -374,8 +362,8 @@ export function apply_element_spread(element, fn, exclude_prop) {
|
|
|
374
362
|
|
|
375
363
|
/** @type {typeof prev} */
|
|
376
364
|
const current = {};
|
|
377
|
-
for (const key
|
|
378
|
-
if (key === 'children'
|
|
365
|
+
for (const key of keys) {
|
|
366
|
+
if (key === 'children') continue;
|
|
379
367
|
|
|
380
368
|
let value = next[key];
|
|
381
369
|
if (is_ref_prop(value)) {
|
|
@@ -396,6 +384,19 @@ export function apply_element_spread(element, fn, exclude_prop) {
|
|
|
396
384
|
};
|
|
397
385
|
}
|
|
398
386
|
|
|
387
|
+
/**
|
|
388
|
+
* The keys an element spread applies: every prop of a props instance, or the
|
|
389
|
+
* enumerable keys of a plain object.
|
|
390
|
+
* @param {Record<string | symbol, any>} next
|
|
391
|
+
* @returns {string[]}
|
|
392
|
+
*/
|
|
393
|
+
function spread_keys(next) {
|
|
394
|
+
/** @type {string[]} */
|
|
395
|
+
var keys = [];
|
|
396
|
+
for (var key in next) keys.push(key);
|
|
397
|
+
return keys;
|
|
398
|
+
}
|
|
399
|
+
|
|
399
400
|
/**
|
|
400
401
|
* Keep spread refs in a branch block so ordinary spread updates do not destroy
|
|
401
402
|
* and recreate the ref block before `apply_element_spread` can compare the
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { encode, revive } from './transport.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @param {string} hash
|
|
5
5
|
* @param {any[]} args
|
|
6
6
|
*/
|
|
7
7
|
export async function rpc(hash, args) {
|
|
8
|
-
const body =
|
|
8
|
+
const body = encode(args);
|
|
9
9
|
/** @type {Response} */
|
|
10
10
|
let response;
|
|
11
11
|
|
|
@@ -55,5 +55,5 @@ export async function rpc(hash, args) {
|
|
|
55
55
|
);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
return
|
|
58
|
+
return revive(data).value;
|
|
59
59
|
}
|