import React from 'react';
import { MergeProps, RemoveNulls } from './mergeProps';
import { Model } from './models';
/**
 * Adds the `as` to the style interface to support `as` in styled components
 */
export type StyledType = {
    as?: React.ElementType;
};
type Constructor<T> = new (...args: any[]) => T;
/**
 * Extract a Ref from T if it exists
 * This will return the following:
 *
 * - `undefined` => `never`
 * - `'button'` => `React.Ref<HTMLButtonElement>`
 * - `ElementComponent<'button', ButtonProps>` => `React.Ref<HTMLButtonElement>`
 */
type ExtractRef<T> = T extends undefined ? never : T extends Constructor<infer C> ? React.LegacyRef<C> : React.Ref<T extends keyof ElementTagNameMap ? ElementTagNameMap[T] : T extends ElementComponent<infer U, any> ? U extends keyof ElementTagNameMap ? ElementTagNameMap[U] : U : T extends React.FC<{
    ref?: infer R;
}> ? R extends React.RefObject<infer E> ? E : never : T>;
/**
 * Generic component props with "as" prop
 * @template P Additional props
 * @template ElementType React component or string element
 */
export type PropsWithoutAs<P, ElementType extends React.ElementType> = P & Omit<React.ComponentProps<ElementType>, 'as' | keyof P> & {
    /**
     * Optional ref. If the component represents an element, this ref will be a reference to the
     * real DOM element of the component. If `as` is set to an element, it will be that element.
     * If `as` is a component, the reference will be to that component (or element if the component
     * uses `React.forwardRef`).
     */
    ref?: ExtractRef<ElementType>;
};
/**
 * Extracts only the HTML attribute interface from `JSX.IntrinsicElements[E]`. This effectively removes `ref` and `key`
 * without using `Omit` which makes the returned type more difficult to understand.
 *
 * For example:
 * JSX.IntrinsicElements['button'] // React.ClassAttributes<HTMLButtonElement> & React.ButtonHTMLAttributes<HTMLButtonElement>
 * ExtractHTMLAttributes<JSX.IntrinsicElements['button']> // React.HTMLButtonAttributes<HTMLButtonElement>
 */
type ExtractHTMLAttributes<T extends React.DetailedHTMLProps<any, any>> = T extends React.DetailedHTMLProps<infer P, any> ? P : T;
/**
 * Extract props from any component that was created using `createComponent`. It will return the
 * HTML attribute interface of the default element used with `createComponent`. If you use `as`, the
 * HTML attribute interface can change, so you can use an override to the element you wish to use.
 * You can also disable the HTML attribute by passing `never`:
 *
 * - `ExtractProps<typeof Card>`: `CardProps & React.HTMLAttributes<HTMLDivElement>`
 * - `ExtractProps<typeof Card, 'aside'>`: `CardProps & React.HTMLAttributes<HTMLElement>`
 * - `ExtractProps<typeof Card, never>`: `CardProps`
 *
 * @template TComponent The component you wish to extract props from. Needs 'typeof` in front:
 * `typeof Card`
 * @template TElement An optional override of the element that will be used. Define this if you use
 * an `as` on the component
 *
 * @example
 * interface MyProps extends ExtractProps<typeof Card.Body> {}
 *
 * ExtractProps<typeof Card>; // CardProps & React.HTMLAttributes<HTMLDivElement>
 * ExtractProps<typeof Card, 'aside'>; // CardProps & React.HTMLAttributes<HTMLElement>
 * ExtractProps<typeof Card, never>; // CardProps
 */
