UNPKG

atomico

Version:

Atomico is a small library for the creation of interfaces based on web-components, only using functions and hooks.

197 lines (179 loc) 4.9 kB
import { Atomico, DOMProps } from "./dom.js"; import { ConstructorType, FillObject, NoTypeFor, SchemaInfer, SchemaProps, Type, TypeCustom, TypeForInstance, TypeForJsx } from "./schema.js"; import { Sheets } from "./css.js"; /** * Infer the types from `component.props`. * ```tsx * function component({value}: Props<typeof component.props >){ * return <host/> * } * * component.props = {value:Number} * ``` */ export type GetProps<P, TypeFor = NoTypeFor> = P extends { readonly "##props"?: infer P; } ? P : P extends { props: SchemaProps } ? GetProps<P["props"], TypeFor> : { [K in GetKeysWithConfigValue<P>]: GetPropType<P[K], TypeFor>; } & { [K in GetKeysWithoutConfigValue<P>]?: GetPropType<P[K], TypeFor>; }; type GetKeysWithConfigValue<P> = { [I in keyof P]-?: P[I] extends { value: any; } ? I : never; }[keyof P]; type GetKeysWithoutConfigValue<P> = { [I in keyof P]-?: P[I] extends { value: any; } ? never : I; }[keyof P]; type GetPropType<Value, TypeFor = NoTypeFor> = Value extends { type: infer T; value: infer V; } ? T extends TypeCustom<any> ? ConstructorType<T, TypeFor> : FunctionConstructor extends T ? V : V extends () => infer T ? T : V : Value extends { type: infer T } ? ConstructorType<T, TypeFor> : Type<any> extends Value // Sometimes TS saturates, this verification limits the effort of TS to infer ? Value extends Type<infer R> ? R : ConstructorType<Value, TypeFor> : ConstructorType<Value, TypeFor>; /** * metaProps allow to hide the props assigned by Component<props> */ interface MetaProps<Props> { readonly "##props"?: Props; } /** * The MetaComponent type allows to identify as * validate types generated by Component<props> */ export interface MetaComponent { (props: any): any; props: MetaProps<any>; styles?: Sheets; render?: (props: any) => any; } /** * Infers the props from the component's props object, example: * ### Syntax * ```tsx * const myProps = { message: String } * Props<typeof MyProps>; * // {message: string} * ``` * ### Usage * You can use the `Prop` type on components, objects or constructors, example: * ```tsx * function component({message}: Props<typeof component>){ * return <host></host> * } * * component.props = {message: String} * ``` * * ### Advanced use * * It also allows to replace types of those already inferred, example: * ```tsx * Props<typeof MyProps, {message: "hello"|"bye bye"}>; * // {message?: "hello"|"bye bye"} * * ``` */ export type Props<P = null, TypeFor = NoTypeFor> = P extends null ? SchemaProps : GetProps<P, TypeFor>; export type Component<Props = null, Meta = any> = Props extends null ? { (props: FillObject): Host<Meta>; props?: SchemaProps; styles?: Sheets; } : { (props: DOMProps<Props>): Host<Meta>; props: SchemaInfer<Props> & MetaProps< Meta extends null ? Props : Props & SyntheticMetaProps<Meta> >; styles?: Sheets; }; export type CreateElement<C, Base, CheckMeta = true> = CheckMeta extends true ? C extends (props: any) => Host<infer Meta> ? CreateElement<C & { props: SyntheticProps<Meta> }, Base, false> : CreateElement<C, Base, false> : C extends { props: infer P } ? Atomico<Props<P, TypeForJsx>, Props<P, TypeForInstance>, Base> : Atomico<{}, {}, Base>; export type SyntheticProps<Props> = { [Prop in keyof Props]: Prop extends `on${string}` ? { type: Function; value: (event: Props[Prop]) => any; } : { type: Function; value: Props[Prop]; }; }; export type SyntheticMetaProps<Meta> = { [Prop in keyof Meta]?: Prop extends `on${string}` ? (event: Meta[Prop]) => any : Meta[Prop]; }; export type Host<Meta> = {}; export type ComponentOptions = { props?: SchemaProps; styles?: Sheets; base?: CustomElementConstructor; }; export function c< Component extends (props: Props<Options["props"]>) => Host<any>, Options extends ComponentOptions >( fn: Component, options: Options ): CreateElement< Component & { props: Options["props"]; sheets: Options["styles"]; }, Options extends { base: infer BaseElement; } ? BaseElement : typeof HTMLElement >; export function c< FnComponent extends Component | MetaComponent, BaseElement extends typeof HTMLElement >( component: FnComponent, baseElement?: BaseElement ): CreateElement<FnComponent, BaseElement>;