import WUPBaseElement, { AttributeMap } from "./baseElement";
import IBaseControl from "./controls/baseControl.i";
export declare const enum SubmitActions {
    /** Disable any action */
    none = 0,
    /** Scroll to first error (if exists) and focus control */
    goToError = 1,
    /** Validate until first error (otherwise validate all) */
    validateUntilFirst = 2,
    /** Validate only that user can change and skip controls that notVisible/disabled/readonly (values from such items still collected) */
    validateChangeable = 4,
    /** Collect to model only changed values */
    collectChanged = 8,
    /** Reset isDirty and assign $value to $initValue for controls (on success only) */
    reset = 16,
    /** Lock the whole form during the pending state (set $isPending = true or provide `promise` to submitEvent.waitFor);
     * Otherwise user can submit several times in a short time;
     * If promise resolves during the short-time pending state won't be set, otherwise it takes at least 300ms via helper {@link promiseWait} */
    lockOnPending = 32
}
declare const tagName = "wup-form";
declare global {
    namespace WUP.Form {
        interface SubmitDetails {
            /** Model collected from controls */
            model: Record<string | number, any>;
            /** Form related to submit event; equal to event.target */
            relatedForm: WUPFormElement;
            /** Event that produced submit event; null if `form.$submit()` is called */
            relatedEvent: MouseEvent | KeyboardEvent | null;
            /** Element that that produced submit event */
            submitter: HTMLElement | null;
            /** Point a promise as callback to allow form show pending state during the promise */
            waitFor?: Promise<unknown>;
        }
        interface EventMap extends WUP.Base.EventMap {
            /** Fires before $submit is happened; can be prevented via `e.preventDefault()` */
            $willSubmit: CustomEvent<Pick<SubmitDetails, "relatedEvent" | "relatedForm" | "submitter">>;
            /** Fires by user-submit when validation successful and model is collected
             *  * @tutorial
             * call `e.preventDefault()` to prevent dispatching `$submitEnd` & closing modal (if form in modal) */
            $submit: CustomEvent<SubmitDetails>;
            /** Fires when submit is end (after http-response);
             * @tutorial
             * call `e.preventDefault()` to prevent closing modal (if form in modal) */
            $submitEnd: CustomEvent<{
                success: boolean;
            }>;
        }
        interface Options {
            /** Actions that enabled on submit event; You can point several like: `goToError | collectChanged`
             * @defaultValue goToError | validateUntilFirst | reset | lockOnPending */
            submitActions: SubmitActions;
            /** Enable to store data in localStorage to prevent losing till submitted;
             * @defaultValue false
             * @tutorial Troubleshooting
             * * It doesn't save values that are complex objects. So `wup-select.$options.items = [{text: "N1",value: {id:1,name:'Nik'} }]` is skipped
             * * Point string-value if default storage-key doesn't fit: based on `url+control.names` @see{@link WUPFormElement.storageKey}
             * @defaultValue false */
            autoStore: boolean | string;
            /** Focus first possible element when it's appended to layout
             * @defaultValue false */
            autoFocus: boolean;
            /** Disallow edit/copy value; adds attr [disabled] for styling
             * @defaultValue false */
            disabled: boolean;
            /** Disallow copy value; adds attr [readonly] for styling
             * @defaultValue false */
            readOnly: boolean;
            /** Enable/disable browser-autocomplete; if control has no autocomplete option then it's inherited from form
             *  @defaultValue false */
            autoComplete: boolean;
        }
        interface JSXProps extends WUP.Base.OnlyNames<Options> {
            "w-submitActions"?: SubmitActions | number;
            "w-autoStore"?: boolean | string;
            "w-autoFocus"?: boolean | "";
            "w-autoComplete"?: boolean | "";
            /** @deprecated use [disabled] instead since related to CSS-styles */
            "w-disabled"?: boolean | "";
            disabled?: boolean | "";
            /** @deprecated use [disabled] instead since related to CSS-styles */
            "w-readonly"?: boolean | "";
            readonly?: boolean | "";
            /** @deprecated SyntheticEvent is not supported. Use ref.addEventListener('$change') instead */
            onChange?: never;
            /** @deprecated SyntheticEvent is not supported. Use ref.addEventListener('$willSubmit') instead */
            onWillSubmit?: never;
            /** @deprecated SyntheticEvent is not supported. Use ref.addEventListener('$submit') instead */
            onSubmit?: never;
        }
    }
    interface HTMLElementTagNameMap {
        [tagName]: WUPFormElement;
    }
}
declare module "react" {
    namespace JSX {
        interface IntrinsicElements {
            /** Wrapper of FormHTMLElement that collect values from controls
             *  @see {@link WUPFormElement} */
            [tagName]: WUP.Base.ReactHTML<WUPFormElement> & WUP.Form.JSXProps;
        }
    }
}
declare module "preact/jsx-runtime" {
    namespace JSX {
        interface HTMLAttributes<RefType> {
        }
        interface IntrinsicElements {
            /** Wrapper of FormHTMLElement that collect values from controls
             *  @see {@link WUPFormElement} */
            [tagName]: HTMLAttributes<WUPFormElement> & WUP.Form.JSXProps;
        }
    }
}
/** Wrapper of FormHTMLElement that collect values from controls
 * @see demo {@link https://yegorich555.github.io/web-ui-pack/controls}
 * @example
 *  // init form
 *  const form = document.createElement("wup-form");
 *  form.$options.autoComplete = false;
 *  form.$initModel = { email: "test-me@google.com" };
 *  form.addEventListener("$submit", (e) => console.warn(e.detail.model) );
 *  form.$onSubmit = async (e)=>{ await postHere(e.detail.model); } // equal to form.addEventListener
 *  // init control
 *  const el = document.createElement("wup-text");
 *  el.$options.name = "email";
 *  el.$options.validations = { required: true, email: true };
 *  form.appendChild(el);
 *  const btn = form.appendChild(document.createElement("button"));
 *  btn.textContent = "Submit";
 *  btn.type = "submit";
 *  document.body.appendChild(form);
 *  // or HTML
 *  <wup-form w-autocomplete w-autofocus>
 *    <wup-text w-name="email" />
 *    <button type="submit">Submit</submit>
 *  </wup-form>;
 * @tutorial Troubleshooting/rules:
 * * options like $initModel, $model overrides control.$initValue, control.$value (every control that matches by $options.name)
 * * In React ref-parent called after ref-children. So if you want to set control.$initValue over form.$initModel use empty setTimeout on ref-control
 * @example
 * <wup-form
      ref={(el) => {
        if (el) {
          el.$initModel = { email: "test-me@google.com" };
          el.$onSubmit = async (ev)=>{
             await postHere();
          }
        }
      }}
    >
      <wup-text
        ref={(el) => {
          if (el) {
            setTimeout(() => {
              el.$options.name = "email";
              el.$initValue = "";
            });
          }
        }}
        <button type="submit">Submit</button>
      />
  </wup-form>
 */
export default class WUPFormElement<Model extends Record<string, any> = any, TOptions extends WUP.Form.Options = WUP.Form.Options, Events extends WUP.Form.EventMap = WUP.Form.EventMap> extends WUPBaseElement<TOptions, Events> {
    #private;
    static get $styleRoot(): string;
    static get $style(): string;
    static get mappedAttributes(): Record<string, AttributeMap>;
    /** Find form related to control,register and apply initModel if initValue undefined */
    static $tryConnect(control: IBaseControl & HTMLElement): WUPFormElement | undefined;
    /** Map model to control-values */
    static $modelToControls<T extends Record<string, any>>(m: T | undefined, controls: IBaseControl[], prop: keyof Pick<IBaseControl, "$value" | "$initValue">): void;
    /** Collect model from control-values */
    static $modelFromControls<T>(prevModel: Partial<T>, controls: IBaseControl[], prop: keyof Pick<IBaseControl, "$value" | "$initValue">, isOnlyChanged?: boolean): Partial<T>;
    static get observedAttributes(): Array<string>;
    /** Default options - applied to every element. Change it to configure default behavior */
    static $defaults: WUP.Form.Options;
    /** Fires before $submit is happened; can be prevented via `e.preventDefault()` */
    $onWillSubmit?: (ev: WUP.Form.EventMap["$willSubmit"]) => void;
    /** Dispatched on submit
     * @tutorial
     * * need to return promise to lock form and show spinner on http-request
     * * call `e.preventDefault()` to prevent dispatching `$submitEnd` & closing modal (if form in modal) */
    $onSubmit?: (ev: WUP.Form.EventMap["$submit"]) => void | Promise<unknown>;
    /** Fires when submit is end (after http-response);
     * @tutorial
     * call `e.preventDefault()` to prevent closing modal (if form in modal) */
    $onSubmitEnd?: (ev: WUP.Form.EventMap["$submitEnd"]) => void;
    /** Dispatched on submit */
    /** All controls related to form */
    $controls: IBaseControl<any>[];
    /** Returns related to form controls with $options.name != null */
    get $controlsAttached(): IBaseControl<any>[];
    _model?: Partial<Model>;
    /** Model related to every control inside (mapped object via control.$options.name);
     *  @see {@link BaseControl.prototype.$value}
     * @tutorial rules
     * * `form.$model = { firstName: 'Hell' }` updates only control with $options.name==='firstName'
     * * `form.$model = { firstName: undefined }` reset only control with $options.name==='firstName' */
    get $model(): Partial<Model>;
    set $model(m: Partial<Model>);
    _initModel?: Partial<Model>;
    /** Default/init model related to every control inside;
     *  @see {@link BaseControl.prototype.$initValue} */
    get $initModel(): Partial<Model> | undefined;
    set $initModel(m: Partial<Model> | undefined);
    /** Pending state (spinner + lock form if SubmitActions.lockOnPending enabled) */
    get $isPending(): boolean;
    set $isPending(v: boolean);
    /** Returns true if all nested controls (with name) are valid */
    get $isValid(): boolean;
    /** Returns true if some of controls value is changed by user */
    get $isChanged(): boolean;
    /** Call it to manually trigger submit or better to use `gotSubmit` for handling events properly */
    $submit(): void;
    /** Validate all attached controls
     * @param tillFirstInvalid point `true` if need to find first invalid control; if skipped then will be defined from `$options.submitActions: SubmitActions.validateUntilFirst`
     * @param includeLocked collect & validate also hidden/readonly/disabled controls
     * @returns array of invalid controls (empty if all are valid) */
    $validate(tillFirstInvalid?: boolean, includeLocked?: boolean): Array<IBaseControl>;
    /** Called on every spin-render */
    renderSpin(target: HTMLElement): {
        dispose: () => void;
    };
    /** Change pending state */
    protected changePending(v: boolean): void;
    /** Called on submit before validation (to fire validation & $onSubmit if successful) */
    gotSubmit(e: KeyboardEvent | MouseEvent | null, submitter: HTMLElement): void;
    protected gotChanges(propsChanged: Array<keyof WUP.Form.Options> | null): void;
    protected gotReady(): void;
    protected connectedCallback(): void;
    /** Returns storage key based on url+control-names or `$options.autoStore` if `string` */
    get storageKey(): string;
    /** Get & parse value from storage according to option `autoStore`, $model */
    storageGet(): Partial<Model> | null;
    _preventStorageSave?: boolean;
    /** Save/remove model (only changes) to storage according to option `autoStore`, $model
     * @returns model saved to localStorage or Null if removed */
    storageSave(model?: null | Record<string, any>): Partial<Model> | null;
    protected disconnectedCallback(): void;
}
export {};