export type ExtractProps<TComponent, TElement extends keyof JSX.IntrinsicElements | ElementComponent<any, any> | Component<any> | React.ComponentType<any> | undefined | never = undefined> = ExtractMaybeModel<TComponent, TComponent extends {
    __element: infer E;
    __props: infer P;
} ? [TElement] extends [never] ? P : TElement extends undefined ? E extends keyof JSX.IntrinsicElements ? P & ExtractHTMLAttributes<JSX.IntrinsicElements[E]> : P & ExtractProps<E> : TElement extends keyof JSX.IntrinsicElements ? P & ExtractHTMLAttributes<JSX.IntrinsicElements[TElement]> : P & ExtractProps<TElement> : TComponent extends {
    __props: infer P;
} ? P : TComponent extends React.ComponentType<infer P> ? P : {}>;
type ExtractMaybeModel<TComponent, P> = TComponent extends {
    __model: infer M;
} ? P & PropsWithModel<M> : P;
/**
 * `PropsWithModel` adds the `model` and `elemPropsHook` props. If a model-based component has an
 * `as` referencing another model-based component, the `model` of the `as` component should win.
 */
export type PropsWithModel<TModel, ElementType = {}> = {
    /**
     * Optional model to pass to the component. This will override the default model created for the
     * component. This can be useful if you want to access to the state and events of the model, or if
     * you have nested components of the same type and you need to override the model provided by
     * React Context.
     */
    model?: ElementType extends {
        __model: infer M;
    } ? M : TModel;
    /**
     * Optional hook that receives the model and all props to be applied to the element. If you use
     * this, it is your responsibility to return props, merging as appropriate. For example, returning
     * an empty object will disable all elemProps hooks associated with this component. This allows
     * finer control over a component without creating a new one.
     */
    elemPropsHook?: <TProps>(model: TModel, elemProps: TProps) => any;
};
/**
 * Component type that allows for `as` to change the element or component type.
 * Passing `as` will correctly change the allowed interface of the JSX element
 */
export type ElementComponent<E extends React.ElementType, P> = {
    displayName?: string;
    <ElementType extends React.ElementType>(props: PropsWithoutAs<P, ElementType> & {
        /**
         * Optional override of the default element used by the component. Any valid tag or Component.
         * If you provided a Component, this component should forward the ref using `React.forwardRef`
         * and spread extra props to a root element.
         */
        as: ElementType;
    }): JSX.Element;
    (props: PropsWithoutAs<P, E>): JSX.Element;
    as<E extends React.ElementType>(as: E): ElementComponent<E, P>;
    /** @private Only used internally to hold the element type for extraction */
    __element: E;
    /** @private Only used internally to hold the element type for extraction */
    __props: P;
};
/**
 * Component type that allows for `as` to change the element or component type.
 * Passing `as` will correctly change the allowed interface of the JSX element.
 * Same as `ElementComponent`, but adds a model to the interface.
 */
