Compare commits
1 commit
main
...
spike/cont
Author | SHA1 | Date | |
---|---|---|---|
|
8541e64be5 |
8 changed files with 131 additions and 50 deletions
|
@ -1,5 +1,6 @@
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import './Counter.css';
|
import './Counter.css';
|
||||||
|
import { Context } from './Provider.tsx';
|
||||||
|
|
||||||
export default function Counter({
|
export default function Counter({
|
||||||
children,
|
children,
|
||||||
|
@ -11,15 +12,16 @@ export default function Counter({
|
||||||
const [count, setCount] = useState(initialCount);
|
const [count, setCount] = useState(initialCount);
|
||||||
const add = () => setCount((i) => i + 1);
|
const add = () => setCount((i) => i + 1);
|
||||||
const subtract = () => setCount((i) => i - 1);
|
const subtract = () => setCount((i) => i - 1);
|
||||||
|
console.log('Counter', { count });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Context.Provider value={{ count }}>
|
||||||
<div className="counter">
|
<div className="counter">
|
||||||
<button onClick={subtract}>-</button>
|
<button onClick={subtract}>-</button>
|
||||||
<pre>{count}</pre>
|
<pre>{count}</pre>
|
||||||
<button onClick={add}>+</button>
|
<button onClick={add}>+</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="counter-message">{children}</div>
|
<div className="counter-message">{children}</div>
|
||||||
</>
|
</Context.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
14
examples/framework-react/src/components/Display.tsx
Normal file
14
examples/framework-react/src/components/Display.tsx
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { useContext } from 'react';
|
||||||
|
import { Context } from './Provider.tsx';
|
||||||
|
|
||||||
|
export default function Display({ children }) {
|
||||||
|
const { count } = useContext(Context);
|
||||||
|
console.log('Display', { count });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ul>
|
||||||
|
<li><output>Current count is: {count}</output></li>
|
||||||
|
<li>{children}</li>
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
3
examples/framework-react/src/components/Provider.tsx
Normal file
3
examples/framework-react/src/components/Provider.tsx
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import { createContext } from "react";
|
||||||
|
|
||||||
|
export const Context = createContext<{ count: number }>({ count: 0 });
|
|
@ -1,8 +1,9 @@
|
||||||
---
|
---
|
||||||
// Component Imports
|
// Component Imports
|
||||||
import Counter from '../components/Counter';
|
import Counter from '../components/Counter';
|
||||||
|
import Display from '../components/Display';
|
||||||
const someProps = {
|
const someProps = {
|
||||||
count: 0,
|
count: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Full Astro Component Syntax:
|
// Full Astro Component Syntax:
|
||||||
|
@ -10,27 +11,34 @@ const someProps = {
|
||||||
---
|
---
|
||||||
|
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta name="generator" content={Astro.generator} />
|
<meta name="generator" content={Astro.generator} />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<style>
|
<style>
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
font-family: system-ui;
|
font-family: system-ui;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main>
|
<main>
|
||||||
<Counter {...someProps} client:visible>
|
<Counter {...someProps} client:visible>
|
||||||
<h1>Hello, React!</h1>
|
<div>
|
||||||
</Counter>
|
<h1>Hello, React!</h1>
|
||||||
</main>
|
<Display client:idle>
|
||||||
</body>
|
<Display client:idle>
|
||||||
|
<Display client:idle />
|
||||||
|
</Display>
|
||||||
|
</Display>
|
||||||
|
</div>
|
||||||
|
</Counter>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { createElement, startTransition } from 'react';
|
import { createElement, startTransition } from 'react';
|
||||||
import { createRoot, hydrateRoot } from 'react-dom/client';
|
import { createRoot, hydrateRoot } from 'react-dom/client';
|
||||||
import StaticHtml from './static-html.js';
|
import StaticHtml from './static-html.js';
|
||||||
|
import { Portal } from './wrapper.js';
|
||||||
|
import { Fragment } from 'react';
|
||||||
|
|
||||||
function isAlreadyHydrated(element) {
|
function isAlreadyHydrated(element) {
|
||||||
for (const key in element) {
|
for (const key in element) {
|
||||||
|
@ -10,31 +12,58 @@ function isAlreadyHydrated(element) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (element) =>
|
const app = document.createElement('astro-app');
|
||||||
(Component, props, { default: children, ...slotted }, { client }) => {
|
const root = createRoot(app)
|
||||||
|
const instances = new Map();
|
||||||
|
const instanceChildren = new Map();
|
||||||
|
const instanceParents = new Map();
|
||||||
|
|
||||||
|
function buildTree() {
|
||||||
|
const vnodes = [];
|
||||||
|
function addNode(node) {
|
||||||
|
const parent = instanceParents.get(node);
|
||||||
|
const children = instanceChildren.get(node);
|
||||||
|
const self = node({ children: children.map(child => addNode(child) )});
|
||||||
|
if (!parent) {
|
||||||
|
vnodes.push(self);
|
||||||
|
} else {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const i of instances.values()) {
|
||||||
|
addNode(i);
|
||||||
|
}
|
||||||
|
return vnodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default (element) => (Component, props, { default: children, ...slotted }, meta) => {
|
||||||
if (!element.hasAttribute('ssr')) return;
|
if (!element.hasAttribute('ssr')) return;
|
||||||
|
if (element.parentElement.closest('astro-island[ssr]')) return;
|
||||||
|
|
||||||
|
const parentElement = element.parentElement.closest('astro-island');
|
||||||
|
let parentInstance = null;
|
||||||
|
if (parentElement) parentInstance = instances.get(parentElement);
|
||||||
|
|
||||||
const renderOptions = {
|
const renderOptions = {
|
||||||
identifierPrefix: element.getAttribute('prefix'),
|
identifierPrefix: element.getAttribute('prefix'),
|
||||||
};
|
};
|
||||||
for (const [key, value] of Object.entries(slotted)) {
|
for (const [key, value] of Object.entries(slotted)) {
|
||||||
props[key] = createElement(StaticHtml, { value, name: key });
|
props[key] = createElement(StaticHtml, { value, name: key });
|
||||||
}
|
}
|
||||||
const componentEl = createElement(
|
const instance = ({ children: _children = [] }) => createElement(Portal, { host: element }, createElement(
|
||||||
Component,
|
Component,
|
||||||
props,
|
props,
|
||||||
children != null ? createElement(StaticHtml, { value: children }) : children
|
children != null ? createElement(StaticHtml, { value: children }) : children,
|
||||||
);
|
..._children
|
||||||
const rootKey = isAlreadyHydrated(element);
|
));
|
||||||
// HACK: delete internal react marker for nested components to suppress aggressive warnings
|
|
||||||
if (rootKey) {
|
instances.set(element, instance);
|
||||||
delete element[rootKey];
|
instanceChildren.set(instance, [])
|
||||||
|
if (parentInstance) {
|
||||||
|
instanceChildren.set(parentInstance, [...instanceChildren.get(parentInstance), instance])
|
||||||
|
instanceParents.set(instance, parentInstance)
|
||||||
}
|
}
|
||||||
if (client === 'only') {
|
const tree = buildTree();
|
||||||
return startTransition(() => {
|
element.replaceChildren();
|
||||||
createRoot(element).render(componentEl);
|
root.render(createElement(Fragment, {}, ...tree))
|
||||||
});
|
}
|
||||||
}
|
|
||||||
return startTransition(() => {
|
|
||||||
hydrateRoot(element, componentEl, renderOptions);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
"./server-v17.js": "./server-v17.js",
|
"./server-v17.js": "./server-v17.js",
|
||||||
"./package.json": "./package.json",
|
"./package.json": "./package.json",
|
||||||
"./jsx-runtime": "./jsx-runtime.js",
|
"./jsx-runtime": "./jsx-runtime.js",
|
||||||
|
"./wrapper.js": "./wrapper.js",
|
||||||
"./vnode-children.js": "./vnode-children.js"
|
"./vnode-children.js": "./vnode-children.js"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
|
@ -38,6 +39,7 @@
|
||||||
"server.js",
|
"server.js",
|
||||||
"server-v17.js",
|
"server-v17.js",
|
||||||
"static-html.js",
|
"static-html.js",
|
||||||
|
"wrapper.js",
|
||||||
"vnode-children.js"
|
"vnode-children.js"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -48,6 +50,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/core": "^7.22.5",
|
"@babel/core": "^7.22.5",
|
||||||
"@babel/plugin-transform-react-jsx": "^7.22.5",
|
"@babel/plugin-transform-react-jsx": "^7.22.5",
|
||||||
|
"its-fine": "^1.1.1",
|
||||||
"ultrahtml": "^1.2.0"
|
"ultrahtml": "^1.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -55,10 +58,10 @@
|
||||||
"@types/react-dom": "^17.0.20",
|
"@types/react-dom": "^17.0.20",
|
||||||
"astro": "workspace:*",
|
"astro": "workspace:*",
|
||||||
"astro-scripts": "workspace:*",
|
"astro-scripts": "workspace:*",
|
||||||
"react": "^18.1.0",
|
|
||||||
"react-dom": "^18.1.0",
|
|
||||||
"chai": "^4.3.7",
|
"chai": "^4.3.7",
|
||||||
"cheerio": "1.0.0-rc.12",
|
"cheerio": "1.0.0-rc.12",
|
||||||
|
"react": "^18.1.0",
|
||||||
|
"react-dom": "^18.1.0",
|
||||||
"vite": "^4.4.6"
|
"vite": "^4.4.6"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|
5
packages/integrations/react/wrapper.js
Normal file
5
packages/integrations/react/wrapper.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
|
export function Portal({ children, host }) {
|
||||||
|
return createPortal([children], host);
|
||||||
|
}
|
|
@ -4772,6 +4772,9 @@ importers:
|
||||||
'@babel/plugin-transform-react-jsx':
|
'@babel/plugin-transform-react-jsx':
|
||||||
specifier: ^7.22.5
|
specifier: ^7.22.5
|
||||||
version: 7.22.5(@babel/core@7.22.5)
|
version: 7.22.5(@babel/core@7.22.5)
|
||||||
|
its-fine:
|
||||||
|
specifier: ^1.1.1
|
||||||
|
version: 1.1.1(react@18.2.0)
|
||||||
ultrahtml:
|
ultrahtml:
|
||||||
specifier: ^1.2.0
|
specifier: ^1.2.0
|
||||||
version: 1.2.0
|
version: 1.2.0
|
||||||
|
@ -9117,6 +9120,12 @@ packages:
|
||||||
'@types/react': 18.2.13
|
'@types/react': 18.2.13
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@types/react-reconciler@0.28.2:
|
||||||
|
resolution: {integrity: sha512-8tu6lHzEgYPlfDf/J6GOQdIc+gs+S2yAqlby3zTsB3SP2svlqTYe5fwZNtZyfactP74ShooP2vvi1BOp9ZemWw==}
|
||||||
|
dependencies:
|
||||||
|
'@types/react': 18.2.13
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@types/react@17.0.62:
|
/@types/react@17.0.62:
|
||||||
resolution: {integrity: sha512-eANCyz9DG8p/Vdhr0ZKST8JV12PhH2ACCDYlFw6DIO+D+ca+uP4jtEDEpVqXZrh/uZdXQGwk7whJa3ah5DtyLw==}
|
resolution: {integrity: sha512-eANCyz9DG8p/Vdhr0ZKST8JV12PhH2ACCDYlFw6DIO+D+ca+uP4jtEDEpVqXZrh/uZdXQGwk7whJa3ah5DtyLw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -13105,6 +13114,18 @@ packages:
|
||||||
/isexe@2.0.0:
|
/isexe@2.0.0:
|
||||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||||
|
|
||||||
|
/its-fine@1.1.1(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-v1Ia1xl20KbuSGlwoaGsW0oxsw8Be+TrXweidxD9oT/1lAh6O3K3/GIM95Tt6WCiv6W+h2M7RB1TwdoAjQyyKw==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=18.0'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
react:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@types/react-reconciler': 0.28.2
|
||||||
|
react: 18.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/jake@10.8.6:
|
/jake@10.8.6:
|
||||||
resolution: {integrity: sha512-G43Ub9IYEFfu72sua6rzooi8V8Gz2lkfk48rW20vEWCGizeaEPlKB1Kh8JIA84yQbiAEfqlPmSpGgCKKxH3rDA==}
|
resolution: {integrity: sha512-G43Ub9IYEFfu72sua6rzooi8V8Gz2lkfk48rW20vEWCGizeaEPlKB1Kh8JIA84yQbiAEfqlPmSpGgCKKxH3rDA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
@ -18566,25 +18587,21 @@ packages:
|
||||||
file:packages/astro/test/fixtures/css-assets/packages/font-awesome:
|
file:packages/astro/test/fixtures/css-assets/packages/font-awesome:
|
||||||
resolution: {directory: packages/astro/test/fixtures/css-assets/packages/font-awesome, type: directory}
|
resolution: {directory: packages/astro/test/fixtures/css-assets/packages/font-awesome, type: directory}
|
||||||
name: '@test/astro-font-awesome-package'
|
name: '@test/astro-font-awesome-package'
|
||||||
version: 0.0.1
|
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:packages/astro/test/fixtures/multiple-renderers/renderers/one:
|
file:packages/astro/test/fixtures/multiple-renderers/renderers/one:
|
||||||
resolution: {directory: packages/astro/test/fixtures/multiple-renderers/renderers/one, type: directory}
|
resolution: {directory: packages/astro/test/fixtures/multiple-renderers/renderers/one, type: directory}
|
||||||
name: '@test/astro-renderer-one'
|
name: '@test/astro-renderer-one'
|
||||||
version: 1.0.0
|
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:packages/astro/test/fixtures/multiple-renderers/renderers/two:
|
file:packages/astro/test/fixtures/multiple-renderers/renderers/two:
|
||||||
resolution: {directory: packages/astro/test/fixtures/multiple-renderers/renderers/two, type: directory}
|
resolution: {directory: packages/astro/test/fixtures/multiple-renderers/renderers/two, type: directory}
|
||||||
name: '@test/astro-renderer-two'
|
name: '@test/astro-renderer-two'
|
||||||
version: 1.0.0
|
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:packages/astro/test/fixtures/solid-component/deps/solid-jsx-component:
|
file:packages/astro/test/fixtures/solid-component/deps/solid-jsx-component:
|
||||||
resolution: {directory: packages/astro/test/fixtures/solid-component/deps/solid-jsx-component, type: directory}
|
resolution: {directory: packages/astro/test/fixtures/solid-component/deps/solid-jsx-component, type: directory}
|
||||||
name: '@test/solid-jsx-component'
|
name: '@test/solid-jsx-component'
|
||||||
version: 0.0.0
|
|
||||||
dependencies:
|
dependencies:
|
||||||
solid-js: 1.7.6
|
solid-js: 1.7.6
|
||||||
dev: false
|
dev: false
|
||||||
|
|
Loading…
Reference in a new issue