@v4fire/client
Version:
V4Fire client core library
491 lines (424 loc) • 10.8 kB
text/typescript
/*!
* V4Fire Client Core
* https://github.com/V4Fire/Client
*
* Released under the MIT license
* https://github.com/V4Fire/Client/blob/master/LICENSE
*/
import type { RegExpOptions, ParseOptions, Key } from 'path-to-regexp';
import type { EventEmitter2 as EventEmitter } from 'eventemitter2';
/**
* Meta information of a route that can be declared statically as a literal
*/
export type StaticRouteMeta<M extends object = Dictionary> = M & {
/**
* Unique route name: can be used to direct transition
*
* @example
* ```js
* this.router.push('foo');
* ```
*/
name?: string;
/**
* @deprecated
* @see [[StaticRouteMeta.name]]
*/
page?: string;
/**
* Dependencies that are loaded with this route
*/
load?(): Promise<unknown>;
/**
* Path to the route.
* Usually, this parameter is used to tie a route with some URL.
* You can use some variable binding within the path.
* To organize such binding is used "path-to-regexp" library.
*
* The values to interpolate the path are taken from the "params" property of a route.
* This parameter can be provided by using "push" or "replace" methods of the router.
*/
path?: string;
/**
* Additional options to parse a path of the route
*/
pathOpts?: PathOptions;
/**
* If true, then the route can take "params" values from the "query" property
*/
paramsFromQuery?: boolean;
/**
* True, if this route can be used as default.
* The default route is used when the router can't automatically detect the current route,
* for example, you have routes for URL-s "/foo" and "/bar", but if somebody tries to enter different paths
* that weren't described, it will be redirected to the default route.
*
* There can be only one default route, but if you defined several routes with this flag,
* then it will be used the last defined.
*
* @default `false`
*/
default?: boolean;
/**
* @deprecated
* @see [[StaticRouteMeta.default]]
*/
index?: boolean;
/**
* If the route is an alias to another route, the parameter contains the route name we refer to.
* The alias preserves its original name of the route (but rest parameters are taken from a referrer route).
* If you want to organize some redirect logic, please see the "redirect" parameter.
*/
alias?: string;
/**
* If you need to automatically redirect to another route whenever you switch to the current,
* you can pass this parameter a name of the route to redirect.
*/
redirect?: string;
/**
* Marks the route as "external", i.e. transitions to this route will be produced by using location.href
*/
external?: boolean;
/**
* Default "query" parameters.
* If some parameter value is specified as a function, it will be invoked with the router instance as an argument.
*/
query?: Dictionary;
/**
* Default "params" parameters.
* If some parameter value is specified as a function, it will be invoked with the router instance as an argument.
*/
params?: Dictionary;
/**
* Default "meta" parameters.
* If some parameter value is specified as a function, it will be invoked with the router instance as an argument.
*/
meta?: Dictionary;
/**
* If false, the router does not automatically scroll a page to coordinates tied with the route.
* Mind, if you switch off this parameter, the scroll position of a page won't be restored
* on a back or forward tap too.
*
* @default `true`
*/
autoScroll?: boolean;
/**
* Scroll coordinates that tied with the route
*/
scroll?: {
x?: number;
y?: number;
};
};
/**
* Decorated path options
*/
export interface PathOptions extends RegExpOptions, ParseOptions {
/**
* Aliases for dynamic parameters in `path`.
* @see [[StaticRouteMeta.path]]Ы
*
* In the example below you can specify either `bar` itself as a parameter or any of its aliases.
* Note that aliases will be used only if the original parameter is not specified.
* The priority of aliases is determined "from left to right".
*
* @example
* ```typescript
* {
* path: '/foo/:bar',
* pathOpts: {
* aliases: {
* bar: ['_bar', 'Bar']
* }
* }
* }
*
* this.router.push('/foo/:bar', {params: {bar: 'bar'}}) // "/foo/bar"
* this.router.push('/foo/:bar', {params: {Bar: 'Bar'}}) // "/foo/Bar"
* this.router.push('/foo/:bar', {params: {bar: 'bar', Bar: 'Bar'}}) // "/foo/bar"
* this.router.push('/foo/:bar', {params: {Bar: 'Bar', _bar: '_bar'}}) // "/foo/_bar"
* ```
*/
aliases?: Dictionary<string[]>;
}
/**
* Static schema of application routes
*/
export type StaticRoutes<M extends object = Dictionary> = Dictionary<
string |
StaticRouteMeta<M>
>;
/**
* Meta information of a route
*/
export type RouteMeta<M extends object = Dictionary> = StaticRouteMeta<M> & {
/** @see [[StaticRouteMeta.name]] */
name: string;
/** @see [[StaticRouteMeta.default]] */
default: boolean;
};
/**
* Route object
*/
export interface Route<
PARAMS extends object = Dictionary,
QUERY extends object = Dictionary,
META extends object = Dictionary
> extends Dictionary {
/**
* URL of the route
*/
url?: string;
/**
* Route name
*/
name: string;
/**
* @deprecated
* @see [[Route.name]]
*/
page?: string;
/**
* If true, the route can be used as default
*/
default: boolean;
/**
* @deprecated
* @see [[Route.default]]
*/
index?: boolean;
/**
* Route parameters that can be passed to the route path
*/
params: PARAMS;
/**
* Route query parameters
*/
query: QUERY;
/**
* Route meta information
*/
meta: RouteMeta<META>;
}
export type TransitionParams = {[K in keyof Route]?: Route[K] extends Dictionary<any> ? Partial<Route[K]> : Route[K]};
export interface HistoryClearFilter {
(page: Route): unknown;
}
/**
* Router API
*/
export interface Router<
PARAMS extends object = Dictionary,
QUERY extends object = Dictionary,
META extends object = Dictionary
> extends EventEmitter {
/**
* Active route
*/
readonly route?: CanUndef<Route<PARAMS, QUERY, META>>;
/**
* @deprecated
* @see [[Router.route]]
*/
readonly page?: CanUndef<Route<PARAMS, QUERY, META>>;
/**
* History of routes
*/
readonly history: Route[];
/**
* Static schema of application routes
*/
readonly routes?: StaticRoutes<META>;
/**
* Returns an identifier of the route by a name or URL
* @param route
*/
id(route: string): string;
/**
* Pushes a new route to the history stack
*
* @param route - route name or URL
* @param params - route parameters
*/
push(route: string, params?: TransitionParams): Promise<void>;
/**
* Replaces the current route
*
* @param route - route name or URL
* @param params - route parameters
*/
replace(route: string, params?: TransitionParams): Promise<void>;
/**
* Switches to a route from the history, identified by its relative position to the current route
* (with the current route being relative index 0)
*
* @param pos
*/
go(pos: number): void;
/**
* Switches to the next route from the history
*/
forward(): void;
/**
* Switches to the previous route from the history
*/
back(): void;
/**
* Clears the routes history
* @param filter - filter predicate
*/
clear(filter?: HistoryClearFilter): Promise<void>;
/**
* Clears all temporary routes from the history.
* The temporary route is a route that has "tmp" flag within its own properties, like, "params", "query" or "meta".
*/
clearTmp(): Promise<void>;
}
/**
* Compiled not applied route
*/
export interface RouteBlueprint<META extends object = Dictionary> {
/**
* Route name
*/
name: string;
/**
* @deprecated
* @see [[RouteBlueprint.name]]
*/
page?: string;
/**
* @deprecated
* @see [[RouteBlueprint.meta.default]]
*/
index?: boolean;
/**
* Pattern of the route path
*/
pattern?: string | ((route: RouteAPI) => CanUndef<string>);
/**
* RegExp to parse the route path
*/
rgxp?: RegExp;
/**
* List of parameters that passed to the route path
*
* @example
* ```js
* {
* path: '/:foo/:bar',
* pathParams: [
* {modifier: '', name: 'foo', pattern: '[^\\/#\\?]+?', prefix: '/', suffix: '', aliases: []},
* {modifier: '', name: 'bat', pattern: '[^\\/#\\?]+?', prefix: '/', suffix: '', aliases: []}
* ]
* }
* ```
*/
pathParams: PathParam[];
/**
* Route meta information
*/
meta: RouteMeta<META>;
}
/**
* Decorated object after parsing the path
*/
export interface PathParam extends Key {
/**
* @see [[StaticRouteMeta.pathOpts.aliases]]
*/
aliases: string[];
}
export type RouteBlueprints = Dictionary<RouteBlueprint>;
/**
* Compiled and applied route
*/
export type AppliedRoute<
PARAMS extends object = Dictionary,
QUERY extends object = Dictionary,
META extends object = Dictionary
> = Route<PARAMS, QUERY, META> & RouteBlueprint<META>;
/**
* Public API to work with a route
*/
export interface RouteAPI<
PARAMS extends object = Dictionary,
QUERY extends object = Dictionary,
META extends object = Dictionary
> extends AppliedRoute<PARAMS, QUERY, META> {
/**
* Applies a dictionary with parameters to the route path and returns the resolved path
* @param params
*/
resolvePath(params?: Dictionary): string;
/**
* @deprecated
* @see [[Route.toPath]]
*/
toPath?(params?: Dictionary): string;
}
export type AnyRoute =
AppliedRoute |
Route |
RouteAPI;
/**
* Additional options to use on getting a route object
*/
export interface AdditionalGetRouteOpts {
basePath?: string;
defaultRoute?: RouteBlueprint;
}
/**
* Additional options to compile routes
*/
export interface CompileRoutesOpts {
/**
* Base route path: all route paths are concatenated with this path
*/
basePath?: string;
}
/**
* Parameters of a route
*/
export interface RouteParams extends TransitionOptions {
/**
* Route name
*/
name: string;
/**
* @deprecated
* @see [[RouteParams.name]]
*/
page?: string;
}
/**
* Options to emit a route transition
*/
export interface TransitionOptions<
PARAMS extends object = Dictionary,
QUERY extends object = Dictionary,
META extends object = Dictionary
> {
params?: PARAMS;
query?: QUERY;
meta?: META;
}
export type InitialRoute = string | RouteParams;
export interface RouteParamsFilter {
(el: unknown, key: string): boolean;
}
/**
* Plain route object
*/
export type PlainRoute<T extends AnyRoute, FILTER extends string = '_'> = Partial<Omit<
T extends RouteAPI ? Omit<T, 'resolvePath' | 'toPath'> : T,
FILTER
>>;
/**
* Purified route, i.e., only common parameters
*/
export type PurifiedRoute<T extends AnyRoute> = PlainRoute<T, 'url' | 'name' | 'page'>;
/**
* Route that support watching
*/
export type WatchableRoute<T extends AnyRoute> = PlainRoute<T, 'meta'>;