export type ElementComponentM<E extends React.ElementType, P, TModel> = {
    displayName?: string;
    <ElementType extends React.ElementType>(props: PropsWithoutAs<P, ElementType> & PropsWithModel<TModel, ElementType> & {
        /**
         * Optional override of the default element used by the component. Any valid tag or Component.
         * If you provided a Component, this component should forward the ref using `React.forwardRef`
         * and spread extra props to a root element.
         */
        as: ElementType;
    }): JSX.Element;
    (props: PropsWithoutAs<P, E> & PropsWithModel<TModel>): JSX.Element;
    as<E extends React.ElementType>(as: E): ElementComponentM<E, P, TModel>;
    /** @private Only used internally to hold the element type for extraction */
    __element: E;
    /** @private Only used internally to hold the element type for extraction */
    __props: P;
    /** @private Only used internally to hold the element type for extraction */
    __model: TModel;
};
export type ComponentM<P, TModel> = {
    displayName?: string;
    (props: P & PropsWithModel<TModel>): JSX.Element;
    /** @private Only used internally to hold the element type for extraction */
    __props: P;
    /** @private Only used internally to hold the element type for extraction */
    __model: TModel;
};
export type Component<P> = {
    displayName?: string;
    (props: P): JSX.Element;
    /** @private Only used internally to hold the element type for extraction */
    __props: P;
};
interface RefForwardingComponent<T, P = {}> {
    (props: React.PropsWithChildren<P>, 
    /**
     * A ref to be forwarded. Pass it along to the root element. If no element was passed, this
     * will result in a `never`
     */
    ref: ExtractRef<T>, 
    /**
     * An element - either a JSX element or a `ElementComponent`. This should be passed as an `as`
     * to a root element or be the root element. If no element was passed, this will result in a
     * `never`
     */
    Element: T extends undefined ? never : T): JSX.Element | null;
}
export declare const createContainer: <E extends keyof JSX.IntrinsicElements | ElementComponent<any, any> | React.ComponentType<{}> | ElementComponentM<any, any, any> | undefined = undefined>(as?: E | undefined) => <TModelHook extends ((config: any) => Model<any, any>) & {
    Context?: React.Context<any> | undefined;
} & {
    defaultConfig?: Record<string, any> | undefined;
}, TDefaultContext extends Model<any, any>, TElemPropsHook, SubComponents = {}>({ displayName, modelHook, elemPropsHook, defaultContext, subComponents, }: {
    displayName?: string | undefined;
    modelHook: TModelHook;
    elemPropsHook?: TElemPropsHook | undefined;
    defaultContext?: TDefaultContext | undefined;
    subComponents?: SubComponents | undefined;
}) => <Props>(Component: (props: CompoundProps<Props, TElemPropsHook, E>, Element: E extends undefined ? never : E, model: TModelHook extends (config: infer TConfig) => infer TModel ? TModel : never) => JSX.Element | null) => (TModelHook extends (config: infer TConfig_1) => infer TModel_1 ? E extends undefined ? ComponentM<Props & TConfig_1, TModel_1> : ElementComponentM<E extends undefined ? React.FC<{}> : E, Props & TConfig_1, TModel_1> : never) & SubComponents;
/**
 * If elemProps returns `null` for a prop, that prop is to be removed from the prop
 * list. This is useful for passing props to an elemProp hook that should not be exposed
 * to the DOM element
 */
type RemoveNull<T> = {
    [K in keyof T]: Exclude<T[K], null>;
};
/**
 * Props for the compound component based on props, elemPropsHook, and element. It will
 * return a prop interface according to all these inputs. The following will be added to
 * the passed in `Props` type:
 * - The prop interface returned by the `elemPropsHook` function
 * - if there is no detected `ref` in the `Props` interface, a `ref` will be added based on the element type E
 * - if there is no detected `children` in the `Props` interface, `children` will be added based on `ReactNode`
 */
type CompoundProps<Props, TElemPropsHook, E> = Props & (TElemPropsHook extends (...args: any[]) => infer TProps ? RemoveNull<Omit<TProps, 'ref'> & {
    ref: ExtractRef<E>;
}> : {
    ref: ExtractRef<E>;
}) & (Props extends {
    children: any;
} ? {} : {
    children?: React.ReactNode;
});
export declare const createSubcomponent: <E extends keyof JSX.IntrinsicElements | ElementComponent<any, any> | React.ComponentType<{}> | ElementComponentM<any, any, any> | undefined = undefined>(as?: E | undefined) => <TElemPropsHook, TModelHook extends ((config: any) => Model<any, any>) & {
    Context?: React.Context<any> | undefined;
}, SubComponents = {}>({ displayName, modelHook, elemPropsHook, subComponents, }: {
    /** @deprecated ⚠️ `displayName` has been deprecated and will be removed in a future major version. You no longer need to use `displayName`. A `displayName` will be automatically added if it belongs to a container. */
    displayName?: string | undefined;
    modelHook: TModelHook;
    elemPropsHook?: TElemPropsHook | undefined;
    subComponents?: SubComponents | undefined;
}) => <Props = {}>(Component: (props: CompoundProps<Props, TElemPropsHook, E>, Element: E extends undefined ? never : E, model: TModelHook extends (...args: any[]) => infer TModel ? TModel : never) => JSX.Element | null) => (TModelHook extends (...args: any[]) => infer TModel_1 ? ElementComponentM<E extends undefined ? React.FC<{}> : E, Props, TModel_1> : never) & SubComponents;
/**
 * Factory function that creates components to be exported. It enforces React ref forwarding, `as`
 * prop, display name, and sub-components, and handles proper typing without much boiler plate. The
 * return type is `Component<element, Props>` which looks like `Component<'div', Props>` which is a
 * clean interface that tells you the default element that is used.
 */
