import { windowScrollPosition } from '@slickgrid-universal/utils';
/***
 * Interactions, add basic behaviors to any element.
 * All the packages are written in pure vanilla JS and supports both mouse & touch events.
 * @module Interactions
 * @namespace Slick
 */
/**
 * Draggable Class, enables dragging functionality for any element for example cell & row selections.
 * Note that mouse/touch start is on the specified container element but all other events are on the document body.
 * code refs:
 *   https://betterprogramming.pub/perfecting-drag-and-drop-in-pure-vanilla-javascript-a761184b797a
 * available optional options:
 *   - containerElement: container DOM element, defaults to "document"
 *   - allowDragFrom: when defined, only allow dragging from an element that matches a specific query selector
 *   - allowDragFromClosest: when defined, only allow dragging from an element or its parent matching a specific .closest() query selector
 *   - onDragInit: drag initialized callback
 *   - onDragStart: drag started callback
 *   - onDrag: drag callback
 *   - onDragEnd: drag ended callback
 * @param {Object} options
 * @returns - Draggable instance which includes destroy method
 * @class Draggable
 */
export function Draggable(options) {
    let { containerElement } = options;
    const { onDragInit, onDragStart, onDrag, onDragEnd } = options;
    let element;
    let startX;
    let startY;
    let deltaX;
    let deltaY;
    let dragStarted;
    if (!containerElement) {
        containerElement = document.body;
    }
    let originaldd = {
        dragSource: containerElement,
        dragHandle: null,
    };
    function init() {
        if (containerElement) {
            containerElement.addEventListener('mousedown', userPressed);
            containerElement.addEventListener('touchstart', userPressed);
        }
    }
    function executeDragCallbackWhenDefined(callback, evt, dd) {
        if (typeof callback === 'function') {
            return callback(evt, dd);
        }
    }
    function destroy() {
        if (containerElement) {
            containerElement.removeEventListener('mousedown', userPressed);
            containerElement.removeEventListener('touchstart', userPressed);
        }
    }
    function userPressed(event) {
        element = event.target;
        const targetEvent = event?.touches?.[0] ?? event;
        const { target } = targetEvent;
        if (!options.allowDragFrom || (options.allowDragFrom && (element.matches(options.allowDragFrom)) || (options.allowDragFromClosest && element.closest(options.allowDragFromClosest)))) {
            originaldd.dragHandle = element;
            const winScrollPos = windowScrollPosition();
            startX = winScrollPos.left + targetEvent.clientX;
            startY = winScrollPos.top + targetEvent.clientY;
            deltaX = targetEvent.clientX - targetEvent.clientX;
            deltaY = targetEvent.clientY - targetEvent.clientY;
            originaldd = Object.assign(originaldd, { deltaX, deltaY, startX, startY, target });
            const result = executeDragCallbackWhenDefined(onDragInit, event, originaldd);
            if (result !== false) {
                document.body.addEventListener('mousemove', userMoved);
                document.body.addEventListener('touchmove', userMoved);
                document.body.addEventListener('mouseup', userReleased);
                document.body.addEventListener('touchend', userReleased);
                document.body.addEventListener('touchcancel', userReleased);
            }
        }
    }
    function userMoved(event) {
        const targetEvent = event?.touches?.[0] ?? event;
        deltaX = targetEvent.clientX - startX;
        deltaY = targetEvent.clientY - startY;
        const { target } = targetEvent;
        if (!dragStarted) {
            originaldd = Object.assign(originaldd, { deltaX, deltaY, startX, startY, target });
            executeDragCallbackWhenDefined(onDragStart, event, originaldd);
            dragStarted = true;
        }
        originaldd = Object.assign(originaldd, { deltaX, deltaY, startX, startY, target });
        executeDragCallbackWhenDefined(onDrag, event, originaldd);
    }
    function userReleased(event) {
        document.body.removeEventListener('mousemove', userMoved);
        document.body.removeEventListener('touchmove', userMoved);
        document.body.removeEventListener('mouseup', userReleased);
        document.body.removeEventListener('touchend', userReleased);
        document.body.removeEventListener('touchcancel', userReleased);
        // trigger a dragEnd event only after dragging started and stopped
        if (dragStarted) {
            const { target } = event;
            originaldd = Object.assign(originaldd, { target });
            executeDragCallbackWhenDefined(onDragEnd, event, originaldd);
            dragStarted = false;
        }
    }
    // initialize Slick.MouseWheel by attaching mousewheel event
    init();
    // public API
    return { destroy };
}
/**
 * MouseWheel Class, add mousewheel listeners and calculate delta values and return them in the callback function.
 * available optional options:
 *   - element: optional DOM element to attach mousewheel values, if undefined we'll attach it to the "window" object
 *   - onMouseWheel: mousewheel callback
 * @param {Object} options
 * @returns - MouseWheel instance which includes destroy method
 * @class MouseWheel
 */
