hybrids
Version:
A JavaScript framework for creating fully-featured web applications, components libraries, and single web components with unique declarative and functional architecture
465 lines (387 loc) • 13.2 kB
TypeScript
export type Property<E, V> =
| (V extends string | number | boolean | null | undefined ? V : never)
| ((host: E & HTMLElement, value?: any) => V)
| Descriptor<E, V>;
export interface Descriptor<E, V> {
value: V | ((host: E & HTMLElement, value?: any) => V);
connect?(
host: E & HTMLElement & { __property_key__: V },
key: "__property_key__",
invalidate: () => void,
): Function | void;
observe?(host: E & HTMLElement, value: V, lastValue: V): void;
reflect?: boolean | ((value: V) => string);
}
export interface UpdateFunction<E> {
(host: E & HTMLElement, target?: ShadowRoot | Text | E): void;
}
export interface RenderFunction<E> {
(host: E & HTMLElement): UpdateFunction<E>;
}
export interface RenderDescriptor<E> extends Descriptor<E, RenderFunction<E>> {
value: RenderFunction<E>;
reflect?: never;
shadow?: boolean | ShadowRootInit;
}
export type ComponentBase = {
tag: string;
__router__connect__?: ViewOptions;
};
export type Component<E> = ComponentBase & {
[property in Extract<
keyof Omit<E, keyof HTMLElement>,
string
>]: property extends "render"
? RenderFunction<E> | RenderDescriptor<E>
: Property<E, E[property]>;
} & {
render?: RenderFunction<E> | RenderDescriptor<E>;
};
export interface HybridElement<E> {
new (): E & HTMLElement;
prototype: E & HTMLElement;
}
/* Define */
export function define<E>(component: Component<E>): typeof component;
export namespace define {
function compile<E>(component: Component<E>): HybridElement<E>;
function from(
components: { [path: string]: Component<any> },
options?: { prefix?: string; root?: string | string[] },
): void;
}
/* Mount */
export function mount<E>(
target: HTMLElement,
component: Component<E>,
): () => void;
/* Factories */
export function parent<E, V>(
componentOrFn: Component<V> | ((component: Component<E>) => boolean),
): Descriptor<E, V>;
export function children<E, V>(
componentOrFn: Component<V> | ((component: Component<E>) => boolean),
options?: { deep?: boolean; nested?: boolean },
): Descriptor<E, V[]>;
/* Store */
export type ModelInstance = { id?: ModelIdentifier } & object &
NonArrayObject &
NonModelDefinition;
export type EnumerableInstance = { id: ModelIdentifier } & ModelInstance;
export type SingletonInstance = { id?: never } & ModelInstance;
export type Unarray<T> = T extends Array<infer U> ? U : T;
export type NonConstructor = { readonly prototype?: never };
export type NonArrayObject = { [Symbol.iterator]?: never } & object;
export type NonModelDefinition = { __store__connect__?: never } & object;
export type Model<M extends ModelInstance> = NonArrayObject & {
[property in keyof Omit<M, "id">]-?: NonNullable<
M[property]
> extends Array<any>
?
| NestedArrayModel<NonNullable<M[property]>>
| (NonConstructor &
((
model: M,
) => undefined extends M[property]
? undefined | NestedArrayModel<M[property]>
: NestedArrayModel<M[property]>))
: NonNullable<M[property]> extends string | String
? string | (NonConstructor & ((model: M) => M[property]))
: NonNullable<M[property]> extends number | Number
? number | (NonConstructor & ((model: M) => M[property]))
: NonNullable<M[property]> extends boolean | Boolean
? boolean | (NonConstructor & ((model: M) => M[property]))
: NonNullable<M[property]> extends ModelInstance
?
| Model<NonNullable<M[property]>>
| (NonConstructor &
((
model: M,
) => undefined extends M[property]
? undefined | Model<NonNullable<M[property]>>
: Model<NonNullable<M[property]>>))
: NonNullable<M[property]> extends NonArrayObject
?
| NonNullable<M[property]>
| (NonConstructor & ((model: M) => M[property]))
: never;
} & (M extends EnumerableInstance
? {
id: true;
}
: {}) & {
__store__connect__?: Storage<M> | Storage<M>["get"];
};
export type NestedArrayModel<T> =
NonNullable<Unarray<T>> extends string | String
? T | string[] | [String | StringConstructor]
: NonNullable<Unarray<T>> extends number | Number
? T | number[] | [Number | NumberConstructor]
: NonNullable<Unarray<T>> extends boolean | Boolean
? T | boolean[] | [Boolean | BooleanConstructor]
: NonNullable<Unarray<T>> extends EnumerableInstance
?
| [Model<NonNullable<Unarray<T>>>]
| [Model<NonNullable<Unarray<T>>>, { loose?: boolean }]
: NonNullable<Unarray<T>> extends NonArrayObject
? T
: never;
export type ModelIdentifier =
| string
| Record<string, string | boolean | number | null>
| undefined;
export type ModelValues<M extends ModelInstance> = {
[property in keyof M]?: NonNullable<M[property]> extends Array<any>
? Array<ModelValues<Unarray<NonNullable<M[property]>>>>
: NonNullable<M[property]> extends ModelInstance
? ModelValues<NonNullable<M[property]>>
: M[property];
};
export type StorageValues<M extends ModelInstance> = {
[property in keyof M]?: NonNullable<M[property]> extends EnumerableInstance
? NonNullable<M[property]> | M["id"]
: NonNullable<M[property]> extends EnumerableInstance[]
? (NonNullable<Unarray<M[property]>> | M["id"])[]
: M[property];
};
export type StorageResult<M extends ModelInstance> =
| StorageValues<M>
| null
| undefined;
export type Storage<M extends ModelInstance> = {
get?: (id: ModelIdentifier) => StorageResult<M> | Promise<StorageResult<M>>;
set?: (
id: ModelIdentifier,
values: M | null,
keys: [keyof M],
) => StorageResult<M> | Promise<StorageResult<M>>;
list?: (
id: ModelIdentifier,
) => Array<StorageResult<M>> | Promise<Array<StorageResult<M>>>;
observe?: (id: ModelIdentifier, model: M | null, lastModel: M | null) => void;
cache?: boolean | number;
offline?: boolean | number;
loose?: boolean;
};
// Enumerable - This overload must be the first one, then its signature and documentation will be displayed in intelephence by default.
export function store<E, M extends EnumerableInstance>(
model: Model<M>,
options?: { draft?: false; id?: keyof E | ((host: E) => ModelIdentifier) },
): Descriptor<E, M | undefined>;
// Enumerable Draft
export function store<E, M extends EnumerableInstance>(
model: Model<M>,
options: { draft: true; id?: keyof E | ((host: E) => ModelIdentifier) },
): Descriptor<E, M>;
// Enumerable Listing
export function store<E, M extends EnumerableInstance>(
model: [Model<M>],
options?: {
draft?: false;
id?: keyof E | ((host: E) => ModelIdentifier);
loose?: boolean;
},
): Descriptor<E, M[]>;
// Singleton
export function store<E, M extends SingletonInstance>(
model: M extends Array<any> ? never : Model<M>,
options?: { draft?: false; id?: keyof E | ((host: E) => ModelIdentifier) },
): Descriptor<E, M>;
// Singleton Draft
export function store<E, M extends SingletonInstance>(
model: M extends Array<any> ? never : Model<M>,
options: { draft: true; id?: keyof E | ((host: E) => ModelIdentifier) },
): Descriptor<E, M>;
export namespace store {
const connect = "__store__connect__";
function get<M extends ModelInstance>(
Model: Model<M>,
id?: ModelIdentifier,
): M;
function get<M extends ModelInstance>(
Model: [Model<M>],
id?: ModelIdentifier,
): M[];
function set<M extends ModelInstance>(
model: Model<M> | M,
values: ModelValues<M> | null,
): Promise<M>;
function sync<M extends ModelInstance>(
model: Model<M> | M,
values: ModelValues<M> | null,
): M;
function clear<M extends ModelInstance>(
model: Model<M> | [Model<M>] | M,
clearValue?: boolean,
): void;
function pending<M extends ModelInstance>(model: M): false | Promise<M>;
function pending<M extends ModelInstance>(
...models: Array<M>
): false | Promise<typeof models>;
function error<M extends ModelInstance>(
model: M,
propertyName?: keyof M | null,
): false | Error | any;
function ready<M extends ModelInstance>(model: M): boolean;
function ready<M extends ModelInstance>(...models: Array<M>): boolean;
function submit<M extends ModelInstance>(
draft: M,
values?: ModelValues<M>,
): Promise<M>;
function resolve<M extends ModelInstance>(model: M): Promise<M>;
function resolve<M extends ModelInstance>(
model: Model<M>,
id?: ModelIdentifier,
): Promise<M>;
function resolve<M extends ModelInstance>(
model: [Model<M>],
id?: ModelIdentifier,
): Promise<M[]>;
function ref<T>(fn: () => T): () => T;
function record<V>(value: V): Record<string, V>;
function record<V extends () => {}>(value: V): Record<string, ReturnType<V>>;
interface ValidateFunction<M extends ModelInstance, T> {
(value: T, key: string, model: M): string | boolean | void;
}
function value<M extends ModelInstance>(
defaultValue: string,
validate?: ValidateFunction<M, string> | RegExp,
errorMessage?: string,
): string;
function value<M extends ModelInstance>(
defaultValue: number,
validate?: ValidateFunction<M, number> | RegExp,
errorMessage?: string,
): number;
function value<M extends ModelInstance>(
defaultValue: boolean,
validate?: ValidateFunction<M, number> | RegExp,
errorMessage?: string,
): boolean;
function observe<M extends ModelInstance>(
model: Model<M>,
callback: (
id: ModelIdentifier,
model: M | null,
lastModel: M | null,
) => void,
): () => void;
}
/* Router */
export interface ViewOptions {
url?: string;
multiple?: boolean;
dialog?: boolean;
replace?: boolean;
stack?: ComponentBase[] | (() => ComponentBase[]);
guard?: () => boolean;
}
export function router<E>(
views:
| ComponentBase
| ComponentBase[]
| (() => ComponentBase | ComponentBase[]),
options?: {
url?: string;
params?: Array<keyof E>;
transition?: boolean;
},
): Descriptor<E, HTMLElement[]>;
export namespace router {
const connect = "__router__connect__";
function debug(value?: boolean): void;
type UrlParams<E> = {
[property in keyof E]?: E[property];
};
type UrlOptions = {
scrollToTop?: boolean;
};
function url<E>(
view: ComponentBase,
params?: UrlParams<E> & UrlOptions,
): URL | "";
function backUrl(options?: { nested?: boolean } & UrlOptions): URL | "";
function guardUrl(params?: UrlParams<any> & UrlOptions): URL | "";
function currentUrl<E>(params?: UrlParams<E> & UrlOptions): URL | "";
function active(
views: ComponentBase | ComponentBase[],
options?: { stack?: boolean },
): boolean;
function resolve<P>(event: Event, promise: Promise<P>): Promise<P>;
}
/* Localize */
export type Messages = {
[key: string]: {
message:
| string
| {
zero?: string;
one?: string;
two?: string;
few?: string;
many?: string;
other?: string;
};
description?: string;
};
};
export function localize(lang: string, messages: Messages): void;
export function localize(
translate: (
key: string,
context: string,
) => string | ((num: number) => string),
options?: {
format?: "chrome.i18n";
},
): void;
export namespace localize {
const languages: string[];
}
export function msg(parts: TemplateStringsArray, ...args: unknown[]): string;
export namespace msg {
function html<E>(
parts: TemplateStringsArray,
...args: unknown[]
): UpdateFunctionWithMethods<E>;
function svg<E>(
parts: TemplateStringsArray,
...args: unknown[]
): UpdateFunctionWithMethods<E>;
}
/* Utils */
export function dispatch(
host: EventTarget,
eventType: string,
options?: CustomEventInit,
): boolean;
export function debug(): void;
/* Template Engine */
export interface UpdateFunctionWithMethods<E> extends UpdateFunction<E> {
key: (id: any) => this;
style: (...styles: Array<string | CSSStyleSheet>) => this;
css: (parts: TemplateStringsArray, ...args: unknown[]) => this;
use: (fn: (template: UpdateFunction<E>) => UpdateFunction<E>) => this;
}
export interface EventHandler<E> {
(host: E & HTMLElement, event?: Event): any;
}
export function html<E>(
parts: TemplateStringsArray,
...args: unknown[]
): UpdateFunctionWithMethods<E>;
export namespace html {
function set<E>(property: keyof E, valueOrPath?: any): EventHandler<E>;
function set<E, M>(property: M, valueOrPath: string | null): EventHandler<E>;
function resolve<E>(
promise: Promise<any>,
placeholder?: UpdateFunction<E>,
delay?: number,
): UpdateFunction<E>;
function transition<E>(template: UpdateFunction<E>): UpdateFunction<E>;
function msg(parts: TemplateStringsArray, ...args: unknown[]): string;
}
export function svg<E>(
parts: TemplateStringsArray,
...args: unknown[]
): UpdateFunctionWithMethods<E>;