export declare const createComponent: <E extends keyof JSX.IntrinsicElements | ElementComponent<any, any> | React.ComponentType<{}> | undefined = undefined>(as?: E | undefined) => <P, SubComponents = {}>({ displayName, Component, subComponents, }: {
    /** This is what the component will look like in the React dev tools. Encouraged to more easily
     * understand the component tree */
    displayName?: string | undefined;
    /** The component function. The function looks like:
     * @example
     * Component: ({children}, ref, Element) {
     *   // `Element` is what's passed to the `as` of your component. If no `as` was defined, it
     *   // will be the default element. It will be 'div' or even a another Component!
     *   return (
     *     <Element ref={ref}>{children}</Element>
     *   )
     * }
     *
     * @example
     * Component: ({children}, ref, Element) {
     *   // `Element` can be passed via `as` to the next component
     *   return (
     *     <AnotherElement as={Element} ref={ref}>{children}</AnotherElement>
     *   )
     * }
     */
    Component: RefForwardingComponent<E, P>;
    /**
     * Used in container components
     */
    subComponents?: SubComponents | undefined;
}) => (E extends undefined ? Component<P> : ElementComponent<E extends undefined ? React.FC<{}> : E, P>) & SubComponents;
/**
 * An `elemPropsHook` is a React hook that takes a model, ref, and elemProps and returns props and
 * attributes to be spread to an element or component.
 *
 * ```tsx
 * const useMyHook = createElemPropsHook(useMyModel)((model) => {
 *   return {
 *     id: model.state.id
 *   }
 * })
 * ```
 *
 * **Note:** If your hook needs to use a ref, it must be forked using `useLocalRef` or `useForkRef`
 * and return the forked ref:
 *
 * ```tsx
 * const useMyHook = createElemPropsHook(useMyModel)((model, ref, elemProps) => {
 *   const {localRef, elementRef} = useLocalRef(ref);
 *
 *   React.useLayoutEffect(() => {
 *     console.log('element', localRef.current) // logs the DOM element
 *   }, [])
 *
 *   return {
 *     ref: elementRef
 *   }
 * })
 * ```
 */
export declare const createElemPropsHook: <TModelHook extends (config: any) => Model<any, any>>(modelHook: TModelHook) => <const PO extends {}, const PI extends {}>(fn: (model: TModelHook extends (config: any) => infer TModel ? TModel : Model<any, any>, ref?: React.Ref<unknown>, elemProps?: PI | undefined) => PO) => BehaviorHook<TModelHook extends (config: any) => infer TModel_1 ? TModel_1 : Model<any, any>, PO>;
/**
 * Factory function to crate a behavior hook with correct generic types. It takes a function that
 * return props and returns a function that will also require `elemProps` and will call `mergeProps` for
 * you. If your hook makes use of the `ref`, you will have to also use `useLocalRef` to properly fork
 * the ref.
 *
 * @example
 * const useMyHook = createHook((model: MyModel, ref) => {
 *   const { localRef, elementRef } = useLocalRef(ref);
 *   // do whatever with `localRef` which is a RefObject
 *
 *   return {
 *     onClick: model.events.doSomething,
 *     ref: elementRef,
 *   };
 * });
 *
 * // Equivalent to:
 * const useMyHook = <P extends {}>(
 *   model: MyModel,
 *   elemProps: P,
 *   ref: React.Ref<unknown>
 * ) => {
 *   const { localRef, elementRef } = useLocalRef(ref);
 *   // do whatever with `localRef` which is a RefObject
 *
 *   return mergeProps({
 *     onClick: model.events.doSomething,
 *     ref: elementRef,
 *   }, elemProps);
 * };
 *
 * @param fn Function that takes a model and optional ref and returns props
 */