export function MouseWheel(options) {
    const { element, onMouseWheel } = options;
    function destroy() {
        element.removeEventListener('wheel', wheelHandler);
        element.removeEventListener('mousewheel', wheelHandler);
    }
    function init() {
        element.addEventListener('wheel', wheelHandler);
        element.addEventListener('mousewheel', wheelHandler);
    }
    // copy over the same event handler code used in jquery.mousewheel
    function wheelHandler(event) {
        const orgEvent = event || window.event;
        let delta = 0;
        let deltaX = 0;
        let deltaY = 0;
        // Old school scrollwheel delta
        if (orgEvent.wheelDelta) {
            delta = orgEvent.wheelDelta / 120;
        }
        if (orgEvent.detail) {
            delta = -orgEvent.detail / 3;
        }
        // New school multidimensional scroll (touchpads) deltas
        deltaY = delta;
        // Gecko
        if (orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS) {
            deltaY = 0;
            deltaX = -1 * delta;
        }
        // WebKit
        if (orgEvent.wheelDeltaY !== undefined) {
            deltaY = orgEvent.wheelDeltaY / 120;
        }
        if (orgEvent.wheelDeltaX !== undefined) {
            deltaX = -1 * orgEvent.wheelDeltaX / 120;
        }
        if (typeof onMouseWheel === 'function') {
            onMouseWheel(event, delta, deltaX, deltaY);
        }
    }
    // initialize Slick.MouseWheel by attaching mousewheel event
    init();
    // public API
    return { destroy };
}
/**
 * Resizable Class, enables resize functionality for any element
 * Code mostly comes from these 2 resources:
 *   https://spin.atomicobject.com/2019/11/21/creating-a-resizable-html-element/
 *   https://htmldom.dev/make-a-resizable-element/
 * available optional options:
 *   - resizeableElement: resizable DOM element
 *   - resizeableHandleElement: resizable DOM element
 *   - onResizeStart: resize start callback
 *   - onResize: resizing callback
 *   - onResizeEnd: resize ended callback
 * @param {Object} options
 * @returns - Resizable instance which includes destroy method
 * @class Resizable
 */
export function Resizable(options) {
    const { resizeableElement, resizeableHandleElement, onResizeStart, onResize, onResizeEnd } = options;
    if (!resizeableHandleElement || typeof resizeableHandleElement.addEventListener !== 'function') {
        throw new Error('[SlickResizable] You did not provide a valid html element that will be used for the handle to resize.');
    }
    function init() {
        // add event listeners on the draggable element
        resizeableHandleElement.addEventListener('mousedown', resizeStartHandler);
        resizeableHandleElement.addEventListener('touchstart', resizeStartHandler);
    }
    function destroy() {
        if (typeof resizeableHandleElement?.removeEventListener === 'function') {
            resizeableHandleElement.removeEventListener('mousedown', resizeStartHandler);
            resizeableHandleElement.removeEventListener('touchstart', resizeStartHandler);
        }
    }
    function executeResizeCallbackWhenDefined(callback, e) {
        if (typeof callback === 'function') {
            return callback(e, { resizeableElement, resizeableHandleElement });
        }
    }
    function resizeStartHandler(e) {
        e.preventDefault();
        const event = e.touches ? e.changedTouches[0] : e;
        const result = executeResizeCallbackWhenDefined(onResizeStart, event);
        if (result !== false) {
            document.body.addEventListener('mousemove', resizingHandler);
            document.body.addEventListener('mouseup', resizeEndHandler);
            document.body.addEventListener('touchmove', resizingHandler);
            document.body.addEventListener('touchend', resizeEndHandler);
        }
    }
    function resizingHandler(e) {
        if (e.preventDefault && e.type !== 'touchmove') {
            e.preventDefault();
        }
        const event = (e.touches ? e.changedTouches[0] : e);
        if (typeof onResize === 'function') {
            onResize(event, { resizeableElement, resizeableHandleElement });
        }
    }
    /** Remove all mouse/touch handlers */
    function resizeEndHandler(e) {
        const event = e.touches ? e.changedTouches[0] : e;
        executeResizeCallbackWhenDefined(onResizeEnd, event);
        document.body.removeEventListener('mousemove', resizingHandler);
        document.body.removeEventListener('mouseup', resizeEndHandler);
        document.body.removeEventListener('touchmove', resizingHandler);
        document.body.removeEventListener('touchend', resizeEndHandler);
    }
    init();
    // public API
    return { destroy };
}
//# sourceMappingURL=slickInteractions.js.map