// src/hooks/focus/useFocusGuards.ts
import { useEffect } from "react";
var selectorName = "data-hw-focus-guard";
function FocusGuard() {
  const element = document.createElement("div");
  element.setAttribute(selectorName, "");
  element.tabIndex = 0;
  element.style.border = "none";
  element.style.outline = "none";
  element.style.boxShadow = "none";
  element.style.opacity = "0";
  element.style.position = "fixed";
  element.style.pointerEvents = "none";
  return element;
}
var FocusGuardsService = class _FocusGuardsService {
  constructor() {
    this.count = 0;
  }
  static getInstance() {
    if (!_FocusGuardsService.instance) {
      _FocusGuardsService.instance = new _FocusGuardsService();
    }
    return _FocusGuardsService.instance;
  }
  add() {
    const edgeGuards = document.querySelectorAll(`[${selectorName}]`);
    document.body.insertAdjacentElement("afterbegin", edgeGuards[0] ?? FocusGuard());
    document.body.insertAdjacentElement("beforeend", edgeGuards[1] ?? FocusGuard());
    this.count++;
  }
  remove() {
    if (this.count === 1) {
      document.querySelectorAll(`[${selectorName}]`).forEach((node) => node.remove());
    }
    this.count--;
  }
};
var useFocusGuards = () => {
  useEffect(() => {
    FocusGuardsService.getInstance().add();
    return () => {
      FocusGuardsService.getInstance().remove();
    };
  }, []);
};

// src/hooks/focus/useFocusManagement.ts
import { useCallback } from "react";
function useFocusManagement() {
  const getFocusableElements = useCallback(() => {
    return Array.from(
      document.querySelectorAll(
        'input, button, select, textarea, a[href], [tabindex]:not([tabindex="-1"])'
      )
    ).filter(
      (el) => el instanceof HTMLElement && !el.hasAttribute("disabled") && !el.hasAttribute("hidden") && el.tabIndex !== -1
    );
  }, []);
  const getNextFocusElement = useCallback(() => {
    const elements = getFocusableElements();
    if (elements.length === 0) {
      return void 0;
    }
    let nextElement = elements[0];
    if (document.activeElement instanceof HTMLElement) {
      const currentIndex = elements.indexOf(document.activeElement);
      nextElement = elements[(currentIndex + 1) % elements.length];
    }
    return nextElement;
  }, [getFocusableElements]);
  const focusNext = useCallback(() => {
    const nextElement = getNextFocusElement();
    nextElement?.focus();
  }, [getNextFocusElement]);
  const getPreviousFocusElement = useCallback(() => {
    const elements = getFocusableElements();
    if (elements.length === 0) {
      return void 0;
    }
    let previousElement = elements[0];
    if (document.activeElement instanceof HTMLElement) {
      const currentIndex = elements.indexOf(document.activeElement);
      if (currentIndex === 0) {
        previousElement = elements[elements.length - 1];
      } else {
        previousElement = elements[currentIndex - 1];
      }
    }
    return previousElement;
  }, [getFocusableElements]);
  const focusPrevious = useCallback(() => {
    const previousElement = getPreviousFocusElement();
    if (previousElement) previousElement.focus();
  }, [getPreviousFocusElement]);
  return {
    getFocusableElements,
    getNextFocusElement,
    getPreviousFocusElement,
    focusNext,
    focusPrevious
  };
}

// src/hooks/focus/useFocusOnceVisible.ts
import React, { useEffect as useEffect2 } from "react";
var useFocusOnceVisible = (ref, disable = false) => {
  const [hasUsedFocus, setHasUsedFocus] = React.useState(false);
  useEffect2(() => {
    if (disable || hasUsedFocus) {
      return;
    }
    const observer = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting && !hasUsedFocus) {
        ref.current?.focus();
        setHasUsedFocus(hasUsedFocus);
      }
    }, {
      threshold: 0.1
    });
    if (ref.current) {
      observer.observe(ref.current);
    }
    return () => observer.disconnect();
  }, [disable, hasUsedFocus, ref]);
};

// src/hooks/focus/useFocusTrap.ts
import { useCallback as useCallback2, useEffect as useEffect4, useId, useRef, useState as useState2 } from "react";

// src/hooks/focus/useIsMounted.ts
import { useEffect as useEffect3, useLayoutEffect, useState } from "react";
var isClient = typeof window !== "undefined" && typeof document !== "undefined";
var useIsomorphicEffect = isClient ? useLayoutEffect : useEffect3;
var useIsMounted = () => {
  const [isMounted, setIsMounted] = useState(false);
  useIsomorphicEffect(() => {
    setIsMounted(true);
    return () => {
      setIsMounted(false);
    };
  }, []);
  return isMounted;
};