export declare const createHook: <M extends Model<any, any>, PO extends {}, PI extends {}>(fn: (model: M, ref?: React.Ref<unknown>, elemProps?: PI | undefined) => PO) => BehaviorHook<M, PO>;
/**
 * @deprecated ⚠️ `subModelHook` has been deprecated and will be removed in a future major version. Please use `createSubModelElemPropsHook` instead.
 */
export declare const subModelHook: <M extends Model<any, any>, SM extends Model<any, any>, O extends {}>(fn: (model: M) => SM, hook: BehaviorHook<SM, O>) => BehaviorHook<M, O>;
/**
 * Creates an elemPropsHook that returns the elemProps from another hook that is meant for a
 * subModel. Usually only used when composing elemProps hooks.
 *
 * For example:
 *
 * ```tsx
 * const useMySubModel = () => {}
 *
 * const useMyModel = () => {
 *   const subModel = useMySubModel()
 *
 *   return {
 *     state,
 *     events,
 *     subModel,
 *   }
 * }
 *
 * const useMyComponent = composeHook(
 *   createElemPropsHook(useMyModel)(model => ({ id: '' })),
 *   createSubModelElemPropsHook(useMyModel)(m => m.subModel, useSomeOtherComponent)
 * )
 * ```
 */
export declare function createSubModelElemPropsHook<M extends () => Model<any, any>>(modelHook: M): <SM extends Model<any, any>, O extends {}>(fn: (model: ReturnType<M>) => SM, elemPropsHook: BehaviorHook<SM, O>) => BehaviorHook<ReturnType<M>, O>;
/** Simplify and speed up inference by capturing types in the signature itself */
interface BaseHook<M extends Model<any, any>, O extends {}> {
    /**
     * Capture the model type in TypeScript only. Do not use in runtime!
     *
     * @private
     */
    __model: M;
    /**
     * Capture the hook's output type in TypeScript only. Do not use in runtime! This is used to cache
     * and speed up the output types during inference
     *
     * @private
     */
    __output: O;
}
/**
 * A BehaviorHook is a React hook that takes a model, elemProps, and a ref and returns props and
 * attributes to apply to an element or component.
 */
export interface BehaviorHook<M extends Model<any, any>, O extends {}> extends BaseHook<M, O> {
    <P extends {}>(model: M, elemProps?: P, ref?: React.Ref<unknown>): O & P;
}
/**
 * This function will create a new forked ref out of two input Refs. This is useful for components
 * that use `React.forwardRef`, but also need internal access to a Ref.
 *
 * This function is inspired by https://www.npmjs.com/package/@rooks/use-fork-ref
 *
 * @example
 * React.forwardRef((props, ref) => {
 *   // Returns a RefObject with a `current` property
 *   const myRef = React.useRef(ref)
 *
 *   // Returns a forked Ref function to pass to an element.
 *   // This forked ref will update both `myRef` and `ref` when React updates the element ref
 *   const elementRef = useForkRef(ref, myRef)
 *
 *   useEffect(() => {
 *     console.log(myRef.current) // `current` is the DOM instance
 *     // `ref` might be null since it depends on if someone passed a `ref` to your component
 *     // `elementRef` is a function and we cannot get a current value out of it
 *   })
 *
 *   return <div ref={elementRef}/>
 * })
 */
export declare function useForkRef<T>(ref1?: React.Ref<T>, ref2?: React.Ref<T>): React.RefCallback<T>;
/**
 * This functions handles the common use case where a component needs a local ref and needs to
 * forward a ref to an element.
 * @param ref The React ref passed from the `createComponent` factory function
 *
 * @example
 * const MyComponent = ({children, ...elemProps}: MyProps, ref, Element) => {
 *   const { localRef, elementRef } = useLocalRef(ref);
 *
 *   // do something with `localRef` which is a `RefObject` with a `current` property
 *
 *   return <Element ref={elementRef} {...elemProps} />
 * }
 */
