UNPKG

@oazmi/tsignal

Version:

a topological order respecting signals library inspired by SolidJS

145 lines (144 loc) 6.36 kB
/** a minimal implementation of JSX runtime element creation. <br> * to use in `esbuild`'s javascript build API, you will need to do one of the following options (or do both): * * 1) option 1 (preferred): <br> * for JSX to work with your IDE's LSP, and for esbuild to automatically discover the hyperscript functions, * you will need to include the following two comment lines at the top of your `.tsx` script: * ```tsx * /** \@jsx h *\/ * /** \@jsxFrag hf *\/ * ``` * * 2) option 2 (no LSP support): <br> * in the esbuild build options (`BuildOptions`), set `jsxFactory = "h"` and `jsxFragment = "hf"`. * ```ts * import { build, stop } from "https://deno.land/x/esbuild/mod.js" * build({ * entryPoints: ["./path/to/your/script.tsx"], * jsxFactory: "h", * jsxFragment: "Fragment", * // other build options * minify: true, * }) * stop() * ``` * * and now in your `.jsx` script, you should: * - import `createHyperScript` from this module * - create a reactive signal `Context` * - call `createHyperScript` with the signal context `ctx` as the argument * - the returned tuple will contain 3 elements: * - the first element should be named `h` (which is the name you declare as `\@jsx h` in **option 1** or `jsxFactory = "h"` in **option 2**) * - the second element should be named `hf` (which is the name you declare as `\@jsxFrag hf` in **option 1** or `jsxFragment = "hf"` in **option 2**) * - the third can be named anything * * @example * ```tsx * // the `\@jsx h` comment comes here, but I can't show multiline comments in this documentation. * // the `\@jsxFrag hf` comment comes here, but I can't show multiline comments in this documentation. * * import { createHyperScript } from "./path/to/tsignal/jsx-runtime/mod.ts" * import { Context } from "./path/to/tsignal/mod.ts" * * const ctx = new Context() * const [h, hf, namespaceStack] = createHyperScript(ctx) * * const my_elem = <div>Hello world</div> * const my_fragment_elems = <> * <span>World<span> * <span>Hello<span> * </> * const my_elem2 = <div>...my_fragment_elems</div> * document.body.appendChild(my_elem) * document.body.appendChild(my_elem2) * * // when creating svgs or xml, you will have to change the DOM namespace, so that the correct kinds of `Node`s are created. * namespaceStack.push("svg") * const my_svg = <svg viewBox="0 0 200 200"> * <g transform="translate(100, 50)"> * <text text-anchor="middle">SVG says Hi!</text> * <text y="25" text-anchor="middle">SVG stands for "SUGOI! Vector Graphics"</text> * </g> * </svg> * namespaceStack.pop() * ``` * * @module */ import { Context } from "../context.js"; import { Stringifyable } from "../deps.js"; import { Accessor } from "../typedefs.js"; type AttributeKey = string; interface Attributes { [key: AttributeKey]: Stringifyable | Accessor<Stringifyable>; } type IntrinsicHTMLElements = { [tagName in keyof HTMLElementTagNameMap]: Attributes; }; type IntrinsicSVGElements = { [tagName in keyof SVGElementTagNameMap]: Attributes; }; /** a minimal implementation of `JSX.IntrinsicElements` to get syntax highlighting in your `.jsx` and `.tsx` files. <br> * to use this, and banish all the red error lines under your jsx blocks, simply import this file. * * ### how to get it working: * * - create the file `global.d.ts` in the root of your source code. so for instance: `/src/global.d.ts` is one possibility. * - add the following lines to it: * ```ts * // file: `/src/global.d.ts` * import { JSX as JSXInternal } from "@jsr:@oazmi/tsignal/jsx-runtime" * type IntrinsicElements = JSXInternal.IntrinsicElements * export as namespace JSX * ``` * * - and now, with the global declaration done, your IDE should pick up on the `JSX.IntrinsicElements` definitions. * however, if it doesn't in certain files, add the following line to the top of that file, so that you can explicitly reference your `global.d.ts` file. * ```tsx * // file `/src/components/navigation_bar.tsx` * /// <reference path="../global.d.ts" /> * ``` * * @example * ```tsx * // file `/src/components/navigation_bar.tsx` * /// <reference path="../global.d.ts" /> * * const my_div = <div> * <span>Hello</span> * <span>World!!</span> * </div> * ``` */ export declare namespace JSX { /** {@inheritDoc JSX} */ type IntrinsicElements = IntrinsicHTMLElements & IntrinsicSVGElements; } /** a reactive text can either be a stringifyable signal `Accessor`, or a regular stringifyable value (which will get upgraded to an accessor). */ export type ReactiveText = Stringifyable | Accessor<Stringifyable>; /** the `props` of an `HTMLElement` are simply their attributes */ export type ElementAttrs = { [attribute_key: Attr["name"]]: ReactiveText; }; type SingleComponentGenerator<P = {}> = (props?: P) => Element; type FragmentComponentGenerator<P = {}> = (props?: P) => Element[]; /** a function that generates one `HTMLElement` or an array of them. */ export type ComponentGenerator<P = {}> = SingleComponentGenerator<P> | FragmentComponentGenerator<P>; type ElementChild = ReactiveText | Node; type ElementChildren = Array<ElementChild | ElementChild[]>; /** function signature of the fragment element generator (array of elements). */ export type HyperScript_CreateFragment = (props: object, ...elements: Node[]) => (typeof elements); /** function signature of the hyperscript DOM generator function. */ export interface HyperScript_CreateElement { (html_tag: string, attrs?: ElementAttrs | null, ...children: ElementChildren): HTMLElement; <P = {}>(component: SingleComponentGenerator<P>, props?: P | null, ...children: ElementChildren): Element; <P = {}>(fragment_component: FragmentComponentGenerator<P>, props?: P | null, ...siblings: ElementChildren): Element[]; <P = {}>(component: ComponentGenerator<P>, props?: P | null, ...children: ElementChildren): ReturnType<typeof component>; } /** create hyperscript functions to create elements and fragments during runtime after your JSX or TSX have been transformed. */ export declare const createHyperScript: (ctx: Context) => readonly [HyperScript_CreateElement, HyperScript_CreateFragment, { push: (...args: "svg"[]) => number; pop: () => "svg" | undefined; seek: () => "svg" | undefined; }]; export {};