// src/hooks/focus/useFocusTrap.ts
var createFocusGuard = () => {
  const div = document.createElement("div");
  Object.assign(div.style, {
    opacity: "0",
    outline: "none",
    boxShadow: "none",
    position: "fixed",
    pointerEvents: "none",
    touchAction: "none"
  });
  div.tabIndex = 0;
  div.setAttribute("data-hw-focus-guard", "");
  document.body.appendChild(div);
  return div;
};
function getContainedFocusableElements(element) {
  return element?.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
}
var FocusTrapService = class {
  constructor() {
    // The last entry is always the active one
    this.listeners = [];
    this.onFocusIn = (event) => {
      const active = this.getActive();
      if (!active || !active.container.current) return;
      const { container } = active;
      if (!container.current.contains(event.target)) {
        this.focusElement();
      }
    };
  }
  getActive() {
    if (this.listeners.length === 0) return void 0;
    return this.listeners[this.listeners.length - 1];
  }
  focusElement() {
    const active = this.getActive();
    if (!active) return;
    const { container, initialFocusElement } = active;
    const containerElement = container.current;
    if (initialFocusElement?.current) {
      initialFocusElement.current.focus();
    } else {
      const elements = getContainedFocusableElements(containerElement);
      if (elements && elements.length > 0) {
        const first = elements.item(0);
        first.focus();
      } else {
        containerElement.focus();
      }
    }
  }
  removeGuards() {
    document.querySelectorAll("[data-hw-focus-guard]").forEach((node) => node.remove());
  }
  addGuards() {
    document.body.insertAdjacentElement("afterbegin", createFocusGuard());
    document.body.insertAdjacentElement("beforeend", createFocusGuard());
  }
  activate() {
    document.addEventListener("focusin", this.onFocusIn);
    this.addGuards();
  }
  deactivate() {
    document.removeEventListener("focusin", this.onFocusIn);
    this.removeGuards();
  }
  register(listener) {
    this.listeners.push(listener);
    if (this.listeners.length === 1) {
      this.activate();
    }
    const active = listener;
    this.listeners.forEach((listener2) => {
      const { focus, pause } = listener2;
      if (listener2 === active) {
        focus();
      } else {
        pause();
      }
    });
  }
  unregister(id) {
    const index = this.listeners.findIndex((trap) => trap.id === id);
    if (index !== -1) {
      const isActive = index === this.listeners.length - 1;
      const listener = this.listeners[index];
      this.listeners = this.listeners.filter((listener2) => listener2.id !== id);
      if (isActive) {
        this.deactivate();
        listener.focusLast();
        const active = this.getActive();
        this.listeners.forEach((listener2) => {
          const { pause, unpause } = listener2;
          if (listener2 === active) {
            unpause();
          } else {
            pause();
          }
        });
        if (this.listeners.length > 0) {
          this.activate();
        }
      }
    } else {
      console.warn(`Unable to unregister id ${id}: not found`);
    }
  }
};
var service = new FocusTrapService();
var useFocusTrap = ({
  container,
  active = true,
  initialFocus,
  focusFirst = true
}) => {
  const lastFocusRef = useRef(null);
  const [paused, setPaused] = useState2(false);
  const isMounted = useIsMounted();
  const id = useId();
  const focusElement = useCallback2(() => {
    const containerElement = container.current;
    if (initialFocus?.current) {
      initialFocus.current.focus();
    } else {
      const elements = getContainedFocusableElements(containerElement);
      if (elements && elements.length > 0) {
        const first = elements.item(0);
        first.focus();
      } else {
        containerElement.focus();
      }
    }
  }, [container, initialFocus]);
  useEffect4(() => {
    if (active && isMounted) {
      let pause = function() {
        setPaused(true);
      }, unpause = function() {
        setPaused(false);
        if (!container.current.contains(document.activeElement)) {
          focusElement();
        }
      }, focus = function() {
        focusElement();
        setPaused(false);
      }, focusLast = function() {
        lastFocusRef.current?.focus();
      };
      if (!lastFocusRef.current) {
        lastFocusRef.current = document.activeElement;
      }
      service.register({ id, pause, focus, focusLast, unpause, container, initialFocusElement: initialFocus });
      return () => {
        service.unregister(id);
        lastFocusRef.current = void 0;
      };
    }
  }, [active, container, focusElement, id, initialFocus, isMounted]);
  useEffect4(() => {
    if (active && !paused && isMounted) {
      let onKeyDown = function(event) {
        const key = event.key;
        const elements = getContainedFocusableElements(containerElement);
        const active2 = document.activeElement;
        const index = [...elements].findIndex((value) => value === active2);
        if (index === -1 || event.altKey || event.ctrlKey || event.metaKey) {
          return;
        }
        if (key === "Tab") {
          const next = event.shiftKey ? -1 : 1;
          const nextIndex = (index + next + elements.length) % elements.length;
          const nextElement = elements[nextIndex];
          nextElement.focus();
          event.preventDefault();
        }
      };
      const containerElement = container.current;
      containerElement.addEventListener("keydown", onKeyDown);
      return () => {
        containerElement.removeEventListener("keydown", onKeyDown);
      };
    }
  }, [active, paused, isMounted, container, initialFocus, focusFirst, focusElement]);
};
export {
  useFocusGuards,
  useFocusManagement,
  useFocusOnceVisible,
  useFocusTrap,
  useIsMounted
};
//# sourceMappingURL=index.js.map