Rocket Projection Pro
Rocket is currently in beta.
A small demo showing how Rocket components can work with host-provided children in both light DOM and shadow DOM.
- Light DOM host children get transformed so they can read/write
$$signals. - Shadow DOM slots get the same treatment; projected content can mutate component state.
- Both components expose a shared, simple API: you pass children, Rocket handles scope.
Explanation #
This is an intentional advanced example. Unlike the simpler prop-driven demos, host-provided children here are supposed to read and write component-local $$ state after Rocket rewrites those expressions into the instance scope.
Usage Example #
1<light-counter>
2 <div class="callout success">
3 <button
4 data-on:click="$$clicks++"
5 data-text="'Light clicks: ' + $$clicks"
6 ></button>
7 </div>
8</light-counter>
9
10<slot-counter>
11 <button
12 data-on:click="$$clicks++"
13 data-text="'Shadow clicks: ' + $$clicks"
14 ></button>
15</slot-counter>Rocket Components #
1import { rocket } from 'datastar'
2
3const rewriteRocketScopedChildren = (root, base) => {
4 if (!base) return
5 for (const node of root.querySelectorAll('*')) {
6 for (const { name, value } of [...node.attributes]) {
7 if (!name.startsWith('data-') || !value.includes('$$')) continue
8 node.setAttribute(
9 name,
10 value.replace(
11 /\$\$([a-zA-Z_\d]\w*(?:[.-]\w+)*)/g,
12 (_, path) => `$${'.'}${base}.${path}`,
13 ),
14 )
15 }
16 }
17}
18
19rocket('light-counter', {
20 mode: 'light',
21 props: ({ number }) => ({ clicks: number }),
22 setup: ({ $$, props }) => {
23 $$.clicks = props.clicks
24 },
25 render: ({ html }) => html`
26 <div data-rocket-content>
27 <slot></slot>
28 </div>
29 `,
30})
31
32rocket('slot-counter', {
33 props: ({ number }) => ({ clicks: number }),
34 setup: ({ apply, host, $$, props }) => {
35 $$.clicks = props.clicks
36 rewriteRocketScopedChildren(host, host.rocketSignalPath)
37 apply(host, false)
38 },
39 render: ({ html }) => html`
40 <div class="callout success">
41 <slot></slot>
42 </div>
43 `,
44})