@vyriy/render 0.4.10 → 0.5.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 +36 -1
- package/custom-element.js +19 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -88,6 +88,41 @@ customElement({
|
|
|
88
88
|
});
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
+
## Custom element SSR hydration
|
|
92
|
+
|
|
93
|
+
For SSR custom elements, render declarative shadow DOM with a stable mount node marked by `data-vyriy-root`.
|
|
94
|
+
The `rendered` attribute tells `customElement` to hydrate the existing shadow DOM instead of creating a new client container.
|
|
95
|
+
|
|
96
|
+
```html
|
|
97
|
+
<vyriy-profile-card rendered name="Ada">
|
|
98
|
+
<template shadowrootmode="open">
|
|
99
|
+
<link rel="stylesheet" href="/main.css" />
|
|
100
|
+
<div data-vyriy-root>
|
|
101
|
+
<!-- React SSR markup -->
|
|
102
|
+
</div>
|
|
103
|
+
</template>
|
|
104
|
+
</vyriy-profile-card>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
On the client, use the same registration. When the browser has already created `shadowRoot` from the template, `customElement` reuses it and calls `hydrateRoot` with `[data-vyriy-root]`.
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
customElement({
|
|
111
|
+
tag: 'vyriy-profile-card',
|
|
112
|
+
elements: () => {
|
|
113
|
+
const container = document.createElement('div');
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
elements: [container],
|
|
117
|
+
root: container,
|
|
118
|
+
};
|
|
119
|
+
},
|
|
120
|
+
render: (customElement) => {
|
|
121
|
+
return <ProfileCard name={customElement.getAttribute('name') ?? ''} />;
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
91
126
|
## HTML string
|
|
92
127
|
|
|
93
128
|
```tsx
|
|
@@ -111,7 +146,7 @@ const htmlStream = await stream({
|
|
|
111
146
|
});
|
|
112
147
|
```
|
|
113
148
|
|
|
114
|
-
##
|
|
149
|
+
## Prerender
|
|
115
150
|
|
|
116
151
|
```tsx
|
|
117
152
|
import { prerender } from '@vyriy/render';
|
package/custom-element.js
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import { createRoot, hydrateRoot } from 'react-dom/client';
|
|
2
2
|
import { createMissingCustomElementRootError } from './errors.js';
|
|
3
|
+
const rootSelector = '[data-vyriy-root]';
|
|
4
|
+
const fallbackRootSelector = ':not(link, style, template)';
|
|
5
|
+
const findCustomElementRoot = (shadow) => {
|
|
6
|
+
return shadow.querySelector(rootSelector) ?? shadow.querySelector(fallbackRootSelector);
|
|
7
|
+
};
|
|
8
|
+
const requireCustomElementRoot = (root) => {
|
|
9
|
+
if (!root) {
|
|
10
|
+
throw createMissingCustomElementRootError();
|
|
11
|
+
}
|
|
12
|
+
return root;
|
|
13
|
+
};
|
|
3
14
|
export const customElement = ({ tag, mode = 'open', renderedAttribute = 'rendered', elements, render, options = {}, }) => {
|
|
4
15
|
if (customElements.get(tag)) {
|
|
5
16
|
return;
|
|
@@ -9,12 +20,15 @@ export const customElement = ({ tag, mode = 'open', renderedAttribute = 'rendere
|
|
|
9
20
|
#mount;
|
|
10
21
|
constructor() {
|
|
11
22
|
super();
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
23
|
+
const existingShadow = this.shadowRoot;
|
|
24
|
+
const shadow = existingShadow ?? this.attachShadow({ mode });
|
|
25
|
+
const isHydratableShadow = this.hasAttribute(renderedAttribute) && existingShadow;
|
|
26
|
+
if (isHydratableShadow) {
|
|
27
|
+
this.#mount = requireCustomElementRoot(findCustomElementRoot(existingShadow));
|
|
28
|
+
return;
|
|
16
29
|
}
|
|
17
|
-
|
|
30
|
+
const renderElements = elements(this);
|
|
31
|
+
this.#mount = requireCustomElementRoot(renderElements.root);
|
|
18
32
|
shadow.append(...renderElements.elements);
|
|
19
33
|
}
|
|
20
34
|
connectedCallback() {
|