Skip to content

Instantly share code, notes, and snippets.

@miguelrk
Last active November 22, 2023 06:03
Show Gist options
  • Save miguelrk/c9f37e311a42addf04fa91a7b704af5e to your computer and use it in GitHub Desktop.
Save miguelrk/c9f37e311a42addf04fa91a7b704af5e to your computer and use it in GitHub Desktop.
A Deno Fresh plugin for Preact Signals `bind:value` inspired by https://gist.github.com/marvinhagemeister/daada86f82466dff81d48840c8d5f27b
import { setup } from "./shared.ts";
export default function hydrate() {
setup();
}
import { Plugin } from "$fresh/server.ts";
import { setup } from "./shared.ts";
export const bindSignal = (): Plugin<unknown> => {
setup();
const url = new URL("./main.ts", import.meta.url).href;
const main = `data:application/javascript,import hydrate from "${url}";
export default function(state) { hydrate(); }
`;
return {
name: "bindSignal",
entrypoints: { main },
async renderAsync(ctx) {
await ctx.renderAsync();
return {
scripts: [{ entrypoint: "main", state: [] }],
};
},
};
};
import { options, VNode } from "preact";
import { Signal } from "@preact/signals";
// Add `bind:value` to JSX types
declare global {
namespace preact.createElement.JSX {
interface HTMLAttributes {
"bind:value"?: Signal<string | string[] | number | undefined>;
}
}
}
export function setup() {
const oldVNodeHook = options.vnode;
options.vnode = (vnode: VNode<any>) => {
const { type, props } = vnode;
if (
typeof type === "string" &&
props?.["bind:value"] &&
isSignal(props["bind:value"])
) {
const signal = props["bind:value"];
props.value = signal;
let oldOnInput = props.onInput;
props.onInput = (event: any) => {
signal.value = event.target.value;
oldOnInput?.(event);
};
}
oldVNodeHook?.(vnode);
};
}
function isSignal(x: any): x is Signal {
return (
x !== null &&
typeof x === "object" &&
typeof x.peek === "function" &&
"value" in x
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment