import * as i0 from '@angular/core';
import { OnChanges, OnDestroy, EventEmitter, ElementRef, SimpleChange, ModuleWithProviders } from '@angular/core';
import { Drake, DragulaOptions as DragulaOptions$1 } from 'dragula';
import { Observable } from 'rxjs';

interface DrakeWithModels extends Drake {
    models?: any[][];
    on(event: 'drag', listener: (el: Element, source: Element) => void): Drake;
    on(event: 'dragend', listener: (el: Element) => void): Drake;
    on(event: 'drop', listener: (el: Element, target: Element, source: Element, sibling: Element) => void): Drake;
    on(event: 'cancel' | 'remove' | 'shadow' | 'over' | 'out', listener: (el: Element, container: Element, source: Element) => void): Drake;
    on(event: 'cloned', listener: (clone: Element, original: Element, type: 'mirror' | 'copy') => void): Drake;
    on(event: 'dropModel', listener: ([el, target, source, sibling, item, sourceModel, targetModel, sourceIndex, targetIndex,]: [
        Element,
        Element,
        Element,
        Element,
        any,
        any[],
        any[],
        number,
        number
    ]) => void): Drake;
    on(event: 'removeModel', listener: ([el, container, source, item, sourceModel, sourceIndex]: [
        Element,
        Element,
        Element,
        any,
        any[],
        number
    ]) => void): Drake;
}

interface DragulaOptions<T = any> extends Omit<DragulaOptions$1, 'copySortSource'> {
    copySortSource?: boolean | ((el: Element, source: Element) => boolean);
    /** You must provide this if you are using `copy` with `[dragulaModel]`. It
     *  is responsible for cloning a model item. Your implementation should
     *  ensure `x !== copyItem(x)` -- so you must create a *new* object.
     **/
    copyItem?: (item: T) => T;
}

declare class Group {
    name: string;
    drake: DrakeWithModels;
    options: DragulaOptions;
    initEvents: boolean;
    constructor(name: string, drake: DrakeWithModels, options: DragulaOptions);
}

declare const dragula: (containers?: any, options?: any) => any;
type DrakeBuilder = (containers: any[], options: DragulaOptions) => DrakeWithModels;
declare class DrakeFactory {
    build: DrakeBuilder;
    constructor(build?: DrakeBuilder);
}

declare class DragulaService {
    private drakeFactory;
    private groups;
    private dispatch$;
    private elContainerSource;
    cancel: (groupName?: string) => Observable<{
        name: string;
        el: Element;
        container: Element;
        source: Element;
    }>;
    remove: (groupName?: string) => Observable<{
        name: string;
        el: Element;
        container: Element;
        source: Element;
    }>;
    shadow: (groupName?: string) => Observable<{
        name: string;
        el: Element;
        container: Element;
        source: Element;
    }>;
    over: (groupName?: string) => Observable<{
        name: string;
        el: Element;
        container: Element;
        source: Element;
    }>;
    out: (groupName?: string) => Observable<{
        name: string;
        el: Element;
        container: Element;
        source: Element;
    }>;
    drag: (groupName?: string) => Observable<{
        name: string;
        el: Element;
        source: Element;
    }>;
    dragend: (groupName?: string) => Observable<{
        name: string;
        el: Element;
    }>;
    drop: (groupName?: string) => Observable<{
        name: string;
        el: Element;
        target: Element;
        source: Element;
        sibling: Element;
    }>;
    cloned: (groupName?: string) => Observable<{
        name: string;
        clone: Element;
        original: Element;
        cloneType: "mirror" | "copy";
    }>;
    dropModel: <T = any>(groupName?: string) => Observable<{
        name: string;
        el: Element;
        target: Element;
        source: Element;
        sibling: Element;
        item: T;
        sourceModel: T[];
        targetModel: T[];
        sourceIndex: number;
        targetIndex: number;
    }>;
    removeModel: <T = any>(groupName?: string) => Observable<{
        name: string;
        el: Element;
        container: Element;
        source: Element;
        item: T;
        sourceModel: T[];
        sourceIndex: number;
    }>;
    constructor(drakeFactory: DrakeFactory);
    /** Public mainly for testing purposes. Prefer `createGroup()`. */
    add(group: Group): Group;
    find(name: string): Group;
    destroy(name: string): void;
    /**
     * Creates a group with the specified name and options.
     *
     * Note: formerly known as `setOptions`
     */
    createGroup<T = any>(name: string, options: DragulaOptions<T>): Group;
    private handleModels;
    private setupEvents;
    private domIndexOf;
    static ɵfac: i0.ɵɵFactoryDeclaration<DragulaService, [{ optional: true; }]>;
    static ɵprov: i0.ɵɵInjectableDeclaration<any>;
}

