ripple 0.3.128 → 0.4.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/CHANGELOG.md +267 -0
- package/package.json +10 -4
- 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 +39 -39
- 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 +325 -275
- 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 +86 -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 +42 -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/vite-plugin-config.test.js +23 -0
- package/types/index.d.ts +48 -7
- 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/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
|
@@ -12,13 +12,13 @@ export function StaticTitle() @{
|
|
|
12
12
|
|
|
13
13
|
// Reactive title
|
|
14
14
|
export function ReactiveTitle() @{
|
|
15
|
-
|
|
15
|
+
const title = track('Initial Title');
|
|
16
16
|
<>
|
|
17
17
|
<head>
|
|
18
|
-
<title>{title}</title>
|
|
18
|
+
<title>{title.value}</title>
|
|
19
19
|
</head>
|
|
20
20
|
<div>
|
|
21
|
-
<span>{title}</span>
|
|
21
|
+
<span>{title.value}</span>
|
|
22
22
|
</div>
|
|
23
23
|
</>
|
|
24
24
|
}
|
|
@@ -37,24 +37,24 @@ export function MultipleHeadElements() @{
|
|
|
37
37
|
|
|
38
38
|
// Head with reactive meta tags
|
|
39
39
|
export function ReactiveMetaTags() @{
|
|
40
|
-
|
|
40
|
+
const description = track('Initial description');
|
|
41
41
|
<>
|
|
42
42
|
<head>
|
|
43
43
|
<title>{'My Page'}</title>
|
|
44
|
-
<meta name="description" content={description} />
|
|
44
|
+
<meta name="description" content={description.value} />
|
|
45
45
|
</head>
|
|
46
|
-
<div>{description}</div>
|
|
46
|
+
<div>{description.value}</div>
|
|
47
47
|
</>
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
// Title with template literal
|
|
51
51
|
export function TitleWithTemplate() @{
|
|
52
|
-
|
|
52
|
+
const name = track('World');
|
|
53
53
|
<>
|
|
54
54
|
<head>
|
|
55
|
-
<title>{`Hello ${name}!`}</title>
|
|
55
|
+
<title>{`Hello ${name.value}!`}</title>
|
|
56
56
|
</head>
|
|
57
|
-
<div>{name}</div>
|
|
57
|
+
<div>{name.value}</div>
|
|
58
58
|
</>
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -70,28 +70,28 @@ export function EmptyTitle() @{
|
|
|
70
70
|
|
|
71
71
|
// Title with conditional content
|
|
72
72
|
export function ConditionalTitle() @{
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
const showPrefix = track(true);
|
|
74
|
+
const title = track('Main Page');
|
|
75
75
|
<>
|
|
76
76
|
<head>
|
|
77
77
|
<title>
|
|
78
|
-
{showPrefix ? 'App - ' + title : title}
|
|
78
|
+
{showPrefix.value ? 'App - ' + title.value : title.value}
|
|
79
79
|
</title>
|
|
80
80
|
</head>
|
|
81
|
-
<div>{title}</div>
|
|
81
|
+
<div>{title.value}</div>
|
|
82
82
|
</>
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// Title with computed value
|
|
86
86
|
export function ComputedTitle() @{
|
|
87
|
-
|
|
87
|
+
const count = track(0);
|
|
88
88
|
let prefix = 'Count: ';
|
|
89
89
|
<>
|
|
90
90
|
<head>
|
|
91
|
-
<title>{prefix + count}</title>
|
|
91
|
+
<title>{prefix + count.value}</title>
|
|
92
92
|
</head>
|
|
93
93
|
<div>
|
|
94
|
-
<span>{count}</span>
|
|
94
|
+
<span>{count.value}</span>
|
|
95
95
|
</div>
|
|
96
96
|
</>
|
|
97
97
|
}
|
|
@@ -17,9 +17,9 @@ export function Layout({ children }: { children: any }) @{
|
|
|
17
17
|
|
|
18
18
|
// A child component with an if block (the key source of deep hydrate_node)
|
|
19
19
|
export function Content() @{
|
|
20
|
-
|
|
20
|
+
const visible = track(true);
|
|
21
21
|
<div class="content">
|
|
22
|
-
@if (visible) {
|
|
22
|
+
@if (visible.value) {
|
|
23
23
|
<p class="text">{'Hello world'}</p>
|
|
24
24
|
}
|
|
25
25
|
</div>
|
|
@@ -89,7 +89,7 @@ function DocFooter() @{
|
|
|
89
89
|
<footer class="doc-footer">{'Footer content'}</footer>
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
export function DocLayout(
|
|
92
|
+
export function DocLayout({
|
|
93
93
|
children,
|
|
94
94
|
editPath = '',
|
|
95
95
|
nextLink = null,
|
|
@@ -274,7 +274,7 @@ export function HtmlAfterComponentInChildren() @{
|
|
|
274
274
|
</ContentWrapper>
|
|
275
275
|
}
|
|
276
276
|
|
|
277
|
-
function NavItem(
|
|
277
|
+
function NavItem({
|
|
278
278
|
href,
|
|
279
279
|
text: label,
|
|
280
280
|
active = false,
|
|
@@ -294,13 +294,13 @@ function NavItem(&{
|
|
|
294
294
|
}
|
|
295
295
|
|
|
296
296
|
function SidebarSection({ title, children }: { title: string; children: any }) @{
|
|
297
|
-
|
|
297
|
+
const expanded = track(true);
|
|
298
298
|
<section class="sidebar-section">
|
|
299
299
|
<div class="section-header">
|
|
300
300
|
<h2>{title}</h2>
|
|
301
|
-
<button onClick={() => (expanded = !expanded)}>{'Toggle'}</button>
|
|
301
|
+
<button onClick={() => (expanded.value = !expanded.value)}>{'Toggle'}</button>
|
|
302
302
|
</div>
|
|
303
|
-
@if (expanded) {
|
|
303
|
+
@if (expanded.value) {
|
|
304
304
|
<div class="section-items">{children}</div>
|
|
305
305
|
}
|
|
306
306
|
</section>
|
|
@@ -432,7 +432,7 @@ function FooterStub() @{
|
|
|
432
432
|
<footer class="footer">{'Footer'}</footer>
|
|
433
433
|
}
|
|
434
434
|
|
|
435
|
-
function DocsLayoutInner(
|
|
435
|
+
function DocsLayoutInner({
|
|
436
436
|
children,
|
|
437
437
|
editPath = '',
|
|
438
438
|
nextLink = null,
|
|
@@ -485,7 +485,7 @@ export function DocsLayoutWithoutData() @{
|
|
|
485
485
|
</DocsLayoutInner>
|
|
486
486
|
}
|
|
487
487
|
|
|
488
|
-
function DocsLayoutExact(
|
|
488
|
+
function DocsLayoutExact({
|
|
489
489
|
children,
|
|
490
490
|
editPath = '',
|
|
491
491
|
prevLink = null,
|
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import { track } from 'ripple';
|
|
1
|
+
import { track, type Tracked } from 'ripple';
|
|
2
2
|
|
|
3
3
|
// Minimal repro for hydration issue with if block containing children
|
|
4
4
|
// Based on SidebarGroup pattern from website-new
|
|
5
5
|
|
|
6
6
|
export function IfWithChildren({ children }: { children: any }) @{
|
|
7
|
-
|
|
7
|
+
const expanded = track(true);
|
|
8
8
|
<div class="container">
|
|
9
|
-
<div
|
|
10
|
-
|
|
9
|
+
<div
|
|
10
|
+
class="header"
|
|
11
|
+
role="button"
|
|
12
|
+
onClick={() => (expanded.value = !expanded.value)}
|
|
13
|
+
>{'Toggle'}</div>
|
|
14
|
+
@if (expanded.value) {
|
|
11
15
|
<div class="content">{children}</div>
|
|
12
16
|
}
|
|
13
17
|
</div>
|
|
@@ -26,10 +30,14 @@ export function TestIfWithChildren() @{
|
|
|
26
30
|
|
|
27
31
|
// Simpler variant - if block with static children
|
|
28
32
|
export function IfWithStaticChildren() @{
|
|
29
|
-
|
|
33
|
+
const expanded = track(true);
|
|
30
34
|
<div class="container">
|
|
31
|
-
<div
|
|
32
|
-
|
|
35
|
+
<div
|
|
36
|
+
class="header"
|
|
37
|
+
role="button"
|
|
38
|
+
onClick={() => (expanded.value = !expanded.value)}
|
|
39
|
+
>{'Toggle'}</div>
|
|
40
|
+
@if (expanded.value) {
|
|
33
41
|
<div class="content">
|
|
34
42
|
<span>{'Static child 1'}</span>
|
|
35
43
|
<span>{'Static child 2'}</span>
|
|
@@ -40,9 +48,9 @@ export function IfWithStaticChildren() @{
|
|
|
40
48
|
|
|
41
49
|
// Variant with sibling elements before the if block (like SidebarGroup)
|
|
42
50
|
export function IfWithSiblingsAndChildren({ children }: { children: any }) @{
|
|
43
|
-
|
|
51
|
+
const expanded = track(true);
|
|
44
52
|
<section class="group">
|
|
45
|
-
<div class="item" role="button" onClick={() => (expanded = !expanded)}>
|
|
53
|
+
<div class="item" role="button" onClick={() => (expanded.value = !expanded.value)}>
|
|
46
54
|
<div class="indicator" />
|
|
47
55
|
<h2 class="text">{'Title'}</h2>
|
|
48
56
|
<div class="caret">
|
|
@@ -51,7 +59,7 @@ export function IfWithSiblingsAndChildren({ children }: { children: any }) @{
|
|
|
51
59
|
</svg>
|
|
52
60
|
</div>
|
|
53
61
|
</div>
|
|
54
|
-
@if (expanded) {
|
|
62
|
+
@if (expanded.value) {
|
|
55
63
|
<div class="items">{children}</div>
|
|
56
64
|
}
|
|
57
65
|
</section>
|
|
@@ -68,7 +76,7 @@ export function TestIfWithSiblingsAndChildren() @{
|
|
|
68
76
|
// This tests that hydrate_node is properly restored after processing an element's children
|
|
69
77
|
// before navigating to a dynamic sibling (if/for/switch)
|
|
70
78
|
export function ElementWithChildrenThenIf() @{
|
|
71
|
-
|
|
79
|
+
const show = track(true);
|
|
72
80
|
<>
|
|
73
81
|
<div class="wrapper">
|
|
74
82
|
<div class="nested-parent">
|
|
@@ -76,17 +84,17 @@ export function ElementWithChildrenThenIf() @{
|
|
|
76
84
|
<span class="deep">{'Deep content'}</span>
|
|
77
85
|
</div>
|
|
78
86
|
</div>
|
|
79
|
-
@if (show) {
|
|
87
|
+
@if (show.value) {
|
|
80
88
|
<div class="conditional">{'Conditional content'}</div>
|
|
81
89
|
}
|
|
82
90
|
</div>
|
|
83
|
-
<button class="toggle" onClick={() => (show = !show)}>{'Toggle'}</button>
|
|
91
|
+
<button class="toggle" onClick={() => (show.value = !show.value)}>{'Toggle'}</button>
|
|
84
92
|
</>
|
|
85
93
|
}
|
|
86
94
|
|
|
87
95
|
// More complex: multiple levels of nesting before if sibling
|
|
88
96
|
export function DeepNestingThenIf() @{
|
|
89
|
-
|
|
97
|
+
const visible = track(true);
|
|
90
98
|
<>
|
|
91
99
|
<section class="outer">
|
|
92
100
|
<article class="middle">
|
|
@@ -97,11 +105,11 @@ export function DeepNestingThenIf() @{
|
|
|
97
105
|
</p>
|
|
98
106
|
</div>
|
|
99
107
|
</article>
|
|
100
|
-
@if (visible) {
|
|
108
|
+
@if (visible.value) {
|
|
101
109
|
<footer class="footer">{'Footer'}</footer>
|
|
102
110
|
}
|
|
103
111
|
</section>
|
|
104
|
-
<button class="btn" onClick={() => (visible = !visible)}>{'Toggle'}</button>
|
|
112
|
+
<button class="btn" onClick={() => (visible.value = !visible.value)}>{'Toggle'}</button>
|
|
105
113
|
</>
|
|
106
114
|
}
|
|
107
115
|
|
|
@@ -109,22 +117,22 @@ export function DeepNestingThenIf() @{
|
|
|
109
117
|
// followed by another sibling element. This requires pop() to restore hydrate_node
|
|
110
118
|
// because we descend into the first element to get the button children.
|
|
111
119
|
export function DomElementChildrenThenSibling() @{
|
|
112
|
-
|
|
120
|
+
const activeTab = track('code');
|
|
113
121
|
<div class="tabs">
|
|
114
122
|
<div class="tab-list">
|
|
115
123
|
<button
|
|
116
124
|
class="tab"
|
|
117
|
-
aria-selected={activeTab === 'code' ? 'true' : 'false'}
|
|
118
|
-
onClick={() => (activeTab = 'code')}
|
|
125
|
+
aria-selected={activeTab.value === 'code' ? 'true' : 'false'}
|
|
126
|
+
onClick={() => (activeTab.value = 'code')}
|
|
119
127
|
>{'Code'}</button>
|
|
120
128
|
<button
|
|
121
129
|
class="tab"
|
|
122
|
-
aria-selected={activeTab === 'preview' ? 'true' : 'false'}
|
|
123
|
-
onClick={() => (activeTab = 'preview')}
|
|
130
|
+
aria-selected={activeTab.value === 'preview' ? 'true' : 'false'}
|
|
131
|
+
onClick={() => (activeTab.value = 'preview')}
|
|
124
132
|
>{'Preview'}</button>
|
|
125
133
|
</div>
|
|
126
134
|
<div class="panel">
|
|
127
|
-
@if (activeTab === 'code') {
|
|
135
|
+
@if (activeTab.value === 'code') {
|
|
128
136
|
<pre class="code">{'const x = 1;'}</pre>
|
|
129
137
|
} @else {
|
|
130
138
|
<div class="preview">{'Preview content'}</div>
|
|
@@ -137,20 +145,20 @@ export function DomElementChildrenThenSibling() @{
|
|
|
137
145
|
// generate sibling() calls. This was causing incorrect pop() generation before next().
|
|
138
146
|
// Pattern: <ul> with dynamic <li> children -> static <h2> -> static <p> -> next()
|
|
139
147
|
export function DomChildrenThenStaticSiblings() @{
|
|
140
|
-
|
|
148
|
+
const count = track(0);
|
|
141
149
|
<>
|
|
142
150
|
<div class="container">
|
|
143
151
|
<ul class="list">
|
|
144
152
|
<li class="item">
|
|
145
153
|
{'Item count: '}
|
|
146
|
-
{count}
|
|
154
|
+
{count.value}
|
|
147
155
|
</li>
|
|
148
156
|
<li class="item">{'Another item'}</li>
|
|
149
157
|
</ul>
|
|
150
158
|
<h2 class="heading">{'Static Heading'}</h2>
|
|
151
159
|
<p class="para">{'Static paragraph'}</p>
|
|
152
160
|
</div>
|
|
153
|
-
<button class="inc" onClick={() => count++}>{'Increment'}</button>
|
|
161
|
+
<button class="inc" onClick={() => count.value++}>{'Increment'}</button>
|
|
154
162
|
</>
|
|
155
163
|
}
|
|
156
164
|
|
|
@@ -183,3 +191,42 @@ export function StaticListThenStaticSiblings() @{
|
|
|
183
191
|
</p>
|
|
184
192
|
</div>
|
|
185
193
|
}
|
|
194
|
+
|
|
195
|
+
// All-component children render into their parent through an append-into
|
|
196
|
+
// sentinel instead of a `<!>` placeholder per child. A child whose root is
|
|
197
|
+
// control flow keeps re-rendering relative to its anchor after hydration, so it
|
|
198
|
+
// must resolve the sentinel to its own position rather than the parent's end.
|
|
199
|
+
function RootIfChild(props: { on: Tracked<boolean> }) @{
|
|
200
|
+
@if (props.on.value) {
|
|
201
|
+
<span class="root-if">{'on'}</span>
|
|
202
|
+
} @else {
|
|
203
|
+
<span class="root-if">{'off'}</span>
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function RootForChild(props: { items: Tracked<number[]> }) @{
|
|
208
|
+
@for (const item of props.items.value; key item) {
|
|
209
|
+
<span class="root-for">{item}</span>
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function TrailingChild() @{
|
|
214
|
+
<span class="trailing">{'end'}</span>
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function ComponentChildrenWithControlFlowRoots() @{
|
|
218
|
+
const on = track(true);
|
|
219
|
+
const items = track([1, 2, 3]);
|
|
220
|
+
<div class="wrapper">
|
|
221
|
+
<div class="host">
|
|
222
|
+
<RootIfChild {on} />
|
|
223
|
+
<RootForChild {items} />
|
|
224
|
+
<TrailingChild />
|
|
225
|
+
</div>
|
|
226
|
+
<button class="toggle" onClick={() => (on.value = !on.value)}>{'Toggle'}</button>
|
|
227
|
+
<button
|
|
228
|
+
class="rotate"
|
|
229
|
+
onClick={() => (items.value = [items.value[1], items.value[2], items.value[0]])}
|
|
230
|
+
>{'Rotate'}</button>
|
|
231
|
+
</div>
|
|
232
|
+
}
|
|
@@ -26,30 +26,30 @@ export function IfElse() @{
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export function ReactiveIf() @{
|
|
29
|
-
|
|
29
|
+
const show = track(true);
|
|
30
30
|
<>
|
|
31
31
|
<button
|
|
32
32
|
class="toggle"
|
|
33
33
|
onClick={() => {
|
|
34
|
-
show = !show;
|
|
34
|
+
show.value = !show.value;
|
|
35
35
|
}}
|
|
36
36
|
>{'Toggle'}</button>
|
|
37
|
-
@if (show) {
|
|
37
|
+
@if (show.value) {
|
|
38
38
|
<div class="content">{'Content visible'}</div>
|
|
39
39
|
}
|
|
40
40
|
</>
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
export function ReactiveIfElse() @{
|
|
44
|
-
|
|
44
|
+
const isOn = track(false);
|
|
45
45
|
<>
|
|
46
46
|
<button
|
|
47
47
|
class="toggle"
|
|
48
48
|
onClick={() => {
|
|
49
|
-
isOn = !isOn;
|
|
49
|
+
isOn.value = !isOn.value;
|
|
50
50
|
}}
|
|
51
51
|
>{'Toggle'}</button>
|
|
52
|
-
@if (isOn) {
|
|
52
|
+
@if (isOn.value) {
|
|
53
53
|
<div class="on">{'ON'}</div>
|
|
54
54
|
} @else {
|
|
55
55
|
<div class="off">{'OFF'}</div>
|
|
@@ -58,25 +58,25 @@ export function ReactiveIfElse() @{
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
export function NestedIf() @{
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
const outer = track(true);
|
|
62
|
+
const inner = track(true);
|
|
63
63
|
<>
|
|
64
64
|
<button
|
|
65
65
|
class="outer-toggle"
|
|
66
66
|
onClick={() => {
|
|
67
|
-
outer = !outer;
|
|
67
|
+
outer.value = !outer.value;
|
|
68
68
|
}}
|
|
69
69
|
>{'Outer'}</button>
|
|
70
70
|
<button
|
|
71
71
|
class="inner-toggle"
|
|
72
72
|
onClick={() => {
|
|
73
|
-
inner = !inner;
|
|
73
|
+
inner.value = !inner.value;
|
|
74
74
|
}}
|
|
75
75
|
>{'Inner'}</button>
|
|
76
|
-
@if (outer) {
|
|
76
|
+
@if (outer.value) {
|
|
77
77
|
<div class="outer-content">
|
|
78
78
|
{'Outer'}
|
|
79
|
-
@if (inner) {
|
|
79
|
+
@if (inner.value) {
|
|
80
80
|
<span class="inner-content">{'Inner'}</span>
|
|
81
81
|
}
|
|
82
82
|
</div>
|
|
@@ -85,29 +85,29 @@ export function NestedIf() @{
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
export function IfElseIfChain() @{
|
|
88
|
-
|
|
88
|
+
const status = track<'loading' | 'success' | 'error'>('loading');
|
|
89
89
|
<div>
|
|
90
90
|
<button
|
|
91
91
|
class="success"
|
|
92
92
|
onClick={() => {
|
|
93
|
-
status = 'success';
|
|
93
|
+
status.value = 'success';
|
|
94
94
|
}}
|
|
95
95
|
>{'Success'}</button>
|
|
96
96
|
<button
|
|
97
97
|
class="error"
|
|
98
98
|
onClick={() => {
|
|
99
|
-
status = 'error';
|
|
99
|
+
status.value = 'error';
|
|
100
100
|
}}
|
|
101
101
|
>{'Error'}</button>
|
|
102
102
|
<button
|
|
103
103
|
class="loading"
|
|
104
104
|
onClick={() => {
|
|
105
|
-
status = 'loading';
|
|
105
|
+
status.value = 'loading';
|
|
106
106
|
}}
|
|
107
107
|
>{'Loading'}</button>
|
|
108
|
-
@if (status === 'loading') {
|
|
108
|
+
@if (status.value === 'loading') {
|
|
109
109
|
<div class="state">{'Loading...'}</div>
|
|
110
|
-
} @else if (status === 'success') {
|
|
110
|
+
} @else if (status.value === 'success') {
|
|
111
111
|
<div class="state">{'Success!'}</div>
|
|
112
112
|
} @else {
|
|
113
113
|
<div class="state">{'Error occurred'}</div>
|
|
@@ -31,9 +31,9 @@ export function MixedControlFlowStatic() @{
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export function MixedControlFlowReactive() @{
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
const show = track(true);
|
|
35
|
+
const mode = track<'a' | 'b'>('a');
|
|
36
|
+
const items = track([
|
|
37
37
|
{ id: 1, label: 'One' },
|
|
38
38
|
{ id: 2, label: 'Two' },
|
|
39
39
|
]);
|
|
@@ -41,25 +41,25 @@ export function MixedControlFlowReactive() @{
|
|
|
41
41
|
<button
|
|
42
42
|
class="toggle-show"
|
|
43
43
|
onClick={() => {
|
|
44
|
-
show = !show;
|
|
44
|
+
show.value = !show.value;
|
|
45
45
|
}}
|
|
46
46
|
>{'Toggle Show'}</button>
|
|
47
47
|
<button
|
|
48
48
|
class="toggle-mode"
|
|
49
49
|
onClick={() => {
|
|
50
|
-
mode = mode === 'a' ? 'b' : 'a';
|
|
50
|
+
mode.value = mode.value === 'a' ? 'b' : 'a';
|
|
51
51
|
}}
|
|
52
52
|
>{'Toggle Mode'}</button>
|
|
53
53
|
<button
|
|
54
54
|
class="add-item"
|
|
55
55
|
onClick={() => {
|
|
56
|
-
items = [...items, { id: 3, label: 'Three' }];
|
|
56
|
+
items.value = [...items.value, { id: 3, label: 'Three' }];
|
|
57
57
|
}}
|
|
58
58
|
>{'Add Item'}</button>
|
|
59
|
-
@if (show) {
|
|
59
|
+
@if (show.value) {
|
|
60
60
|
<div class="mixed-reactive-list">
|
|
61
|
-
@for (const item of items; key item.id) {
|
|
62
|
-
@switch (mode) {
|
|
61
|
+
@for (const item of items.value; key item.id) {
|
|
62
|
+
@switch (mode.value) {
|
|
63
63
|
@case 'a': {
|
|
64
64
|
@try {
|
|
65
65
|
<p class={`item item-${item.id}`}>{`A:${item.label}`}</p>
|
|
@@ -106,6 +106,6 @@ export function MixedControlFlowAsyncPending() @{
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
function AsyncRow({ label }: { label: string }) @{
|
|
109
|
-
|
|
110
|
-
<div class="resolved-row">{value}</div>
|
|
109
|
+
const value = trackAsync(() => Promise.resolve(label));
|
|
110
|
+
<div class="resolved-row">{value.value}</div>
|
|
111
111
|
}
|
|
@@ -13,10 +13,10 @@ export function SimplePortal() @{
|
|
|
13
13
|
|
|
14
14
|
// Portal with conditional rendering
|
|
15
15
|
export function ConditionalPortal() @{
|
|
16
|
-
|
|
16
|
+
const show = track(true);
|
|
17
17
|
<div class="container">
|
|
18
|
-
<button class="toggle" onClick={() => (show = !show)}>{'Toggle'}</button>
|
|
19
|
-
@if (show) {
|
|
18
|
+
<button class="toggle" onClick={() => (show.value = !show.value)}>{'Toggle'}</button>
|
|
19
|
+
@if (show.value) {
|
|
20
20
|
<Portal target={typeof document !== 'undefined' ? document.body : null}>
|
|
21
21
|
<div class="portal-content">{'Portal is visible'}</div>
|
|
22
22
|
</Portal>
|
|
@@ -3,14 +3,14 @@ import { track } from 'ripple';
|
|
|
3
3
|
// Reactive components for hydration testing
|
|
4
4
|
|
|
5
5
|
export function TrackedState() @{
|
|
6
|
-
|
|
7
|
-
<div class="count">{count}</div>
|
|
6
|
+
const count = track(0);
|
|
7
|
+
<div class="count">{count.value}</div>
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export function CounterWithInitial(props: { initial: number }) @{
|
|
11
|
-
|
|
11
|
+
const count = track(props.initial);
|
|
12
12
|
<div>
|
|
13
|
-
<span class="count">{count}</span>
|
|
13
|
+
<span class="count">{count.value}</span>
|
|
14
14
|
</div>
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -19,26 +19,26 @@ export function CounterWrapper() @{
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export function ComputedValues() @{
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const sum = () => a + b;
|
|
22
|
+
const a = track(2);
|
|
23
|
+
const b = track(3);
|
|
24
|
+
const sum = () => a.value + b.value;
|
|
25
25
|
<div class="sum">{sum()}</div>
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export function MultipleTracked() @{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
const x = track(10);
|
|
30
|
+
const y = track(20);
|
|
31
|
+
const z = track(30);
|
|
32
32
|
<div class="multiple-tracked">
|
|
33
|
-
<div class="x">{x}</div>
|
|
34
|
-
<div class="y">{y}</div>
|
|
35
|
-
<div class="z">{z}</div>
|
|
33
|
+
<div class="x">{x.value}</div>
|
|
34
|
+
<div class="y">{y.value}</div>
|
|
35
|
+
<div class="z">{z.value}</div>
|
|
36
36
|
</div>
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export function DerivedState() @{
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const fullName = () => `${firstName} ${lastName}`;
|
|
40
|
+
const firstName = track('John');
|
|
41
|
+
const lastName = track('Doe');
|
|
42
|
+
const fullName = () => `${firstName.value} ${lastName.value}`;
|
|
43
43
|
<div class="name">{fullName()}</div>
|
|
44
44
|
}
|
|
@@ -32,11 +32,11 @@ export function resetControls() {
|
|
|
32
32
|
resetControls();
|
|
33
33
|
|
|
34
34
|
function BasicContent() @{
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
const data = trackAsync(() => controls.basic.promise);
|
|
36
|
+
const count = track(0);
|
|
37
37
|
<div class="resolved">
|
|
38
|
-
<span class="value">{data + ':' + count}</span>
|
|
39
|
-
<button class="inc" onClick={() => count++}>{'inc'}</button>
|
|
38
|
+
<span class="value">{data.value + ':' + count.value}</span>
|
|
39
|
+
<button class="inc" onClick={() => count.value++}>{'inc'}</button>
|
|
40
40
|
</div>
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -56,8 +56,8 @@ export function StreamPending() @{
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
function CatchOnlyContent() @{
|
|
59
|
-
|
|
60
|
-
<p class="resolved">{data}</p>
|
|
59
|
+
const data = trackAsync(() => controls.catchOnly.promise);
|
|
60
|
+
<p class="resolved">{data.value}</p>
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
export function StreamCatchOnly() @{
|
|
@@ -72,8 +72,8 @@ export function StreamCatchOnly() @{
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
function RejectContent() @{
|
|
75
|
-
|
|
76
|
-
<p class="resolved">{data}</p>
|
|
75
|
+
const data = trackAsync(() => controls.rejects.promise);
|
|
76
|
+
<p class="resolved">{data.value}</p>
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
export function StreamRejects() @{
|
|
@@ -87,8 +87,8 @@ export function StreamRejects() @{
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
function NoCatchContent() @{
|
|
90
|
-
|
|
91
|
-
<p class="resolved">{data}</p>
|
|
90
|
+
const data = trackAsync(() => controls.noCatch.promise);
|
|
91
|
+
<p class="resolved">{data.value}</p>
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
export function StreamNoCatch() @{
|
|
@@ -108,14 +108,14 @@ export function RootPending() @{
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
function HeadContent() @{
|
|
111
|
-
|
|
111
|
+
const data = trackAsync(() => controls.head.promise);
|
|
112
112
|
<>
|
|
113
|
-
@if (data) {
|
|
113
|
+
@if (data.value) {
|
|
114
114
|
<>
|
|
115
115
|
<head>
|
|
116
|
-
<title>{'title:' + data}</title>
|
|
116
|
+
<title>{'title:' + data.value}</title>
|
|
117
117
|
</head>
|
|
118
|
-
<p class="head-content">{data}</p>
|
|
118
|
+
<p class="head-content">{data.value}</p>
|
|
119
119
|
</>
|
|
120
120
|
}
|
|
121
121
|
</>
|
|
@@ -130,18 +130,18 @@ export function StreamHead() @{
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
export function StreamRootDirect() @{
|
|
133
|
-
|
|
134
|
-
<p class="root-async">{data}</p>
|
|
133
|
+
const data = trackAsync(() => controls.rootDirect.promise);
|
|
134
|
+
<p class="root-async">{data.value}</p>
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
function OuterContent() @{
|
|
138
|
-
|
|
139
|
-
<p class="outer">{data}</p>
|
|
138
|
+
const data = trackAsync(() => controls.outer.promise);
|
|
139
|
+
<p class="outer">{data.value}</p>
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
function InnerContent() @{
|
|
143
|
-
|
|
144
|
-
<p class="inner">{data}</p>
|
|
143
|
+
const data = trackAsync(() => controls.inner.promise);
|
|
144
|
+
<p class="inner">{data.value}</p>
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
export function StreamNested() @{
|