UNPKG

@formidable-webview/webshell

Version:

🔥 Craft Robust React Native WebView-based components with ease.

503 lines (472 loc) • 13.8 kB
import type { ComponentType, ForwardRefExoticComponent, RefAttributes, ElementRef, ComponentPropsWithoutRef, Ref } from 'react'; import Feature, { FeatureClass } from './Feature'; // LOOKUP TYPES /** * A lookup type to extract the instance from a {@link FeatureClass}. * * @typeparam F - The type of the feature class to infer from. * * @example * * ```ts * type ForceElementInstanceType = ExtractFeatureFromClass<typeof ForceElementSizeFeature>; * ``` * * @public */ export type ExtractFeatureFromClass<F> = F extends FeatureClass< infer O, infer S, infer W > ? Feature<O, S, W> : never; /** * A lookup type to get the shell component from `WebView` and feature classes. * * @example * * ```ts * type MyShellComponent = ExtractWebshellFromFeatClass< * typeof WebView, * [typeof HandleElementCSSBoxFeature] * >; * ``` * * @typeparam C - The type of the `WebView` component. * @typeparam F - The type for a collection of features classes. * * @public */ export type ExtractWebshellFromFeatClass< C extends ComponentType<any>, F extends FeatureClass<any, any, any>[] > = WebshellComponent<C, ExtractFeatureFromClass<F[number]>[]>; /** * A lookup type to extract Web Handler specs from {@link WebHandlerDefinition}. * * @typeparam W - The type for the webhandler definition to which specs should be built. * * @public */ export type ExtractWebHandlerSpecFromDef<W> = W extends WebHandlerDefinition< infer I, infer P > ? { [k in I]: WebHandlerDefinition<I, P>; } : never; /** * A lookup type to extract props from {@link PropsSpecs}. * * @typeparam S - The type for the specs to which props should be extracted. * * @public */ export type ExtractPropsFromSpecs<S> = S extends PropsSpecs<infer N, any> ? S[N] extends never ? {} : Required<S[N]>['signature'] : never; /** * A lookup type to extract Web handler specs from {@link Feature}. * * @typeparam F - The type of the feature from which handler specs should be extracted. * * @public */ export type ExtractWebHandlerSpecsFromFeature<F> = F extends Feature< any, any, infer P > ? P : never; /** * A lookup type to infer the additional props from a feature. * * @typeparam F - The type of the feature from which prop specs should be extracted. * * @public */ export type ExtractPropsFromFeature<F> = F extends Feature<any, infer P, any> ? ExtractPropsFromSpecs<P> : {}; // CONCRETE TYPES /** * A shell component type derived from its features. * * @typeparam C - A type of the `WebView` component. * @typeparam F - A type for a collection of features to inject. * * @public */ export type WebshellComponent< C extends ComponentType<any>, F extends Feature<any, any, any>[] > = ForwardRefExoticComponent< WebshellProps<ComponentPropsWithoutRef<C>, F> & RefAttributes<ElementRef<C>> >; /** * A minimal set of attributes to define a feature. * * @typeparam O - A type describing the shape of the JSON-serializable object that will be passed to the Web script. * * @public */ export type FeatureDefinition<O extends {}> = { /** * The string containing valid ECMAScript 5 to be run in the WebView. * * @remarks * The script must define a single function which only argument is of the * type {@link WebjsContext}. * * It is recommended that you use eslint to validate this script syntax, and * event better, unit-test the script. See our repository home page for more * information. */ readonly script: string; /** * A unique identifier of the feature. The convention is to use a reverse * namespace domain ending with the feature name. * * @example * org.formidable-webview/webshell.link-press */ readonly identifier: string; /** * These options will be shallow-merged with the options provided to the {@link FeatureClass}. */ readonly defaultOptions: Required<O>; }; /** * An object to define an API to send messages from shell to Web. * * @typeparam I - A type for the event identifier. * @typeparam P - A type describing the shape of payloads sent to Web handlers. * * @public */ export interface WebHandlerDefinition<I extends string, P> { eventId: I; payload?: P; async: false; } /** * An object describing the structure of messages a feature Web script can handle. * * @typeparam I - A type for the event identifier. * @typeparam P - A type describing the shape of payloads sent to Web handlers. * * @public */ export type WebHandlersSpecs<I extends string = string, P = {}> = { [k in I]: WebHandlerDefinition<I, P>; }; /** * An object to define an API to send messages from Web to shell. * * @typeparam N - A type to define the name of the prop. * @typeparam P - A type describing the shape of the prop. * * @public */ export type PropDefinition<N extends string, P> = { eventId: string; type: 'handler' | 'inert'; featureIdentifier: string; name: N; signature?: Partial<Record<N, P>>; }; /** * * @typeparam N - A type to define the names of the props. * @typeparam P - A type describing the shapes of the props. * * @public */ export type PropsSpecs<N extends string, P> = { [k in N]: PropDefinition<k, P>; }; /** * An object to send messages from the shell to the Web. * See {@link WebjsContext.onShellMessage}, {@link WebshellInvariantProps.webHandleRef} and {@link FeatureBuilder.withWebHandler}. * * @public */ export interface WebHandle { /** * Send a message to a Web handler, e.g. a callback registered in the Web script associated with a feature. * * @remarks * * Web handlers must have been declared with {@link FeatureBuilder.withWebHandler}. * * * @param feat - The feature to which a message should be sent. * @param eventId - A unique identifier for the event sent to the Web script. * @param payload - The type of the message to sent. */ postMessageToWeb< F extends Feature<any, any, any>, H extends keyof ExtractWebHandlerSpecsFromFeature<F> >( feat: F, eventId: H, payload: Required<ExtractWebHandlerSpecsFromFeature<F>[H]>['payload'] ): void; } /** * Props all shell components will support. * * @public */ export interface WebshellInvariantProps { /** * Triggered when a feature script throws. */ onWebFeatureError?: (featureIdentifier: string, error: string) => void; /** * Report Web error messages from features in the console. * * @defaultvalue `__DEV__` (`true` in development, `false` otherwise) */ webshellDebug?: boolean; /** * If this prop is `true` and `webshellDebug` is `true`, errors will be * thrown when inconsistencies are identified. * * @defaultvalue false */ webshellStrictMode?: boolean; /** * Pass a reference to send messages to the Web environment. */ webHandleRef?: Ref<WebHandle>; } /** * Props of the Webshell produced by {@link makeWebshell}. * * @typeparam W - The type for the Props of the `WebView` component. * @typeparam F - The type for a collection of features classes. * * @public */ export type WebshellProps< W extends MinimalWebViewProps, F extends Feature<any, any, any>[] > = WebshellInvariantProps & W & (F[number] extends never ? {} : ExtractPropsFromFeature<F[number]>); /** * A high-compatibility type expressing minimal requirements for the * WebView Component's props. * * @public */ export interface MinimalWebViewProps { readonly onMessage?: unknown; readonly onError?: unknown; readonly injectedJavaScript?: unknown; readonly javaScriptEnabled?: unknown; readonly source?: Record<string, any>; readonly style?: unknown; readonly onNavigationStateChange?: unknown; readonly scalesPageToFit?: unknown; readonly showsVerticalScrollIndicator?: unknown; readonly disableScrollViewPanResponder?: unknown; readonly contentMode?: unknown; } // Web TYPES /** * A collection of utilities to manipulate the DOM. * * @public */ export interface DOMUtils { /** * Get one element in the DOM from a request. See {@link DOMElementRequest}. * * @returns An {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement | HTMLElement} or `null`. */ getDOMSelection(selector: DOMElementRequest): HTMLElement | null; /** * Get a collection of live elements in the DOM from a query request. * * @param selector - Which elements should be returned? * @returns A live {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection | HTMLCollection}. */ getDOMSelectionAll(selector: DOMElementQueryRequest | string): any; /** * Get a collection of static elements in the DOM from a class or tag-name request. * * @param selector - Which elements should be returned? * @returns A static {@link https://developer.mozilla.org/en-US/docs/Web/API/NodeList | NodeList}. */ getDOMSelectionAll( selector: DOMElementClassNameRequest | DOMElementTagNameRequest ): any; /** * @param style - The style to parse, e.g. `'18px'` * * @returns Numeric value in CSS pixels. */ numericFromPxString(style: string): number; } /** * This type specifies the shape of the object passed to Web features scripts. * * @typeparam O - A type describing the shape of the JSON-serializable object given by the shell. * * @public */ export interface WebjsContext<O extends {}> { /** * The options to customize the script behavior. */ readonly options: O; /** * Instruct the shell to send **the default event** associated with * this feature, if any. * * @param payload - The value which will be passed to the handler. * @typeparam P - A type describing the shape of the payload sent to shell. * */ postMessageToShell<P>(payload: P): void; /** * Instruct the shell to call the handler associated with this * feature and `eventId`, if any. * * @param eventId - A unique identifier for the event sent to the shell. * You can omit this param if you are sending a `"default"` event identifier. * @param payload - The value which will be passed to the handler. * @typeparam P - A type describing the shape of the payload sent to shell. */ postMessageToShell<P>(eventId: string, payload: P): void; /** * Register a handler on messages sent from the shell. * * @param eventId - A unique identifier for the event received by the Web script. * @param payload - The value which will be passed to the handler. * @typeparam P - A type describing the shape of the payload sent by shell. */ onShellMessage<P>(eventId: string, handler: (payload: P) => void): void; /** * Create a function which execute a callback in a try-catch block that will * grab errors en send them to the `Webshell` component. * * @param callback - The callback to try-catch. */ makeCallbackSafe<T extends Function>(callback: T): T; /** * Safely post a warn message to the console. The message will be routed to * shell and printed in the React Native console during development. */ warn(message: string): void; /** * Safely post an info message to the console. The message will be routed to * shell and printed in the React Native console during development. */ info(message: string): void; /** * A collection of utilities to manipulate the DOM. */ utils: DOMUtils; } /** * @public */ export interface DOMRectSize { width: number; height: number; } /** * See {@link https://developer.mozilla.org/en-US/docs/Web/API/DOMRect | DOMRect}. * * @public */ export interface DOMRect extends DOMRectSize { top: number; left: number; right: number; bottom: number; } /** * A request to select one element in the DOM. * * @remarks * A string will be interpreted as a “query” request. * See {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector | Document.querySelector() } and {@link DOMElementQueryRequest}. * * @public */ export type DOMElementRequest = | DOMElementQueryRequest | DOMElementClassNameRequest | DOMElementIdRequest | DOMElementTagNameRequest | string; /** * A request to select a collection of elements in the DOM. * * @remarks * A string will be interpreted as a “query” request. * See {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll | Document.querySelectorAll() } and {@link DOMElementQueryRequest}. * * @public */ export type DOMCollectionRequest = | DOMElementQueryRequest | DOMElementClassNameRequest | DOMElementTagNameRequest | string; /** * A request by query string. * See {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll | Document.querySelectorAll() } * and {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector | Document.querySelector() } * * @public */ export type DOMElementQueryRequest = { query: string; }; /** * A request by id (case-insensitive); * See {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById | Document.getElementById() } * * @public */ export type DOMElementIdRequest = { id: string; }; /** * A request by one or many case-sensitive class names, separated by spaces. * See {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName | Document.getElementsByClassName() } * * @public */ export type DOMElementClassNameRequest = { className: string; }; /** * A query by tag name. * See {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName | Document.getElementsByTagName() } * * @remarks * `'html'` will select `document.documentElement`. * * @public */ export type DOMElementTagNameRequest = { tagName: string; };