import { InputSignal, OnChanges, Signal, SimpleChanges } from '@angular/core';
import { DICompareHost } from 'di-controls/classes';
import { DIControl, DIControlConfig } from './control';
import { DICompareFunction } from 'di-controls/types';
import * as i0 from "@angular/core";
/**
 * Configuration for the `DIStateControl`.
 */
export interface DIStateControlConfig<TModel, TValue extends TModel = TModel> extends DIControlConfig<TModel, TModel> {
    /**
     * Value that will be used for the unchecked state.
     */
    uncheckValue?: TModel;
    /**
     * Function that will be used to compare model value with the `value` property.
     */
    compare?: DICompareHost<TModel | null, TValue> | DICompareFunction<TModel | null, TValue> | null;
    /**
     * Indicates whether the current control can have intermediate state.
     */
    hasIntermediate?: boolean;
}
/**
 * `DIStateControl` can be used to implement state controls (checkbox, radio, chip, switch, etc.).
 * It extends `DIControl` and adds `checked` signal that can be used to get checked state.
 * By default it works with `boolean` type, it adds `value` input that can be used to set custom
 * "true" value.
 *
 * ## Creating a control
 * To create a control you need to extend your `@Component` or `@Directive` from `DIStateControl` class.
 * After that your control will be able to work with `NgModel`, `FormControl`.
 *
 * ```ts fileName="custom-control.component.ts"
 * @Component({})
 * export class CustomControlComponent<T = boolean> extends DIStateControl<T> {
 *   @Input({required: true})
 *   value!: T;
 *
 *   constructor() {
 *    super();
 *  }
 * }
 *  ```
 *
 * ## Injecting host control
 * By default your control doesn't communicate with host controls. But you can inject host control and put it
 * into `super` call. This will register your control in the host control and start communication between them.
 *
 * > **Note**
 * > If you register your control as a host for another controls, then you can inject it
 * > only with `skipSelf` option.
 *
 * ```ts {5} fileName="custom-control.component.ts"
 * @Component({})
 * export class CustomControlComponent<T = boolean> extends DIStateControl<T> {
 *   constructor() {
 *     // we add `optional` option to make it possible to use this control without host
 *     super({host: injectHostControl({optional: true})});
 *   }
 * }
 * ```
 *
 * ## Getting checked state
 * To get checked state you need to use `checked` signal. It will return `true` if the current control is checked,
 * `false` if it is unchecked and `null` if it is in intermediate state.
 *
 * ```ts {9} fileName="custom-control.component.ts"
 * @Component({})
 * export class CustomControlComponent<T> extends DIControl<T> {
 *  @Input({required: true})
 *   value!: T;
 *
 *   constructor() {
 *     super();
 *
 *     console.log(this.checked());
 *   }
 * }
 * ```
 *
 * ## Getting model
 * To get model you need to use `model` property. It will return model for the current control.
 *
 * ```ts {9} fileName="custom-control.component.ts"
 * @Component({})
 * export class CustomControlComponent<T = boolean> extends DIStateControl<T> {
 *   constructor() {
 *     super();
 *   }
 *
 *   @HostListener('click')
 *   onClick() {
 *     console.log(this.checked());
 *   }
 * }
 * ```
 *
 * ## Updating model
 * To update model you should use `check`, `uncheck`, `intermediate` or `toggle` methods.
 * They will update model based on the current state and configuration.
 *
 * ```ts {9} fileName="custom-control.component.ts"
 * @Component({})
 * export class CustomControlComponent<T = boolean> extends DIStateControl<T> {
 *   constructor() {
 *     super();
 *   }
 *
 *   @HostListener('click')
 *   onClick() {
 *     this.toggle();
 *   }
 * }
 * ```
 *
 * ## Catching updates
 * Sometimes you may need to catch updates from different sources. For example, to update the value of the native
 * input element. To do this, you can provide the `onIncomingUpdate` hook.
 *
 * ```ts {6} fileName="custom-control.component.ts"
 * @Component({})
 * export class CustomControlComponent<T = boolean> extends DIStateControl<T>  {
 *   constructor() {
 *     super({
 *       onIncomingUpdate: (value: string | null) => {
 *         this.elementRef.nativeElement.value = value;
 *       },
 *     });
 *   }
 * }
 * ```
 *
 *
 *  ## Using with DICollectionControl
 *  Using `DIStateControl` together with `DICollectionControl` will result in
 *  `DICollectionControl` containing a list of values from `DIStateControl` that have
 *  a checked state. If your `DIStateControl` has objects as values, you may
 *  likely need a comparison function because they can sometimes be immutable.
 *
 * To achieve this, provide your `DICollectionControl` as a `DICompareHost` and
 * inject it into your `DIStateControl` to give `DIStateControl` access to
 * the `compareFn` function.
 *
 * > **Warning**
 * > `DICollectionControl` requires an explicit specification of the `uncheckedValue` in the `DIStateControl`
 *
 * ```ts {3-4} fileName="checkbox-group.component.ts"
 * @Component({
 *   providers: [
 *     provideHostControl(CheckboxGroupComponent),
 *     provideCompareHost(CheckboxGroupComponent),
 *   ],
 * })
 * export class CheckboxGroupComponent<T> extends DICollectionControl<T> {
 *   constructor() {
 *     super();
 *   }
 * }
 * ```
 *
 * ```ts {5-7} fileName="checkbox.component.ts"
 * @Component()
 * export class CheckboxComponent<T> extends DIStateControl<T> {
 *   constructor() {
 *     super({
 *       host: injectHostControl({ optional: true }),
 *       compare: inject(DICompareHost, { optional: true }),
 *       hasIntermediate: true,
 *     });
 *
 *   @HostListener('click')
 *   onClick() {
 *     this.toggle();
 *   }
 * }
 * ```
 */
export declare abstract class DIStateControl<TModel, TValue extends TModel = TModel> extends DIControl<TModel> implements OnChanges {
    protected readonly config?: DIStateControlConfig<TModel, TValue> | undefined;
    /**
     * Value that will be used for the checked state.
     * You can override it to transform it to `@Input` or to set value by default.
     */
    abstract value: TValue | InputSignal<TValue>;
    checked: Signal<boolean | null>;
    protected constructor(config?: DIStateControlConfig<TModel, TValue> | undefined);
    ngOnChanges({ value }: SimpleChanges): void;
    /** Sets checked state */
    check(): void;
    /** Sets unchecked state */
    uncheck(): void;
    /** Sets intermediate state */
    intermediate(): void;
    /** Toggles checked state */
    toggle(): void;
    get isIntermediate(): boolean;
    static ɵfac: i0.ɵɵFactoryDeclaration<DIStateControl<any, any>, never>;
    static ɵdir: i0.ɵɵDirectiveDeclaration<DIStateControl<any, any>, never, never, {}, {}, never, never, false, never>;
}
