/*!
 * Custom elements for Nanopore-Digital Web applications
 */
import { g as getElement, d as writeTask, e as readTask, r as registerInstance, c as createEvent, a as Host } from './index-BM3Om9WE.js';
import { h } from './renderer-DpNDfhNy.js';
import { d as drag } from './drag-BLOEuv-q.js';
import { d as debounce } from './throttle-C93FMm2Z.js';
import { a as addListeners } from './events-ClRFmcmJ.js';

const sortableCss = ":host,*,*::before,*::after{box-sizing:border-box}[hidden]{display:none !important}@media (prefers-reduced-motion: reduce){:host,*,*::before,*::after{animation-duration:0.01ms !important;animation-iteration-count:1 !important;transition-duration:0.01ms !important;scroll-behavior:auto !important}}:host,*,*::before,*::after{box-sizing:border-box}[hidden]{display:none !important}@media (prefers-reduced-motion: reduce){:host,*,*::before,*::after{animation-duration:0.01ms !important;animation-iteration-count:1 !important;transition-duration:0.01ms !important;scroll-behavior:auto !important}}:host{position:relative;display:block}.sortable__live-region{clip-path:inset(50%);block-size:1px;overflow:hidden;position:absolute;white-space:nowrap;inline-size:1px}";

// Orientation map to limit dragging to horizontal or vertical.
const orientationMap = {
    horizontal: { x: 1, y: 0 },
    vertical: { x: 0, y: 1 },
};
let sortableIds = 0;
const Sortable = class {
    /// Public API & surface ///
    /** A css query selector which matches a set of elements that should be sortable. e.g. `<li>` or `<tr>` */
    itemSelector = 'li';
    handleItemSelectorChange() {
        this.refreshKeyboardHandles();
    }
    /** A css query selector which matches an element *within* the `itemSelector` element
     * which will act like a drag handle. **Note** For keyboard a11y it's recommended
     * to use `handleSelector` when possible. Alternatively, the `createKeyboardHandle` prop will be used */
    handleSelector;
    handleHandleSelectorChange() {
        this.refreshKeyboardHandles();
        this.attachMutationObserver();
    }
    /** When dragging between grouped collections of items, it is possible to drag all sortable items from one group to another.
     * At that point, you will not be able to drag items back to the original group.
     * A `dropzoneSelector` allows you to define a placeholder area where items can always be dropped.
     */
    dropzoneSelector;
    /** Accessibility helper text applied to handle controls */
    helperText = 'Press "Space" or "Enter" to enable element reordering and use the arrow keys to reorder items.' +
        'Press "Escape" to cancel reordering. Alternatively, use your mouse to drag / reorder.';
    /**
     * As a sortable element is interacted with, announcements are made to screen readers
     * - informing the user what is changing.
     * By default, the text from the element is extracted to describe the interacted with element.
     * Use this function to provide a more meaningful, concise item descriptor
     * @param el the element being interacted with
     * @returns a string, describing the element being interacted with
     */
    itemDescriptor = (el) => `"${el?.textContent.trim()}"`;
    /**
     * Used to generate a screen reader live update, informing the user of the current element being grabbed.
     * @param el the element that has been grabbed by the user
     * @returns a string, describing the element being grabbed
     */
    grabbedHelperText = (el) => `${this.itemDescriptor(el)} grabbed`;
    /**
     * Used to generate a screen reader live update, informing the user of the current element being dropped.
     * @param el the element that has been dropped by the user
     * @returns a string, describing the element that has been dropped
     */
    droppedHelperText = (el) => `${this.itemDescriptor(el)} dropped`;
    /**
     *  Used to generate a screen reader live update, informing the user when items have been reordered.
     * @param el the element that has been reordered by the user
     * @param elements the elements in the sortable list
     * @param position the new position of the dropped element
     * @returns a string, describing the element being dropped
     */
    reorderHelperText = (el, elements, position) => `The list has been reordered, ${this.itemDescriptor(el)} is now item ${position} of ${elements.length}`;
    /**
     * A function that should attach a keyboard accessible control.
     * When a `handleSelector` is not used - to allow keyboard accessibility -
     * `createKeyboardHandle` should render a focusable element.
     *
     * If your sortable item is itself focusable,
     * you must use this function to render a control *outside* of the focusable element.
     * @param _number
     * @param _element
     * @default () => <nano-icon-button></nano-icon-button>;
     * @returns a function that attaches a keyboard accessible handle control element.
     * The function itself *must* return the handle element.
     */
    createKeyboardHandle = (_number, _element) => {
        const handleTpl = /* html */ `
      <nano-icon-button
        slot="end"
        icon-name="light/arrows-up-down-left-right"
        class="nano-sortable__keyboard-handle visually-hidden"
        label="Re-order this item"
      ></nano-icon-button>`;
        const div = document.createElement('div');
        div.innerHTML = handleTpl;
        const handle = div.children[0];
        _element.append(handle);
        return handle;
    };
    handleCreateKeyboardHandleChange() {
        this.refreshKeyboardHandles();
    }
    /** use `sortableHostElement` to watch a sortable list without needing to wrap in `nano-sortable`.
     * Note that the `sortableHostElement` element css `position` will be set to `relative` */
    sortableHostElement;
    handleSortableHostElement(_newVal, oldVal) {
        if (oldVal)
            this.removeEventHandlers(oldVal);
        this.addEventHandlers();
        this.refreshKeyboardHandles();
        this.attachMutationObserver();
        if (this.sortableHostElement) {
            // sortable host must have position relative
            this.sortableHostElement.style.position = 'relative';
        }
    }
    /** Items being sorted will animate into place */
    animationEnabled = true;
    /** Use this class to change how the element looks whilst being sorted */
    draggedClass = 'nano-sortable__dragged';
    /** Use this class to change how the handle looks whilst being sorted */
    handleDraggedClass = 'nano-sortable__handle-dragged';
    /** Use this class to change how the element looks when acting as a placeholder */
    placeholderClass = 'nano-sortable__placeholder';
    /** Used in-conjunction with `dropzoneSelector`, applied when a dropzone is dragged over */
    dropzoneActiveClass = '';
    /** The css animation timing applied when `animationEnabled` is `true` */
    animationTiming = { duration: 200, easing: 'ease-out' };
    /** The axis to lock dragging elements to. `undefined` means any axis */
    orientation;
    /** Dragged items by default, will take whatever default css is applied in situ.
     * When sortable items' dimensions are dynamic (i.e they can shrink / grow via the space allowed) dragging items can look 'wrong'.
     * Apply `dragResize` to read & recreate the dragged items current dimensions when dragged.  */
    dragResize = false;
    /** Fired when an the order changes. Use this to update any underlying data model.
     * `event.preventDefault()` to prevent the element being moved; will revert to original position. */
    nanoOrderChange;
    /** Fired when a sortable element is picked-up; via keyboard, mouse or touch.
     * `event.preventDefault()` to prevent the element being picked-up. */
    nanoGrabbed;
    /** Fired when a sortable element is dropped after having been picked up. */
    nanoDropped;
    /** If sortable elements change dynamically, use this method to add handle controls to new elements */
    async refreshKeyboardHandles() {
        if (this.handleSelector) {
            if (this.keyboardHandleMap?.size) {
                this.keyboardHandleMap.clear();
            }
            this.sortableHost
                .querySelectorAll(this.handleSelector)
                .forEach((handle) => {
                if (!handle.getAttribute('aria-describedby'))
                    handle.setAttribute('aria-describedby', this.sortableId);
                const sortEle = handle.closest(this.itemSelector);
                if (sortEle)
                    this.keyboardHandleMap.set(handle, sortEle);
            });
            return;
        }
        if (this.keyboardHandleMap?.size) {
            this.keyboardHandleMap?.forEach((_ele, handle) => handle.remove());
            this.keyboardHandleMap.clear();
        }
        this.sortableHost
            .querySelectorAll(this.itemSelector)
            .forEach((ele, i) => {
            const handle = this.createKeyboardHandle(i, ele);
            if (!handle) {
                console.error('`createKeyboardHandle` *must* return the handle element it creates');
                return;
            }
            this.keyboardHandleMap.set(handle, ele);
            handle.setAttribute('aria-describedby', this.sortableId);
        });
    }
    get host() { return getElement(this); }
    dragRequestPending = false;
    draggedElement;
    draggedElementClone;
    draggedElementOrigin;
    sortableNodes = [];
    dropzoneNodes = [];
    animatedElements = [];
    animationPromise;
    keyboardHandleMap = new Map();
    sortableId = `nano-sortable-${sortableIds++}`;
    mouseDownTimer;
    mutationObserver;
    keyboardSortActive = false;
    ariaTextList = [];
    /**
     * Get instance of sortable host.
     * By default it is `nano-sortable` which can be overridden by property `sortableHostElement`
     */
    get sortableHost() {
        if (this.sortableHostElement)
            return this.sortableHostElement;
        return this.host;
    }
    /**
     * Queues aria messages which are then displayed in a 'live' region.
     * Messages are cleared after 10s
     * @param msg aria message to announce
     */
    addAriaMsg(msg) {
        this.ariaTextList = [...this.ariaTextList, msg];
        setTimeout(() => {
            const mIdx = this.ariaTextList.indexOf(msg);
            this.ariaTextList.splice(mIdx, 1);
            this.ariaTextList = [...this.ariaTextList];
        }, 10000);
    }
    /**
     * Try to stop text highlight whilst dragging
     * @param userSelect
     */
    updateUserSelectStyle(userSelect) {
        this.host.style.userSelect = userSelect;
    }
    /**
     * Fast and simple hit test to check whether the center of a node intersects with the rectangle of any of the
     * given targets. Returns an array of matches.
     * @param node
     * @param targets
     * @returns all the items that currently intersect with the target node
     */
    hitTest(node, targets) {
        const { left: l, top: t, width: w, height: h, } = node.getBoundingClientRect();
        const x = l + w / 2;
        const y = t + h / 2;
        return targets.filter((item) => {
            const { left, right, top, bottom } = item.getBoundingClientRect();
            return !(x < left || x > right || y < top || y > bottom);
        });
    }
    /**
     * Returns a boolean indicating whether the node is currently in an animation.
     * @param node
     * @returns whether a node is currently animating or not
     */
    isAnimating(node) {
        return this.animatedElements.indexOf(node) !== -1;
    }
    /**
     * Triggers a CSS animation on a node with the given dx and dy. Used following dom updates to make it appear as
     * if nodes animate from their old to their new position in the dom. */
    animateNode(node, dx = 0, dy = 0) {
        if (!node.animate) {
            return;
        }
        // keep a stack of currently animating nodes to exclude as drag & drop targets.
        this.animatedElements.push(node);
        node.style.willChange = 'transform';
        // animate from dx/dy (old node position) to none (new node position)
        writeTask(() => {
            this.animationPromise = new Promise((resolve) => {
                node
                    .animate([
                    { transform: `translate3d(${dx}px, ${dy}px, 0)` },
                    { transform: 'none' },
                ], this.animationTiming)
                    .addEventListener('finish', () => {
                    const index = this.animatedElements.indexOf(node);
                    node.style.willChange = '';
                    if (index !== -1) {
                        // splice out when done to unlock as a valid target
                        this.animatedElements.splice(index, 1);
                    }
                    resolve();
                    delete this.animationPromise;
                }, { once: true });
            });
        });
    }
    /**
     * Inserts node at target to update sibling sorting. If the node precedes the target, it is inserted after it;
     * If it follows the target, it is inserted before it. This ensures any node can be dragged from the very
     * beginning to the very end and vice versa. The animateNode function is called for all nodes that moved because
     * of this dom update */
    insertAtTarget(node, target) {
        if (!node || !target)
            return;
        let offsets = [];
        if (this.animationEnabled) {
            offsets = this.sortableNodes.map((item) => ({
                x: item.offsetLeft,
                y: item.offsetTop,
            }));
        }
        if (!node.isConnected || !target.isConnected)
            return;
        if (this.dropzoneNodes.indexOf(target) > -1) {
            target.append(node);
        }
        else {
            const nodeComparison = node.compareDocumentPosition(target);
            let position;
            if (nodeComparison & this.host.DOCUMENT_POSITION_FOLLOWING) {
                position =
                    target.parentNode === node.parentNode ? 'afterend' : 'beforebegin';
            }
            if (nodeComparison & this.host.DOCUMENT_POSITION_PRECEDING) {
                position =
                    target.parentNode === node.parentNode ? 'beforebegin' : 'afterend';
            }
            if (position)
                target.insertAdjacentElement(position, node);
        }
        if (this.animationEnabled) {
            this.sortableNodes.forEach((sortableNode, i) => {
                const { x, y } = offsets[i];
                const dx = x - sortableNode.offsetLeft;
                const dy = y - sortableNode.offsetTop;
                if (dx !== 0 || dy !== 0) {
                    this.animateNode(sortableNode, dx, dy);
                }
            });
        }
    }
    reset() {
        if (this.draggedElementClone !== undefined &&
            this.draggedElementClone.parentNode !== null) {
            this.draggedElementClone.parentNode.removeChild(this.draggedElementClone);
        }
        if (this.draggedElement &&
            this.draggedElement.parentNode &&
            this.draggedElementOrigin) {
            this.draggedElement.classList.remove(this.placeholderClass);
        }
        if (this.dropzoneActiveClass && this.dropzoneNodes.length) {
            this.dropzoneNodes.forEach((dze) => dze.classList.remove(this.dropzoneActiveClass));
        }
        delete this.draggedElementClone;
        delete this.draggedElement;
        this.dropzoneNodes = [];
        this.sortableNodes = [];
        this.animatedElements = [];
        this.dragRequestPending = false;
        this.updateUserSelectStyle('');
    }
    /**
     * Clones a given node to visually drag around. The original node is left in the same flow as its siblings.
     * Clone styles are added onto the style object directly, since the ::slotted()
     * selector can't universally target nodes that may be nested an unknown amount of shadow dom levels deep
     * @param node html node to clone
     * @returns the cloned html node
     */
    createClone(node) {
        const clone = node.cloneNode(true);
        if (node.id)
            clone.id = 'clone__' + clone.id;
        /**
         * Bugfix for table row sorting.
         * During dragging table rows shrink, so we manually set them width of original node.
         */
        Array.from(clone.children).forEach((nodeChild, index) => {
            const clonedNodeChild = nodeChild;
            const originalNodeChild = node.children.item(index);
            if (originalNodeChild) {
                clonedNodeChild.style.width = `${originalNodeChild.offsetWidth}px`;
            }
        });
        const { offsetLeft: x, offsetTop: y, offsetWidth: w, offsetHeight: h, } = node;
        Object.assign(clone.style, {
            position: 'absolute',
            left: `calc(${x}px - var(--grab-offset-x, 0px))`,
            top: `calc(${y}px - var(--grab-offset-y, 0px))`,
            height: this.dragResize ? `${h}px` : undefined,
            width: this.dragResize ? `${w}px` : undefined,
            willChange: 'transform,opacity',
        });
        clone.classList.add(this.draggedClass);
        return node.parentNode.appendChild(clone);
    }
    /// Event handlers ///
    removeEventHandlers(ele) {
        ele = ele || this.sortableHost;
        ele.removeEventListener('mousedown', this.handleTrack);
        ele.removeEventListener('touchstart', this.handleTrack);
        ele.removeEventListener('keydown', this.handleKeydown);
    }
    addEventHandlers(ele) {
        ele = ele || this.sortableHost;
        addListeners(ele, 'mousedown touchstart', this.handleTrack, {
            passive: true,
        });
        ele.addEventListener('keydown', this.handleKeydown);
    }
    /**
     * Watch for any changes in grab-able handle elements.
     */
    attachMutationObserver() {
        if (this.mutationObserver) {
            this.mutationObserver.disconnect();
            this.mutationObserver = undefined;
        }
        this.mutationObserver = new MutationObserver(() => {
            const currHandles = Array.from(this.keyboardHandleMap.values());
            const newHandles = Array.from(this.sortableHost.querySelectorAll(this.itemSelector));
            // simple test for equality
            if (currHandles.length !== newHandles.length ||
                !!newHandles.find((h) => !currHandles.includes(h))) {
                this.refreshKeyboardHandles();
            }
        });
        this.mutationObserver.observe(this.sortableHost, {
            subtree: true,
            childList: true,
        });
    }
    handleKeydown(e) {
        const targetElement = e.target;
        let foundHandle;
        let sortEle;
        if (this.handleSelector) {
            foundHandle = targetElement.closest(this.handleSelector);
            sortEle = targetElement.closest(this.itemSelector);
        }
        else {
            sortEle = this.keyboardHandleMap.get(targetElement);
            foundHandle = targetElement;
        }
        // have we found a handle and matching sort element from the keydown element
        if (!foundHandle || !sortEle)
            return;
        const activateSort = (isActive) => {
            this.keyboardSortActive = isActive;
            this.draggedElement = isActive ? sortEle : undefined;
            sortEle.classList.toggle(this.draggedClass, isActive);
            foundHandle.classList.toggle(this.handleDraggedClass, isActive);
            if (isActive) {
                this.addAriaMsg(this.grabbedHelperText(sortEle));
                document.addEventListener('mousedown', () => activateSort(false), {
                    once: true,
                });
            }
            else {
                this.addAriaMsg(this.droppedHelperText(sortEle));
            }
        };
        // start editing this element's order?
        if ([' ', 'Space', 'Enter'].includes(e.key)) {
            e.preventDefault();
            if (!this.keyboardSortActive) {
                // grabbed the element
                activateSort(true);
                this.sortableNodes =
                    Array.from(this.sortableHost.querySelectorAll(this.itemSelector)) ||
                        [];
                const nanoGrabbedEv = this.nanoGrabbed.emit({
                    element: sortEle,
                    index: this.sortableNodes.indexOf(sortEle),
                });
                if (nanoGrabbedEv.defaultPrevented) {
                    activateSort(false);
                    return;
                }
            }
            else {
                // dropped the element
                activateSort(false);
                this.nanoDropped.emit({ element: sortEle });
            }
            return;
        }
        if (!this.keyboardSortActive)
            return;
        // Tabbing away from this handle - deactivate
        if (['Escape', 'Tab'].includes(e.key))
            activateSort(false);
        let moveKeys = ['Home', 'End'];
        if (!this.orientation || this.orientation === 'horizontal')
            moveKeys = [...moveKeys, 'ArrowRight', 'ArrowLeft'];
        if (!this.orientation || this.orientation === 'vertical')
            moveKeys = [...moveKeys, 'ArrowUp', 'ArrowDown'];
        if (!moveKeys.includes(e.key))
            return;
        // move the element with the keyboard
        e.preventDefault();
        /** Collect all elements that have drag positions.
         * Both sortable elements and 'dropzones' (placeholders where items can always be placed) */
        this.sortableNodes =
            Array.from(this.sortableHost.querySelectorAll(this.itemSelector)) || [];
        this.dropzoneNodes =
            Array.from(this.sortableHost.querySelectorAll(this.dropzoneSelector)) ||
                [];
        const currIdx = this.sortableNodes.indexOf(this.draggedElement);
        let curDzIdx = -1;
        if (this.dropzoneNodes.length) {
            const curDropzone = this.draggedElement.closest(this.dropzoneSelector);
            curDzIdx = this.dropzoneNodes.indexOf(curDropzone);
            curDzIdx = curDzIdx > -1 ? curDzIdx : -1;
        }
        /** If we don't have a next / prev sortable element in our list, test to see if there's a dropzone instead */
        const prevEle = currIdx - 1 < 0 && curDzIdx > -1
            ? this.dropzoneNodes[curDzIdx - 1]
            : this.sortableNodes[currIdx - 1];
        const nextEle = currIdx + 1 === this.sortableNodes.length && curDzIdx > -1
            ? this.dropzoneNodes[curDzIdx + 1]
            : this.sortableNodes[currIdx + 1];
        if (e.key === 'Home') {
            this.insertAtTarget(this.draggedElement, this.sortableNodes[0]);
        }
        if (e.key === 'End') {
            this.insertAtTarget(this.draggedElement, this.sortableNodes[this.sortableNodes.length - 1]);
        }
        if (['ArrowRight', 'ArrowDown'].includes(e.key)) {
            this.insertAtTarget(this.draggedElement, nextEle);
        }
        if (['ArrowLeft', 'ArrowUp'].includes(e.key)) {
            this.insertAtTarget(this.draggedElement, prevEle);
        }
        this.finishOrder();
        this.draggedElement = sortEle;
        const focus = () => {
            requestAnimationFrame(() => {
                if (typeof foundHandle['setFocus'] === 'function')
                    foundHandle.setFocus();
                else
                    foundHandle.focus();
            });
        };
        // replace focus to handle so we don't 'drop' the element
        if (this.animationPromise)
            this.animationPromise.then(() => focus());
        else
            focus();
    }
    /** Tracks a pointer from touchstart/mousedown to touchend/mouseup. */
    handleTrack(e) {
        // ignore if we've started dragging or right click
        if (this.dragRequestPending || (e.button && e.button !== 1))
            return;
        // ignore touchmove events (namespace it so we can remove it)
        const handleMove = (e) => {
            if (e.cancelable)
                e.preventDefault();
            clearTimeout(this.mouseDownTimer);
        };
        // remove temporary events and reset timer
        const resetTrack = () => {
            clearTimeout(this.mouseDownTimer);
            document.removeEventListener('touchmove', handleMove);
            document.removeEventListener('contextmenu', this.finishOrder);
        };
        resetTrack();
        // start a timer - only start dragging if pressed for 150ms
        this.mouseDownTimer = window?.setTimeout(() => {
            // ignore if this is not a valid drag target
            if (!this.trackStart(e))
                return;
            // ignore touchmove events (they scroll the screen)
            document.addEventListener('touchmove', handleMove, {
                passive: false,
            });
            // cancel if user presses and holds (without move) for context menu
            document.addEventListener('contextmenu', this.finishOrder);
            this.addAriaMsg(this.grabbedHelperText(this.draggedElement));
            drag(this.sortableHost, {
                initialEvent: e,
                relative: true,
                onMove: (x, y) => {
                    this.trackMove(x, y);
                },
                onStop: () => {
                    this.nanoDropped.emit({ element: this.draggedElement });
                    resetTrack();
                    const didStop = () => {
                        this.addAriaMsg(this.droppedHelperText(this.draggedElement));
                        requestAnimationFrame(() => this.finishOrder());
                    };
                    if (this.animationPromise) {
                        this.animationPromise.then(() => didStop());
                    }
                    else
                        didStop();
                },
            });
        }, 150);
        // reset track timer & cancel DnD on mouseup / touchend / touchmove
        addListeners(document, 'mouseup touchend touchmove', () => clearTimeout(this.mouseDownTimer), { once: true });
    }
    /**
     * Initialized a drag and drop sequence if a child node was clicked that matches the itemSelector property.
     * OR If a handleSelector is defined, a node matching this selector must be clicked instead
     * @returns boolean - whether tracking for this event should continue or not
     */
    trackStart(e) {
        const targetElement = e.target;
        let targetHandle;
        // If we have a handle set, return now if not being pressed
        if (this.handleSelector) {
            targetHandle = targetElement.closest(this.handleSelector);
            if (!targetHandle)
                return;
            targetHandle.classList.add(this.handleDraggedClass);
        }
        // Check that we've found the target element via itemSelector
        const node = targetElement.closest(this.itemSelector);
        if (!node)
            return false;
        this.sortableNodes =
            Array.from(this.sortableHost.querySelectorAll(this.itemSelector)) || [];
        const nanoGrabbedEv = this.nanoGrabbed.emit({
            element: node,
            index: this.sortableNodes.indexOf(node),
        });
        if (nanoGrabbedEv.defaultPrevented)
            return false;
        // Element found... start everything
        e.preventDefault();
        this.updateUserSelectStyle('none');
        this.dragRequestPending = true;
        this.draggedElement = node;
        this.dropzoneNodes =
            Array.from(this.sortableHost.querySelectorAll(this.dropzoneSelector)) ||
                [];
        this.draggedElementClone = this.createClone(node);
        this.draggedElementOrigin = node.nextSibling;
        this.animatedElements = [];
        this.draggedElement.classList.add(this.placeholderClass);
        return true;
    }
    /** Ends re-ordering */
    finishOrder = () => {
        if (!this.draggedElement)
            return;
        const updated = Array.from(this.sortableHost.querySelectorAll(this.itemSelector)).filter((ele) => ele !== this.draggedElementClone);
        const originalIndex = this.sortableNodes.indexOf(this.draggedElement);
        const targetIndex = updated.indexOf(this.draggedElement);
        if (this.handleSelector) {
            const targetHandle = this.draggedElement.querySelector(this.handleSelector);
            targetHandle.classList.remove(this.handleDraggedClass);
        }
        if (originalIndex !== targetIndex) {
            const orderChangeEv = this.nanoOrderChange.emit({
                element: this.draggedElement,
                originalIndex,
                targetIndex,
            });
            if (orderChangeEv.defaultPrevented) {
                /** Event was prevented, wait a moment to send element back to whence it came - gives a nicer visual queue */
                this.animationPromise = new Promise((resolve) => {
                    setTimeout(() => {
                        this.insertAtTarget(this.draggedElement, updated[originalIndex]);
                        this.reset();
                        this.dragRequestPending = false;
                        resolve();
                    }, 200);
                });
                return;
            }
            this.addAriaMsg(this.reorderHelperText(this.draggedElement, updated, targetIndex + 1));
        }
        this.reset();
        this.dragRequestPending = false;
    };
    /**
     * Moves the active node's clone to follow the pointer. The node that the clone intersects with (via hitTest) is
     * the insert point for updated sorting */
    trackMove = (x, y) => {
        if (!this.draggedElementClone) {
            return;
        }
        if (this.orientation) {
            x = x * orientationMap[this.orientation].x;
            y = y * orientationMap[this.orientation].y;
        }
        writeTask(() => {
            Object.assign(this.draggedElementClone.style, {
                transform: `translate3d(${x}px, ${y}px, 0)`,
            });
        });
        let target = this.hitTest(this.draggedElementClone, this.sortableNodes)[0];
        let activeDropzone;
        if (this.dropzoneSelector && this.dropzoneActiveClass) {
            readTask(() => {
                activeDropzone = this.draggedElement.closest(this.dropzoneSelector);
                writeTask(() => {
                    this.dropzoneNodes
                        .filter((dze) => dze !== activeDropzone)
                        .forEach((dze) => dze.classList.remove(this.dropzoneActiveClass));
                    activeDropzone.classList.add(this.dropzoneActiveClass);
                });
            });
        }
        // didn't find a target - let's test to see if we can use a drop-zone instead
        if (!target && this.dropzoneNodes.length) {
            target = this.hitTest(this.draggedElementClone, this.dropzoneNodes)[0];
            if (this.draggedElement.closest(this.dropzoneSelector) === target)
                return;
        }
        if (
        // if clone intersects with a valid target,
        target &&
            // other than its own origin,
            target !== this.draggedElement &&
            // and the target isn't currently animating, which causes false hit tests,
            !this.isAnimating(target)) {
            this.insertAtTarget(this.draggedElement, target);
        }
    };
    /// Component lifecycle ///
    constructor(hostRef) {
        registerInstance(this, hostRef);
        this.nanoOrderChange = createEvent(this, "nanoOrderChange", 7);
        this.nanoGrabbed = createEvent(this, "nanoGrabbed", 7);
        this.nanoDropped = createEvent(this, "nanoDropped", 7);
        this.handleTrack = this.handleTrack.bind(this);
        this.handleKeydown = this.handleKeydown.bind(this);
        this.refreshKeyboardHandles = this.refreshKeyboardHandles.bind(this);
        this.refreshKeyboardHandles = debounce(this.refreshKeyboardHandles, 500);
    }
    connectedCallback() {
        this.addEventHandlers();
        this.refreshKeyboardHandles();
        this.attachMutationObserver();
        if (!this.host.querySelector(`#${this.sortableId}`)) {
            // inject a light-dom / a11y description that we can connect sortable items to
            this.host.insertAdjacentHTML('beforeend', `<div class="visually-hidden" id="${this.sortableId}">${this.helperText}</div>`);
        }
    }
    disconnectedCallback() {
        this.removeEventHandlers();
        this.host.querySelector(`#${this.sortableId}`)?.remove();
        if (this.mutationObserver) {
            this.mutationObserver.disconnect();
            this.mutationObserver = undefined;
        }
    }
    render() {
        return (h(Host, { key: '845be657a2bbfb42718ee9e8d4672ff31756fcc3', class: "nano-sortable" }, h("div", { key: '9c603a11407cb62ffe50bb6cfd89b47994df21e9', class: "sortable__live-region", "aria-live": "polite", "aria-relevant": "additions", "aria-atomic": "true", role: "log", part: "announcements" }, this.ariaTextList.map((str) => (h("div", null, str)))), h("slot", { key: '36a5e7111ed26a7714206c449ffc7be1a46337d1' })));
    }
    static get watchers() { return {
        "itemSelector": ["handleItemSelectorChange"],
        "handleSelector": ["handleHandleSelectorChange"],
        "createKeyboardHandle": ["handleCreateKeyboardHandleChange"],
        "sortableHostElement": ["handleSortableHostElement"]
    }; }
};
Sortable.style = sortableCss;

export { Sortable as nano_sortable };
