"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.UIQuickNavigation = exports.isQuickNavigationEnabled = exports.setQuickNavigationKey = exports.QUICK_NAVIGATION_ATTRIBUTE_OFFSET_X = exports.QUICK_NAVIGATION_ATTRIBUTE_OFFSET_Y = exports.QUICK_NAVIGATION_ATTRIBUTE = void 0;
const react_1 = __importStar(require("react"));
const utilities_1 = require("../../utilities");
require("./UIQuickNavigation.scss");
exports.QUICK_NAVIGATION_ATTRIBUTE = 'data-quick-navigation-key';
exports.QUICK_NAVIGATION_ATTRIBUTE_OFFSET_Y = 'data-quick-navigation-offset-y';
exports.QUICK_NAVIGATION_ATTRIBUTE_OFFSET_X = 'data-quick-navigation-offset-x';
const QUICK_NAVIGATION_CLASSES = {
    inline: 'quick-navigation--inline',
    external: 'quick-navigation--external'
};
const EXTERNAL_HELPER_OFFSET = {
    x: 15,
    y: 15
};
const setQuickNavigationKey = (key, offset) => {
    const attributes = {
        [exports.QUICK_NAVIGATION_ATTRIBUTE]: key.toUpperCase()
    };
    if (offset) {
        attributes[exports.QUICK_NAVIGATION_ATTRIBUTE_OFFSET_Y] = offset.y.toString();
        attributes[exports.QUICK_NAVIGATION_ATTRIBUTE_OFFSET_X] = offset.x.toString();
    }
    return attributes;
};
exports.setQuickNavigationKey = setQuickNavigationKey;
/**
 * Method returns CSS classnames based on current quick navigation state.
 *
 * @param className External classname(s).
 * @param enabled Is quick navigation enabled/activated.
 * @param inline Is quick navigation should be rendered with inline approach.
 * @returns CSS classnames based on current quick navigation state.
 */
function getClassName(className, enabled, inline) {
    const result = [className];
    if (enabled && inline) {
        result.push(QUICK_NAVIGATION_CLASSES.inline);
    }
    return result.join(' ');
}
/**
 * Method returns scroll offset position for external rendering approach.
 * External rendering happens in 'document.body' and body scroll position should be considered.
 *
 * @returns Scroll position of body and html.
 */
function getScrollOffset() {
    let container = document.body;
    const scrollOffset = {
        y: 0,
        x: 0
    };
    do {
        scrollOffset.y += container.scrollTop || 0;
        scrollOffset.x += container.scrollLeft || 0;
        container = container.parentNode && (0, utilities_1.isHTMLElement)(container.parentNode) ? container.parentNode : undefined;
    } while (container);
    return scrollOffset;
}
/**
 * Method returns offset for passed DOM element.
 *
 * @param target Target element to detect offset.
 * @returns Scroll position of body and html.
 */
function getOffsetFromElement(target) {
    const yAttr = target.getAttribute(exports.QUICK_NAVIGATION_ATTRIBUTE_OFFSET_Y);
    const xAttr = target.getAttribute(exports.QUICK_NAVIGATION_ATTRIBUTE_OFFSET_X);
    if (!yAttr || !xAttr) {
        return undefined;
    }
    const y = Number.parseFloat(yAttr);
    const x = Number.parseFloat(xAttr);
    return !Number.isNaN(y) && !Number.isNaN(x) ? { y, x } : undefined;
}
/**
 * Method toggles visibility of external quick navigation helpers UI.
 *
 * @param enabled Is quick navigation enabled/activated.
 * @param offset Offset values for helper position.
 */
function toggleExternalVisibility(enabled, offset = EXTERNAL_HELPER_OFFSET) {
    // Cleanup container
    const existingContainer = document.querySelector(`.${QUICK_NAVIGATION_CLASSES.external}`);
    if (existingContainer) {
        existingContainer.remove();
    }
    // Show helpers if quick navigation is active
    if (enabled) {
        // Create external container
        const externalContainer = document.createElement('div');
        externalContainer.classList.add(QUICK_NAVIGATION_CLASSES.external);
        const navigationTargets = document.querySelectorAll(`[${exports.QUICK_NAVIGATION_ATTRIBUTE}]`);
        // Create external DOM element to each navigation target
        const scrollOffset = getScrollOffset();
        navigationTargets.forEach((target) => {
            const rect = target.getBoundingClientRect();
            const helper = document.createElement('div');
            helper.textContent = target.getAttribute(exports.QUICK_NAVIGATION_ATTRIBUTE);
            const elementOffset = getOffsetFromElement(target) ?? offset;
            const position = {
                top: rect.top + scrollOffset.y - elementOffset.y,
                left: rect.left + scrollOffset.x - elementOffset.x
            };
            position.top = Math.max(position.top, 0);
            position.left = Math.max(position.left, 0);
            helper.style.top = `${position.top}px`;
            helper.style.left = `${position.left}px`;
            externalContainer.appendChild(helper);
        });
        document.body.appendChild(externalContainer);
    }
}
/**
 * Method checks if quick navigation should be enabled for passed keyboard event.
 * Quick navigation is enabled when user hold 'ctrl + alt' or 'meta + alt'.
 *
 * @param event Keyboard event.
 * @returns True if quick navigation should be enabled for passed keyboard event.
 */