export declare function useLocalRef<T>(ref?: React.Ref<T>): {
    localRef: React.RefObject<T>;
    elementRef: (instance: T | null) => void;
};
/**
 * Returns a model, or calls the model hook with config. Clever way around the conditional React
 * hook ESLint rule.
 * @param model A model, if provided
 * @param config Config for a model
 * @param modelHook A model hook that takes valid config
 * @example
 * const ContainerComponent = ({children, model, ...config}: ContainerProps) => {
 *   const value = useDefaultModel(model, config, useContainerModel);
 *
 *   // ...
 * }
 */
export declare function useDefaultModel<T, C>(model: T | undefined, config: C, modelHook: (config: C) => T, as?: React.ElementType): T;
/**
 * Returns a model, or returns a model context. Clever way around the conditional React hook ESLint
 * rule
 * @param model A model, if provided
 * @param context The context of a model
 * @example
 * const SubComponent = ({children, model, ...elemProps}: SubComponentProps, ref, Element) => {
 *   const {state, events} = useModelContext(model, SubComponentModelContext, Element);
 *
 *   // ...
 * }
 */
export declare function useModelContext<T>(context: React.Context<T>, model?: T, as?: React.ElementType): T;
/**
 * Compose many hooks together. Each hook should make a call to `mergeProps` which is automatically
 * done by `createElemPropsHook` and `createHook. Returns a function that will receive a model and
 * return props to be applied to a component. Hooks run from last to first, but props override from
 * first to last. This means the last hook will run first, passing `elemProps` to the next last
 * hook. There is a special exception, which is `null`. `null` means "remove this prop" and the null
 * handling takes precedence to the first. Take care when using `null` as it will remove props
 * passed in even from the developer. It can be useful when passing data between composed hooks or
 * then redirecting a prop somewhere else.
 *
 * For example:
 *
 * ```ts
 * const useHook1 = createElemPropsHook(useMyModel)((model, ref, elemProps) => {
 *   console.log('useHook1', elemProps)
 *   return {
 *     a: 'useHook1',
 *     c: 'useHook1',
 *     d: null, // remove the `d` prop
 *   }
 * })
 *
 * const useHook2 = createElemPropsHook(useMyModel)((model, ref, elemProps) => {
 *   console.log('useHook2', elemProps)
 *   return {
 *     b: 'useHook2',
 *     c: 'useHook2',
 *     d: 'useHook2',
 *   }
 * })
 *
 * const useHook3 = composeHooks(
 *   useHook1, // run last, will have access to `useHook2`'s elemProps, but can remove a prop with `null`
 *   useHook2 // run first and will override all of `useHook1`'s props
 * )
 * const props = useHook3(model, { c: 'props', d: 'props' })
 * console.log('props', props)
 * ```
 *
 * The output would be:
 *
 * ```ts
 * useHook2 {c: 'props', d: 'props'}
 * useHook1 {b: 'useHook2', c: 'props', d: 'props'}
 * props {a: 'useHook1', b: 'useHook2', c: 'props', d: null}
 * ```
 */
export declare function composeHooks<H1 extends BaseHook<any, {}>, H2 extends BaseHook<any, {}>, H3 extends BaseHook<any, {}>, H4 extends BaseHook<any, {}>, H5 extends BaseHook<any, {}>, H6 extends BaseHook<any, {}>, H7 extends BaseHook<any, {}>>(hook1: H1, hook2: H2, hook3?: H3, hook4?: H4, hook5?: H5, hook6?: H6, hook7?: H7, ...hooks: BehaviorHook<any, any>[]): H1 extends BaseHook<infer M, infer O1> ? H2 extends BaseHook<any, infer O2> ? H3 extends BaseHook<any, infer O3> ? H4 extends BaseHook<any, infer O4> ? H5 extends BaseHook<any, infer O5> ? H6 extends BaseHook<any, infer O6> ? H7 extends BaseHook<any, infer O7> ? BehaviorHook<M, RemoveNulls<MergeProps<O1, MergeProps<O2, MergeProps<O3, MergeProps<O4, MergeProps<O5, MergeProps<O6, O7>>>>>>>> : never : never : never : never : never : never : never;
export {};
//# sourceMappingURL=components.d.ts.map