/*!
 * Custom elements for Nanopore-Digital Web applications
 */
'use strict';

var index = require('./index-DGttnXif.js');
var renderer = require('./renderer-jVpDRMfV.js');
var drag = require('./drag-B5XXLJW9.js');
var math = require('./math-DIjJ3V87.js');
var throttle = require('./throttle-CrRDOkln.js');

const splitPaneCss = ":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{--divider-width:4px;--divider-hit-area:14px;--min:0%;--max:100%;--background-color:var(--nano-color-neutral-300);--content-color:var(--nano-color-neutral-1000);display:grid}.start,.end{overflow:hidden}.divider{flex:0 0 var(--divider-width);display:flex;position:relative;align-items:center;justify-content:center;background-color:var(--background-color);color:var(--content-color);z-index:1;font-size:0.8rem}.divider:focus{outline:none}:host(:not([disabled])) .divider:focus-visible{background-color:var(--nano-focus-ring-color)}:host([disabled]) .divider{cursor:not-allowed}:host(:not([vertical],[disabled])) .divider{cursor:col-resize}:host(:not([vertical])) .divider::after{display:flex;content:\"\";position:absolute;block-size:100%;inset-inline-start:calc(var(--divider-hit-area) / -2 + var(--divider-width) / 2);inline-size:var(--divider-hit-area)}:host([vertical]){flex-direction:column}:host([vertical]:not([disabled])) .divider{cursor:row-resize}:host([vertical]) .divider::after{content:\"\";position:absolute;inline-size:100%;inset-block-start:calc(var(--divider-hit-area) / -2 + var(--divider-width) / 2);block-size:var(--divider-hit-area)}";

