aboutsummaryrefslogtreecommitdiff
path: root/packages/integrations/preact/src/client.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/integrations/preact/src/client.ts')
-rw-r--r--packages/integrations/preact/src/client.ts61
1 files changed, 61 insertions, 0 deletions
diff --git a/packages/integrations/preact/src/client.ts b/packages/integrations/preact/src/client.ts
new file mode 100644
index 000000000..32fdc9888
--- /dev/null
+++ b/packages/integrations/preact/src/client.ts
@@ -0,0 +1,61 @@
+import { h, hydrate, render } from 'preact';
+import StaticHtml from './static-html.js';
+import type { SignalLike } from './types.js';
+
+const sharedSignalMap = new Map<string, SignalLike>();
+
+export default (element: HTMLElement) =>
+ async (
+ Component: any,
+ props: Record<string, any>,
+ { default: children, ...slotted }: Record<string, any>,
+ { client }: Record<string, string>,
+ ) => {
+ if (!element.hasAttribute('ssr')) return;
+ for (const [key, value] of Object.entries(slotted)) {
+ props[key] = h(StaticHtml, { value, name: key });
+ }
+ let signalsRaw = element.dataset.preactSignals;
+ if (signalsRaw) {
+ const { signal } = await import('@preact/signals');
+ let signals: Record<string, string | [string, number][]> = JSON.parse(
+ element.dataset.preactSignals!,
+ );
+ for (const [propName, signalId] of Object.entries(signals)) {
+ if (Array.isArray(signalId)) {
+ signalId.forEach(([id, indexOrKeyInProps]) => {
+ const mapValue = props[propName][indexOrKeyInProps];
+ let valueOfSignal = mapValue;
+
+ // not an property key
+ if (typeof indexOrKeyInProps !== 'string') {
+ valueOfSignal = mapValue[0];
+ indexOrKeyInProps = mapValue[1];
+ }
+
+ if (!sharedSignalMap.has(id)) {
+ const signalValue = signal(valueOfSignal);
+ sharedSignalMap.set(id, signalValue);
+ }
+ props[propName][indexOrKeyInProps] = sharedSignalMap.get(id);
+ });
+ } else {
+ if (!sharedSignalMap.has(signalId)) {
+ const signalValue = signal(props[propName]);
+ sharedSignalMap.set(signalId, signalValue);
+ }
+ props[propName] = sharedSignalMap.get(signalId);
+ }
+ }
+ }
+
+ const bootstrap = client !== 'only' ? hydrate : render;
+
+ bootstrap(
+ h(Component, props, children != null ? h(StaticHtml, { value: children }) : children),
+ element,
+ );
+
+ // Preact has no "unmount" option, but you can use `render(null, element)`
+ element.addEventListener('astro:unmount', () => render(null, element), { once: true });
+ };