import { Component, ComponentClass, ComponentType, ReactNode } from 'react' import { Actor, Selector } from 'zedux' /** * Create an api around an observable (e.g. a Zedux store). * The observable must be exposed on each instance of the class * as a property named `store`. * * A class that extends StoreApi can be passed to * ReactZedux.createContext() * * React Zedux will instantiate the class and bind actors * and selectors to the store. * * @prop {ActorsMap} [actors] - A hash (can be nested) of actors * to bind to the store and attach to the StoreApi instance. * * @prop {SelectorsMap} [selectors] - A hash (can be nested) of selectors * to bind to the store and attach to the StoreApi instance. * * @export * @class StoreApi */ export abstract class StoreApi { /** * Performs the actor/selector binding and flattening. * * React Zedux uses this internally, but it can be useful for testing. * * Will throw an error if the StoreApi instance does not have a visible * `store` property. * * @returns {StoreApi} for chaining * @memberof StoreApi */ _bindControls(): StoreApi } /** * Creates a consumable context out of a state container. * * The state container can be either: * 1. An observable (e.g. a Zedux store) * 2. A class that extends ReactZedux.StoreApi * * Since `new [class that extends StoreApi]()` must return an object that * has an observable `store` property, option 2 is really just an * extension of option 1. But since it will be instantiated once for * every , it allows for dynamic observable creation. * * Returns an object containing 3 First-Order Components: * * , , and * * and their corresponding Higher-Order Components: * * provide(), consume(), inject() * * Provider and Consumer must be used together. * An error will be thrown if Consumer is used alone. * * Use Injector to simultaneously provide and consume the context. * * @export * @template TState The state type emitted by the observable * @template TContainer The state container type * @param {TContainer} stateContainer The state container. * Can be either an observable or a class that extends ReactZedux.StoreApi * * @returns {Context>} */ export function createContext< TState, TContainer extends StateContainer = StateContainer >( stateContainer: TContainer ): Context> export function createContext>( stateContainer: StoreApiConstructor ): Context< WrappedStateContainer< TState, StoreApiConstructor['actors'] & StoreApiConstructor['selectors'] & TStoreApi & TStoreApi['store'] > > export interface ActorsMap { [key: string]: Actor } export interface Consumer extends Component< ConsumerProps, {} > {} export interface ConsumerComponentEnhancer< TEnhancerProps, TConsumerProps extends TEnhancerProps > { (component: ComponentType): ComponentClass< Omit > } export interface ConsumerProps { children: (value: TWrappedContainer) => ReactNode } export interface Context { Provider: Provider provide( component: ComponentType ): ComponentClass Consumer: Consumer consume(): ConsumerComponentEnhancer // A single string consume< TConsumerProps extends { [key in TStorePropName]: any }, TStorePropName extends string = '' >( mapStoreToProps: TStorePropName ): ConsumerComponentEnhancer< { [key in TStorePropName]: TWrappedContainer }, TConsumerProps > // An array of values to pluck consume< TConsumerProps extends {}, TListOfPropsToPluck extends keyof TConsumerProps >( mapStoreToProps: TListOfPropsToPluck[] ): ConsumerComponentEnhancer< Partial, TConsumerProps > // A storeFields-to-props map consume< TConsumerProps extends { [key in TAliasedProps]: any }, TAliasedProps extends string >( mapStoreToProps: { [key in keyof TWrappedContainer]?: TAliasedProps } ): ConsumerComponentEnhancer< Partial, TConsumerProps > // A custom storeFields-to-props mapper function consume< TConsumerProps extends TMappedProps, TMappedProps extends Object >( mapStoreToProps: (store: TWrappedContainer) => TMappedProps ): ConsumerComponentEnhancer< TMappedProps, TConsumerProps > Injector: Injector inject(): ConsumerComponentEnhancer // A single string inject< TConsumerProps extends {[key in TStorePropName]: any }, TStorePropName extends string = '' >( mapStoreToProps: TStorePropName ): ConsumerComponentEnhancer< {[key in TStorePropName]: TWrappedContainer }, TConsumerProps > // An array of values to pluck inject< TConsumerProps extends {}, TListOfPropsToPluck extends keyof TConsumerProps >( mapStoreToProps: TListOfPropsToPluck[] ): ConsumerComponentEnhancer< Partial, TConsumerProps > // A storeFields-to-props map inject< TConsumerProps extends {[key in TAliasedProps]: any }, TAliasedProps extends string >( mapStoreToProps: {[key in keyof TWrappedContainer]?: TAliasedProps } ): ConsumerComponentEnhancer< Partial, TConsumerProps > // A custom storeFields-to-props mapper function inject< TConsumerProps extends TMappedProps, TMappedProps extends Object >( mapStoreToProps: (store: TWrappedContainer) => TMappedProps ): ConsumerComponentEnhancer< TMappedProps, TConsumerProps > } export interface Injector extends Component< Omit, 'children'> & ConsumerProps, {} > {} export interface Observable { getState?(): TState subscribe(observer: Observer): Subscription | (() => void) } export interface Provider extends Component< ProviderProps, {} > {} export interface ProviderProps { children?: ReactNode onMount?: (store: TWrappedContainer) => void onUnmount?: (store: TWrappedContainer) => void } export interface SelectorsMap { [key: string]: Selector } export interface StoreApi { store: Observable } export interface StoreApiConstructor { new (): StoreApi actors?: ActorsMap selectors?: SelectorsMap } export interface Subscription { unsubscribe(): void } export type ListOfPropsToPluck = Array export type Observer = { next(newState: TState): void } | ((newState: TState) => void) | (() => void) export type StateContainer = Observable | StoreApiConstructor export type StorePropName = string export type StoreToPropsMap = StorePropName | ListOfPropsToPluck | StoreToPropsMapper export type StoreToPropsMapper = (store: TWrappedContainer) => { [key: string]: any } export type WrappedStateContainer = TContainer & { state: Readonly store: TContainer } type Diff< T extends string, U extends string > = ( {[P in T]: P } & {[P in U]: never } & { [x: string]: never } )[T] type Omit< T, K extends keyof T > = Pick< T, Diff >