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
TypeScript
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>;