const isQuickNavigationEnabled = (event) => {
    return (event.ctrlKey || event.metaKey) && event.altKey;
};
exports.isQuickNavigationEnabled = isQuickNavigationEnabled;
/**
 * Method resolves passed keyboard code by removing 'Digit' and 'Key' keywords.
 *
 * @param code Code from keyboard event.
 * @returns Resolved char or digit.
 */
function resolveKeyCode(code) {
    return code.replace('Digit', '').replace('Key', '').toUpperCase();
}
/**
 * Method to stop event bubbling.
 *
 * @param event Keyboard event.
 */
function stopEventBubling(event) {
    event.stopImmediatePropagation();
    event.stopPropagation();
    event.preventDefault();
}
const UIQuickNavigation = (props) => {
    const { className, children, inline, offset } = props;
    const [enabled, setEnabled] = (0, react_1.useState)(false);
    const navigated = (0, react_1.useRef)(false);
    // Handle visibility for 'external' rendering approach(helpers rendered outside of target navigation containers)
    (0, react_1.useEffect)(() => {
        if (inline) {
            return;
        }
        toggleExternalVisibility(enabled, offset);
    }, [enabled]);
    /**
     * Method handles key down event.
     *
     * @param event Key down event.
     */
    const onKeyDown = (0, react_1.useCallback)((event) => {
        let activated = enabled;
        if (!enabled && (0, exports.isQuickNavigationEnabled)(event)) {
            setEnabled(true);
            activated = true;
        }
        if (!activated) {
            return;
        }
        // Quick navigation is active - handle final key press
        const resolvedKey = resolveKeyCode(event.code);
        const navigationElement = document.querySelector(`[${exports.QUICK_NAVIGATION_ATTRIBUTE}="${resolvedKey}"]`);
        const startElement = navigationElement?.firstElementChild;
        if (navigationElement && startElement && (0, utilities_1.isHTMLElement)(startElement)) {
            const firstFocusableElement = (0, utilities_1.getUIFirstFocusable)(navigationElement, startElement, true);
            if (firstFocusableElement) {
                // Set focus visiblity in UI(fluent-ui feature)
                (0, utilities_1.setUIFocusVisibility)(true, firstFocusableElement);
                // Apply focus to element
                firstFocusableElement?.focus();
                // Prevent event bubbling
                stopEventBubling(event);
                navigated.current = true;
            }
            // Disable/deactivate UI with quick navigation
            setEnabled(false);
        }
    }, [enabled]);
    /**
     * Method handles key up and window blur events to detect if quick navigation should be deactivated.
     *
     * @param event Key up or blur event.
     */
    const onRelease = (0, react_1.useCallback)((event) => {
        if (enabled && (!('keyCode' in event) || !(0, exports.isQuickNavigationEnabled)(event))) {
            setEnabled(false);
        }
        if (navigated.current) {
            navigated.current = false;
            stopEventBubling(event);
        }
    }, [enabled]);
    /**
     * Attach events to listen keydown/keyup within window and blur from window.
     */
    (0, react_1.useEffect)(() => {
        document.body.addEventListener('keydown', onKeyDown);
        document.body.addEventListener('keyup', onRelease);
        window.addEventListener('blur', onRelease);
        return () => {
            toggleExternalVisibility(false);
            document.body.removeEventListener('keydown', onKeyDown);
            document.body.removeEventListener('keyup', onRelease);
            window.removeEventListener('blur', onRelease);
        };
    }, [onKeyDown, onRelease]);
    return react_1.default.createElement("div", { className: getClassName(className, enabled, inline) }, children);
};
exports.UIQuickNavigation = UIQuickNavigation;
exports.UIQuickNavigation.defaultProps = {
    inline: false,
    offset: EXTERNAL_HELPER_OFFSET
};
//# sourceMappingURL=UIQuickNavigation.js.map