const SplitPane = class {
    get host() { return index.getElement(this); }
    cachedPositionInPixels;
    ro;
    isAnimating = false;
    didLoad = false;
    get size() {
        const { width, height } = this.host.getBoundingClientRect();
        return this.vertical ? height : width;
    }
    get shouldAnimate() {
        return this.didLoad && this._shouldAnimate;
    }
    set shouldAnimate(sa) {
        this._shouldAnimate = sa;
    }
    _shouldAnimate = true;
    constructor(hostRef) {
        index.registerInstance(this, hostRef);
        this.nanoReposition = index.createEvent(this, "nanoReposition", 7);
        this.nanoDragging = index.createEvent(this, "nanoDragging", 7);
        this.handlePositionChange = throttle.debounce(this.handlePositionChange.bind(this), 100);
    }
    /**
     * The current position of the divider from the primary panel's edge
     * as a percentage 0-100. Defaults to 50% of the container's initial size.
     */
    get position() {
        return this._position;
    }
    set position(pos) {
        // override too high / low
        pos = Math.min(Math.max(pos, 0), 100);
        if (isNaN(pos) || pos === this._position)
            return;
        if (this.shouldAnimate &&
            this.animationDuration > 0 &&
            !isNaN(this.position)) {
            this.animatePosition(pos);
            return;
        }
        this._position = pos;
    }
    _position;
    handlePositionChange() {
        this.cachedPositionInPixels = this.percentageToPixels(this.position);
        this.positionInPixels = this.percentageToPixels(this.position);
        this.nanoReposition.emit();
    }
    /**
     * The current position of the divider from the primary panel's edge in pixels.
     */
    positionInPixels;
    handlePositionInPixelsChange() {
        this.position = this.pixelsToPercentage(this.positionInPixels);
    }
    /** Draws the split panel in a vertical orientation with the start and end panels stacked. */
    vertical = false;
    /** Disables resizing. Note that the position may still change as a result of resizing the host element. */
    disabled = false;
    /**
     * If no primary panel is designated, both panels will resize proportionally when the host element is resized. If a
     * primary panel is designated, it will maintain its size and the other panel will grow or shrink as needed when the
     * host element is resized.
     */
    primary;
    /**
     * One or more space-separated values at which the divider should snap. Values can be in pixels or percentages, e.g.
     * `"100px 50%"`.
     */
    snap;
    /** How close the divider must be to a snap point until snapping occurs. */
    snapThreshold = 12;
    /** When changing `position` via property, the pane will animate into position. Make `animationDuration` 0 to disable. */
    animationDuration = 0.6;
    /** Emitted when the divider's position changes. */
    nanoReposition;
    /** Emitted when the divider is being dragged. */
    nanoDragging;
    /**
     * Utility to convert % to pixels
     * @param value to convert
     * @returns converted value as pixels
     */
    async getPercentageToPixels(value) {
        return this.percentageToPixels(value);
    }
    /**
     * Utility to convert pixels to % (`position`)
     * @param value to convert
     * @returns converted value as a %
     */
    async getPixelsToPercentage(value) {
        return this.pixelsToPercentage(value);
    }
    // Private logic
    animatePosition(end) {
        if (this.isAnimating)
            return;
        const duration = this.animationDuration; // seconds
        const fps = 60;
        const start = this.position;
        const distance = end - start;
        let position = start;
        let time = 0;
        function easeInOutQuad(t, s, e, d) {
            if ((t /= d / 2) < 1)
                return (e / 2) * t * t + s;
            else
                return (-e / 2) * (--t * (t - 2) - 1) + s;
        }
        const go = () => {
            time += 1 / fps;
            position = easeInOutQuad(time, start, distance, duration);
            if ((end > start && position >= end) ||
                (end < start && position <= end)) {
                this.position = end;
                this.shouldAnimate = true;
                this.isAnimating = false;
                return;
            }
            this.position = position;
            requestAnimationFrame(go);
        };
        this.shouldAnimate = false;
        this.isAnimating = true;
        requestAnimationFrame(go);
    }
    percentageToPixels(value) {
        return this.size * (value / 100);
    }
    pixelsToPercentage(value) {
        return (value / this.size) * 100;
    }
    attachRO() {
        if (!window['ResizeObserver'])
            return;
        this.detachRO();
        this.ro = new ResizeObserver(() => this.handleResize());
        this.ro.observe(this.host);
    }
    detachRO() {
        if (!this.ro)
            return;
        this.ro.unobserve(this.host);
        this.ro = undefined;
    }
    // Event handlers
    handleDrag = (e) => {
        if (this.disabled) {
            return;
        }
        // Prevent text selection when dragging
        e.preventDefault();
        drag.drag(this.host, {
            onMove: (x, y) => {
                let newPositionInPixels = this.vertical ? y : x;
                this.nanoDragging.emit(newPositionInPixels);
                // Flip for end panels
                if (this.primary === 'end') {
                    newPositionInPixels = this.size - newPositionInPixels;
                }
                // Check snap points
                if (this.snap) {
                    const snaps = this.snap.split(' ');
                    snaps.forEach((value) => {
                        let snapPoint;
                        if (value.endsWith('%')) {
                            snapPoint = this.size * (parseFloat(value) / 100);
                        }
                        else {
                            snapPoint = parseFloat(value);
                        }
                        if (newPositionInPixels >= snapPoint - this.snapThreshold &&
                            newPositionInPixels <= snapPoint + this.snapThreshold) {
                            newPositionInPixels = snapPoint;
                        }
                    });
                }
                this.shouldAnimate = false;
                this.position = math.clamp(this.pixelsToPercentage(newPositionInPixels), 0, 100);
                requestAnimationFrame(() => (this.shouldAnimate = true));
            },
        });
    };
    handleKeyDown = (event) => {
        if (this.disabled) {
            return;
        }
        if ([
            'ArrowLeft',
            'ArrowRight',
            'ArrowUp',
            'ArrowDown',
            'Home',
            'End',
        ].includes(event.key)) {
            let newPosition = this.position;
            const incr = (event.shiftKey ? 10 : 1) * (this.primary === 'end' ? -1 : 1);
            event.preventDefault();
            if ((event.key === 'ArrowLeft' && !this.vertical) ||
                (event.key === 'ArrowUp' && this.vertical)) {
                newPosition -= incr;
            }
            if ((event.key === 'ArrowRight' && !this.vertical) ||
                (event.key === 'ArrowDown' && this.vertical)) {
                newPosition += incr;
            }
            if (event.key === 'Home') {
                newPosition = this.primary === 'end' ? 100 : 0;
            }
            if (event.key === 'End') {
                newPosition = this.primary === 'end' ? 0 : 100;
            }
            this.shouldAnimate = false;
            this.position = math.clamp(newPosition, 0, 100);
            requestAnimationFrame(() => (this.shouldAnimate = true));
        }
    };
    handleResize = () => {
        if (!this.didLoad || this.isAnimating)
            return;
        // Resize when a primary panel is set
        if (this.primary) {
            this.shouldAnimate = false;
            this.position = this.pixelsToPercentage(this.cachedPositionInPixels);
            requestAnimationFrame(() => (this.shouldAnimate = true));
        }
    };
    componentDidLoad() {
        if (this.positionInPixels)
            this.handlePositionInPixelsChange();
        setTimeout(() => (this.didLoad = true));
    }
    connectedCallback() {
        this.cachedPositionInPixels = this.percentageToPixels(this.position);
        this.attachRO();
    }
    disconnectedCallback() {
        this.detachRO();
    }
    componentDidRender() {
        // bit hacky ... because we use getter / setter for position, there's a render *before* `position` is passed in via attribute
        // if we set a default position in the class, this causes the divider to jump (from default to user set position)
        // so - wait a render, see if there's a position passed in via attribute, *then* set default if not
        requestAnimationFrame(() => {
            if (typeof this.position === 'undefined') {
                this.position = 50;
            }
        });
    }
    render() {
        if (typeof this.position === 'undefined')
            return;
        const styles = {};
        const gridTemplate = this.vertical
            ? 'gridTemplateRows'
            : 'gridTemplateColumns';
        const primary = `
      clamp(
        0%,
        clamp(
          var(--min),
          ${this.position}% - var(--divider-width) / 2,
          var(--max)
        ),
        calc(100% - var(--divider-width))
      )
    `;
        const secondary = 'auto';
        if (this.primary === 'end') {
            styles[gridTemplate] = `${secondary} var(--divider-width) ${primary}`;
        }
        else {
            styles[gridTemplate] = `${primary} var(--divider-width) ${secondary}`;
        }
        return (renderer.h(index.Host, { style: styles, class: "nano-split-pane" }, renderer.h("div", { part: "panel start", class: "start" }, renderer.h("slot", { name: "start" })), renderer.h("div", { part: "divider", class: "divider", tabindex: this.disabled ? undefined : '0', role: "separator", "aria-label": "Resize", onKeyDown: this.handleKeyDown, onMouseDown: this.handleDrag, onTouchStart: this.handleDrag }, renderer.h("slot", { name: "handle" }, !this.disabled && this.vertical ? (renderer.h("nano-icon", { slot: "handle", name: "solid/grip-dots" })) : (renderer.h("nano-icon", { slot: "handle", name: "solid/grip-dots-vertical" })))), renderer.h("div", { part: "panel end", class: "end" }, renderer.h("slot", { name: "end" }))));
    }
    static get watchers() { return {
        "position": ["handlePositionChange"],
        "positionInPixels": ["handlePositionInPixelsChange"]
    }; }
};
SplitPane.style = splitPaneCss;

exports.nano_split_pane = SplitPane;
