import { type ChildModel } from 'racer';
import { Controller } from './Controller';
import { Page } from './Page';
import derbyTemplates = require('./templates');
import { Context } from './templates/contexts';
import { Expression } from './templates/expressions';
import { Attribute, Binding } from './templates/templates';
export interface DataConstructor extends Record<string, unknown> {
    new (): Record<string, unknown>;
}
type AnyVoidFunction = (...args: any[]) => void;
export interface ComponentConstructor {
    new (context: Context, data: Record<string, unknown>): Component;
    DataConstructor?: DataConstructor;
    singleton?: undefined;
    view?: ComponentViewDefinition;
}
export interface SingletonComponentConstructor {
    new (): object;
    singleton: true;
    view?: ComponentViewDefinition;
}
export interface ComponentViewDefinition {
    dependencies?: Array<ComponentConstructor | SingletonComponentConstructor>;
    file?: string;
    is?: string;
    source?: string;
    viewPartialDependencies?: Array<string | {
        is: string;
    }>;
}
export declare abstract class Component<T extends object = object> extends Controller<T> {
    context: Context;
    /**
     * Unique ID assigned to the component
     */
    id: string;
    /**
     * Whether the component instance is fully destroyed. Initially set to false.
     */
    isDestroyed: boolean;
    page: Page;
    /**
     * Reference to the containing controller
     */
    parent: Controller;
    singleton?: true;
    _scope: string[];
    view?: ComponentViewDefinition;
    static DataConstructor?: DataConstructor;
    constructor(context: Context, data: Record<string, unknown>);
    /**
     * Method called by Derby after instantiating a component and before rendering the template.
     *
     * This should initialize any data needed by the component, like with `this.model.start(...)`.
     *
     * `init()` could be called from the server and the browser, so do not use any DOM-only methods
     * here. Put those in `create()` instead.
     */
    init?(_model: ChildModel): void;
    /**
     * Method called by Derby once a component is loaded and ready in the DOM.
     *
     * Any model listeners (`this.model.on(...)`) and DOM listeners (`this.dom.addListener(...)`)
     * should be added here.
     *
     * This will only be called in the browser.
     */
    create?(): void;
    destroy(): void;
    /**
     * Generate a function, bound function to the component instance's `this`.
     * The returned function will no longer be invoked once the component is destroyed.
     *
     * @param fn - A function to be invoked with the component as its `this` value.
     * @returns a bound function, similar to JavaScript's Function.bind()
     *
     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind
     */
    bind(fn: (...args: unknown[]) => void): (...args: any[]) => any;
    /**
     * Generate a function that, when passing in a numeric delay, calls the function at
     * most once per that many milliseconds. Additionally, implements an interface
     * intended to be used with window.requestAnimationFrame, process.nextTick, or window.setImmediate.
     *
     * @param fn - A function to be invoked with the component instance as its `this` value.
     * @param delayArg - Amount of time (in ms) to wait until invoking `fn` again. Default '0'.
     *
     * When passing in a numeric delay, calls the function at most once per that
     * many milliseconds. Like Underscore, the function will be called on the
     * leading and the trailing edge of the delay as appropriate. Unlike Underscore,
     * calls are consistently called via setTimeout and are never synchronous. This
     * should be used for reducing the frequency of ongoing updates, such as scroll
     * events or other continuous streams of events.
     *
     * Additionally, implements an interface intended to be used with
     * window.requestAnimationFrame or process.nextTick. If one of these is passed,
     * it will be used to create a single async call following any number of
     * synchronous calls. This mode is typically used to coalesce many synchronous
     * events (such as multiple model events) into a single async event.
     * Like component.bind(), will no longer call back once the component is
  -  * destroyed, which avoids possible bugs and memory leaks.
     *
     * @returns a bound function
     */
    throttle(fn: (...args: unknown[]) => void, delayArg?: number | ((fn: () => void) => void)): (...args: any[]) => void;
    /**
     * Safe wrapper around `window.requestAnimationFrame` that ensures function not invoked
     * when component has been destroyed
     * @param fn - A function to be invoked with the component instance as its `this` value.
     */
    requestAnimationFrame(fn: () => void): void;
    /**
     * Safe wrapper around `process.nextTick` that ensures function not invoked
     * when component has been destroyed
     * @param fn - A function to be invoked with the component instance as its `this` value.
     */
    nextTick(fn: () => void): void;
    /**
     * Suppresses calls until the function is no longer called for that many milliseconds.
     * This should be used for delaying updates triggered by user input or typing text.
     *
     * @param fn - A function to be invoked with the component instance as its `this` value.
     * @param delay - Amount of time (in ms) to wait until invoking `fn`. Default '0'.
     *
     * @returns a bound function
     */
    debounce<F extends AnyVoidFunction>(fn: (...args: Parameters<F>) => void, delay?: number): (...args: Parameters<F>) => void;
    /**
     * Like debounce(), suppresses calls until the function is no longer called for
     * that many milliseconds. In addition, suppresses calls while the callback
     * function is running. In other words, the callback will not be called again
     * until the supplied done() argument is called. When the debounced function is
     * called while the callback is running, the callback will be called again
     * immediately after done() is called. Thus, the callback will always receive
     * the last value passed to the debounced function.
     *
     * This avoids the potential for multiple callbacks to execute in parallel and
     * complete out of order. It also acts as an adaptive rate limiter. Use this
     * method to debounce any field that triggers an async call as the user types.
     *
     * Like component.bind(), will no longer call back once the component is
     * destroyed, which avoids possible bugs and memory leaks.
     *
     * Forked from: https://github.com/juliangruber/async-debounce
     *
     * @param fn - A function to be invoked with the component instance as its `this` value.
     * @param delay - Amount of time (in ms) to wait until invoking `fn`. Default '0'.
     *
     * @returns a bound function
     */
    debounceAsync<F extends AnyVoidFunction>(fn: (...args: Parameters<F>) => void, delay?: number): (...args: Parameters<F>) => void;
    get(viewName: string, unescaped: boolean): any;
    getFragment(viewName: string, _ns?: string): any;
    getView(viewName: string, _ns?: string): any;
    /**
     * Retrieve the appropriate view attribute's value for a given view instance.
     * If the value is a template, it will be rendered prior to being returned.
     *
     * @param attrName the name of the view attribute used with a component instance
     * @returns any of the possible values that can be expressed with a view attribute
     *
     * @see https://derbyjs.github.io/derby/views/template-syntax/view-attributes
     */
    getAttribute<T>(attrName: string): T;
    setAttribute(key: string, value: Attribute): void;
    setNullAttribute(key: string, value: Attribute): void;
}
export declare class ComponentAttribute {
    expression: Expression;
    model: ChildModel;
    key: string;
    constructor(expression: Expression, model: ChildModel, key: string);
    update(context: Context, binding: Binding): void;
}
export declare class ComponentAttributeBinding extends Binding {
    template: any;
    context: Context;
    condition: any;
    constructor(expression: Expression, model: any, key: any, context: Context);
}
export declare function createFactory(constructor: ComponentConstructor | SingletonComponentConstructor): SingletonComponentFactory | ComponentFactory;
export declare class ComponentModelData {
    id: string;
    $controller: Controller;
    $element: any;
    $event: any;
    [key: string]: unknown;
}
export declare class ComponentFactory {
    constructorFn: ComponentConstructor;
    constructor(constructorFn: ComponentConstructor);
    init(context: Context): derbyTemplates.contexts.Context;
    create(context: any): void;
}
declare class SingletonComponentFactory {
    constructorFn: SingletonComponentConstructor;
    isSingleton: true;
    component: Component;
    constructor(constructorFn: any);
    init(context: any): any;
    create(): void;
}
export declare function extendComponent(constructor: SingletonComponentConstructor | ComponentConstructor): void;
export {};
