import { Cleanup, MaybeSignal, Signal, SignalLike } from "./scope.js"; import type { DomEventProps, DomProps } from "./dom.js"; import { EventNameToJsxProp, JsxPropNameToEventName } from "./utils.js"; import { useScope } from "./scope.js"; import { Context } from "./context.js"; import { Template } from "./template.js"; interface Tagged { _tag: T; } type OmitNever = Omit; type PartialRequire = Omit & Required>; /** @ignore */ export interface PropMeta extends PropOptions, Tagged<"p"> { _type?: [T]; _defaultOrContext: T; } export interface AttributeOptions { /** * The name of the attribute to observe. * * Defaults to the kebab-case version of the prop. */ name?: string; /** * A function to transform the attribute value to the prop value. */ transform: (value: string) => T; /** * Set to `true` to not observe the attribute for changes. * * @default false */ static?: boolean; } type PartialPartial = Omit & Partial>; export interface PropOptions { attribute?: ((value: string) => T) | (string extends T ? PartialPartial, "transform"> : AttributeOptions); } type Props = OmitNever<{ readonly [K in keyof M]: M[K] extends PropMeta ? Signal : never; }>; export type EventConstructor = new (name: string, arg: T) => E; /** @ignore */ export interface EventMeta extends Tagged<"e"> { _event: E; } type Events = OmitNever ? E : never : never; }, `on${Lowercase}`>>; type GeneralJsxProps = Partial | `on${string}` | `${Uppercase}${string}` | "accessKeyLabel" | "offsetHeight" | "offsetLeft" | "offsetParent" | "offsetTop" | "offsetWidth" | "attributes" | "classList" | "clientHeight" | "clientLeft" | "clientTop" | "clientWidth" | "localName" | "namespaceURI" | "ownerDocument" | "part" | "prefix" | "scrollHeight" | "scrollWidth" | "shadowRoot" | "tagName" | "baseURI" | "childNodes" | "firstChild" | "isConnected" | "lastChild" | "nextSibling" | "nodeName" | "nodeType" | "parentElement" | "parentNode" | "previousSibling" | "nextElementSibling" | "previousElementSibling" | "childElementCount" | "firstElementChild" | "lastElementChild" | "assignedSlot" | "attributeStyleMap" | "isContentEditable" | "dataset" ? never : T[K] extends Function ? never : MaybeSignal; }>> & DomProps & DomEventProps & Record; export type JsxProps = typeof jsxPropsSym extends keyof T ? NonNullable : any; type ComponentJsxProps = Partial]: Props[K] extends Signal ? MaybeSignal : never; }> & { [K in keyof Events]: (evt: InstanceType[K]>) => void; }> & GeneralJsxProps; type EventEmitters = OmitNever]: Events[K] extends EventConstructor ? undefined extends E ? (arg?: E) => boolean : (arg: E) => boolean : never; }, `on${Lowercase}`>>; /** * Defines a property in your component metadata that can be set from outside * of the component. * * Make sure to avoid conflicts with native `HTMLElement` properties. * * You can get properties by accessing the {@link Signal} in `this.props`. * It's also possible to set the properties directly on the component instance. * * It's also possible to define an attribute for the property by setting the * `attribute` option. By default, the attribute name is the kebab-case version * of the property name. The attribute will be observed and the signal updated * on changes. You can also provide a custom name and a transform function to * convert the attribute value to the property value. * * @example * ```tsx * class App extends Component("x-app", { * greetingMessage: prop("Hello, world!", { * attribute: { * name: "greeting", * } * }), * }) { * render() { * return

{this.props.greetingMessage}

; * } * } * * defineComponents(App); * * const app = new App(); * app.greetingMessage = "Hello, universe!"; * ``` */ export declare const prop: ((context: Context, opts?: PropOptions) => PropMeta) & ((defaultValue: T, opts?: PropOptions) => PropMeta) & ((defaultValue?: T, opts?: PropOptions) => PropMeta); type _CustomEventContructor = undefined extends T ? typeof CustomEvent : EventConstructor, "detail">, CustomEvent>; /** * Defines an event in your component metadata that can be dispatched by * the component. * * Make sure your event name starts with `on` and to avoid conflicts with * native `HTMLElement` events. The event name will be converted to kebab-case. * * You can dispatch events either using `HTMLElement.dispatchEvent` or by * calling the event emitter function in `this.events` inside the `render` * function of a component. * * @example * ```tsx * class App extends Component("x-app", { * onSomethingHappen: event(), * // Event name will be `something-happen` * }) { * render() { * // … * this.events.onSomethingHappen({ detail: "Something happened! "}); * } * } * * const app = new App(); * app.addEventListener("something-happen", (evt) => { * // `evt` is `CustomEvent` * console.log(evt.detail); * }); * ``` * * You can also provide a custom event constructor: * * @example * ```tsx * class App extends Component("x-app", { * onSomethingClick: event(() => MouseEvent), * }) { * render() { * return ( * * ); * } * } * ``` */ export declare const event: (() => EventMeta<_CustomEventContructor>) & (() => EventMeta<_CustomEventContructor>) & ((eventConstructor: E) => EventMeta); export type Metadata = { [K in keyof _ComponentInner | "props" | "events"]?: never; } & { [K in keyof DomProps]?: never; } & { [K in keyof HTMLElement]?: never; } & { [name: string]: PropMeta | EventMeta; }; export declare const componentSym: unique symbol; export declare const jsxPropsSym: unique symbol; export declare abstract class _ComponentInner { protected props: Props; protected events: EventEmitters; readonly [jsxPropsSym]?: ComponentJsxProps; readonly [componentSym]: { _scope?: ReturnType; _destroy?: (() => void) | void; }; connectedCallback(): void; disconnectedCallback(): void; attributeChangedCallback(name: string, oldValue: string | null, value: string | null): void; addEventListener & string>>(type: K, listener: (event: InstanceType[Extract, keyof Events>]>) => void, options?: boolean | AddEventListenerOptions): void; removeEventListener & string>>(type: K, listener: (event: InstanceType[Extract, keyof Events>]>) => void, options?: boolean | EventListenerOptions): void; abstract render(): Template; } export type Component = { -readonly [K in keyof Props]: Props[K] extends Signal ? T | undefined : never; } & _ComponentInner & HTMLElement; export interface ComponentConstructor { /** @ignore */ readonly [componentSym]: { readonly _tagName: string | null; }; readonly observedAttributes: readonly string[]; new (): Component; } export interface ComponentOptions { /** * Shadow DOM options. Set to `false` to disable shadow DOM. * * @default { mode: "open" } */ shadow?: ShadowRootInit | false; } /** * Creates an effect which will rerun when any accessed signal changes. * * If used inside of a component and the component is not yet mounted, the * effect will run only after the component is mounted. Otherwise, the effect * will run immediately. * * @param fn The function to run; it may return a cleanup function. */ export declare const useMountEffect: (fn: () => Cleanup, deps?: SignalLike[]) => void; /** * Creates a new web component class. * * Specify props and events using the `metadata` parameter. * * @example * ```tsx * class MyComponent extends Component({ * myProp: prop("Hello, world!"), * onMyEvent: event(), * }) { * render() { * return ( * <> *

{this.props.myProp}

* * * ); * }, * } * * defineComponents(MyComponent); * ``` */ export declare const Component: ((tagName?: string) => ComponentConstructor<{}>) & ((tagName: string, metadata: M, opts?: ComponentOptions) => ComponentConstructor) & ((metadata: M, opts?: ComponentOptions) => ComponentConstructor); /** * Determines whether the given value is a component created by * extending {@link ComponentConstructor}. */ export declare const isComponent: (value: any) => value is ComponentConstructor | Component; /** * Represents a functional component. * * @example * ```tsx * const MyComponent: FunctionalComponent<{ * name: MaybeSignal; * }> = ({ name }) => { * return

Hello, {name}!

; * }; * ``` */ export interface FunctionalComponent { (props: P): Template; } /** * Defines a set of components with the given prefix. */ export declare const defineComponents: ((...components: ComponentConstructor[]) => void) & ((prefix: string, ...components: ComponentConstructor[]) => void); export {};