declare class DragulaDirective implements OnChanges, OnDestroy {
    private el;
    private dragulaService;
    dragula?: string;
    dragulaModel?: any[];
    dragulaModelChange: EventEmitter<any[]>;
    private subs?;
    private get container();
    private group?;
    constructor(el: ElementRef, dragulaService: DragulaService);
    ngOnChanges(changes: {
        dragula?: SimpleChange;
        dragulaModel?: SimpleChange;
    }): void;
    /**
     * drake.models[i] belongs to drake.containers[i], so the model is always stored at this
     * container's index, including `undefined` while the container has no model (e.g. data still
     * loading). drake.models stays undefined until some container in the group has a model.
     */
    private setModel;
    setup(): void;
    subscribe(name: string): void;
    teardown(groupName: string): void;
    ngOnDestroy(): void;
    static ɵfac: i0.ɵɵFactoryDeclaration<DragulaDirective, never>;
    static ɵdir: i0.ɵɵDirectiveDeclaration<DragulaDirective, "[dragula]", never, { "dragula": { "alias": "dragula"; "required": false; }; "dragulaModel": { "alias": "dragulaModel"; "required": false; }; }, { "dragulaModelChange": "dragulaModelChange"; }, never, never, false, never>;
}

declare class DragulaModule {
    static forRoot(): ModuleWithProviders<DragulaModule>;
    static ɵfac: i0.ɵɵFactoryDeclaration<DragulaModule, never>;
    static ɵmod: i0.ɵɵNgModuleDeclaration<DragulaModule, [typeof DragulaDirective], never, [typeof DragulaDirective]>;
    static ɵinj: i0.ɵɵInjectorDeclaration<DragulaModule>;
}

declare enum EventTypes {
    Cancel = "cancel",
    Cloned = "cloned",
    Drag = "drag",
    DragEnd = "dragend",
    Drop = "drop",
    Out = "out",
    Over = "over",
    Remove = "remove",
    Shadow = "shadow",
    DropModel = "dropModel",
    RemoveModel = "removeModel"
}

declare const MockDrakeFactory: DrakeFactory;
/** You can use MockDrake to simulate Drake events.
 *
 * The three methods that actually do anything are `on(event, listener)`,
 * `destroy()`, and a new method, `emit()`. Use `emit()` to manually emit Drake
 * events, and if you injected MockDrake properly with MockDrakeFactory or
 * mocked the DragulaService.find() method, then you can make ng2-dragula think
 * drags and drops are happening.
 *
 * Caveats:
 *
 * 1. YOU MUST MAKE THE DOM CHANGES YOURSELF.
 * 2. REPEAT: YOU MUST MAKE THE DOM CHANGES YOURSELF.
 *    That means `source.removeChild(el)`, and `target.insertBefore(el)`.
 * 3. None of the other methods do anything.
 *    That's ok, because ng2-dragula doesn't use them.
 */
declare class MockDrake implements DrakeWithModels {
    containers: Element[];
    options: DragulaOptions;
    models?: any[][] | undefined;
    private emitter$;
    private subs;
    /**
     * @param containers A list of container elements.
     * @param options These will NOT be used. At all.
     * @param models Nonstandard, but useful for testing using `new MockDrake()` directly.
     *               Note, default value is undefined, like a real Drake. Don't change that.
     */
    constructor(containers?: Element[], options?: DragulaOptions, models?: any[][] | undefined);
    on(event: 'drag', listener: (el: Element, source: Element) => void): Drake;
    on(event: 'dragend', listener: (el: Element) => void): Drake;
    on(event: 'drop', listener: (el: Element, target: Element, source: Element, sibling: Element) => void): Drake;
    on(event: 'cancel' | 'remove' | 'shadow' | 'over' | 'out', listener: (el: Element, container: Element, source: Element) => void): Drake;
    on(event: 'cloned', listener: (clone: Element, original: Element, type: 'copy' | 'mirror') => void): Drake;
    on(event: 'dropModel', listener: ([el, target, source, sibling, item, sourceModel, targetModel, sourceIndex, targetIndex,]: [Element, Element, Element, Element, any, any[], any[], number, number]) => void): Drake;
    on(event: 'removeModel', listener: ([el, container, source, item, sourceModel, sourceIndex]: [Element, Element, Element, any, any[], number]) => void): Drake;
    dragging: boolean;
    start(item: Element): any;
    end(): any;
    cancel(revert: boolean): any;
    cancel(): any;
    canMove(item: Element): boolean;
    remove(): any;
    destroy(): any;
    /**
     * This is the most useful method. You can use it to manually fire events that would normally
     * be fired by a real drake.
     *
     * You're likely most interested in firing `drag`, `remove` and `drop`, the three events
     * DragulaService uses to implement [dragulaModel].
     *
     * See https://github.com/bevacqua/dragula#drakeon-events for what you should emit (and in what order).
     *
     * (Note also, firing dropModel and removeModel won't work. You would have to mock DragulaService for that.)
     */
    emit(eventType: EventTypes, ...args: any[]): void;
}

export { DragulaDirective, DragulaModule, DragulaService, DrakeFactory, EventTypes, Group, MockDrake, MockDrakeFactory, dragula };
export type { DragulaOptions, DrakeWithModels };
