{"version":3,"file":"AccessibilityUtils.mjs","sources":["../src/AccessibilityUtils.ts"],"sourcesContent":["/**\n * @module AccessibilityUtils\n * @description A collection of utility functions for improving web accessibility, ARIA management, and inclusive design.\n * Provides methods for handling ARIA attributes, focus management, and keyboard navigation.\n *\n * @example\n * ```typescript\n * import { AccessibilityUtils } from 'houser-js-utils';\n *\n * // Set ARIA attributes\n * AccessibilityUtils.setAriaLabel(element, 'Close dialog');\n *\n * // Manage focus\n * AccessibilityUtils.trapFocus(modalElement);\n *\n * // Check accessibility\n * const isAccessible = AccessibilityUtils.hasValidAriaLabels(form);\n * ```\n */\n\nexport const AccessibilityUtils = {\n  /**\n   * Gets the ARIA described by attribute of an element\n   * @param element - The DOM element to check\n   * @returns The value of aria-describedby attribute or null if not set\n   * @example\n   * ```typescript\n   * const element = document.querySelector('.input');\n   * const describedBy = AccessibilityUtils.getAriaDescribedBy(element);\n   * ```\n   */\n  getAriaDescribedBy(element: Element): string | null {\n    return element.getAttribute(\"aria-describedby\");\n  },\n\n  /**\n   * Sets the ARIA described by attribute of an element\n   * @param element - The DOM element to modify\n   * @param describedBy - The ID(s) of the element(s) that describe this element\n   * @example\n   * ```typescript\n   * const input = document.querySelector('.input');\n   * const helpText = document.querySelector('.help-text');\n   * AccessibilityUtils.setAriaDescribedBy(input, helpText.id);\n   * ```\n   */\n  setAriaDescribedBy(element: Element, describedBy: string): void {\n    element.setAttribute(\"aria-describedby\", describedBy);\n  },\n\n  /**\n   * Gets the ARIA expanded state of an element\n   * @param element - Element to check\n   * @returns ARIA expanded state\n   */\n  getAriaExpanded(element: Element): boolean | null {\n    const expanded = element.getAttribute(\"aria-expanded\");\n    return expanded === null ? null : expanded === \"true\";\n  },\n\n  /**\n   * Sets the ARIA expanded state of an element\n   * @param element - Element to modify\n   * @param expanded - ARIA expanded state to set\n   */\n  setAriaExpanded(element: Element, expanded: boolean): void {\n    element.setAttribute(\"aria-expanded\", expanded.toString());\n  },\n\n  /**\n   * Gets the ARIA hidden state of an element\n   * @param element - Element to check\n   * @returns ARIA hidden state\n   */\n  getAriaHidden(element: Element): boolean | null {\n    const hidden = element.getAttribute(\"aria-hidden\");\n    return hidden === null ? null : hidden === \"true\";\n  },\n\n  /**\n   * Sets the ARIA hidden state of an element\n   * @param element - Element to modify\n   * @param hidden - ARIA hidden state to set\n   */\n  setAriaHidden(element: Element, hidden: boolean): void {\n    element.setAttribute(\"aria-hidden\", hidden.toString());\n  },\n\n  /**\n   * Gets the ARIA invalid state of an element\n   * @param element - Element to check\n   * @returns ARIA invalid state\n   */\n  getAriaInvalid(element: Element): boolean | null {\n    const invalid = element.getAttribute(\"aria-invalid\");\n    return invalid === null ? null : invalid === \"true\";\n  },\n\n  /**\n   * Sets the ARIA invalid state of an element\n   * @param element - Element to modify\n   * @param invalid - ARIA invalid state to set\n   */\n  setAriaInvalid(element: Element, invalid: boolean): void {\n    element.setAttribute(\"aria-invalid\", invalid.toString());\n  },\n\n  /**\n   * Gets the ARIA label of an element\n   * @param element - Element to check\n   * @returns ARIA label\n   */\n  getAriaLabel(element: Element): string | null {\n    return element.getAttribute(\"aria-label\");\n  },\n\n  /**\n   * Sets the ARIA label of an element\n   * @param element - Element to modify\n   * @param label - ARIA label to set\n   */\n  setAriaLabel(element: Element, label: string): void {\n    element.setAttribute(\"aria-label\", label);\n  },\n\n  /**\n   * Gets the ARIA required state of an element\n   * @param element - Element to check\n   * @returns ARIA required state\n   */\n  getAriaRequired(element: Element): boolean | null {\n    const required = element.getAttribute(\"aria-required\");\n    return required === null ? null : required === \"true\";\n  },\n\n  /**\n   * Sets the ARIA required state of an element\n   * @param element - Element to modify\n   * @param required - ARIA required state to set\n   */\n  setAriaRequired(element: Element, required: boolean): void {\n    element.setAttribute(\"aria-required\", required.toString());\n  },\n\n  /**\n   * Gets the ARIA role of an element\n   * @param element - Element to check\n   * @returns ARIA role\n   */\n  getAriaRole(element: Element): string | null {\n    return element.getAttribute(\"role\");\n  },\n\n  /**\n   * Sets the ARIA role of an element\n   * @param element - Element to modify\n   * @param role - ARIA role to set\n   */\n  setAriaRole(element: Element, role: string): void {\n    element.setAttribute(\"role\", role);\n  },\n\n  /**\n   * Removes focus from an element\n   * @param element - Element to blur\n   */\n  blurElement(element: Element): void {\n    if (element instanceof HTMLElement) {\n      element.blur();\n    }\n  },\n\n  /**\n   * Sets focus to an element\n   * @param element - Element to focus\n   */\n  focusElement(element: Element): void {\n    if (element instanceof HTMLElement) {\n      element.focus();\n    }\n  },\n\n  /**\n   * Sets focus to the first focusable element in a container\n   * @param container - Container element\n   */\n  focusFirstElement(container: Element): void {\n    const focusableElements = this.getFocusableElements(container);\n    if (focusableElements.length > 0) {\n      (focusableElements[0] as HTMLElement).focus();\n    }\n  },\n\n  /**\n   * Sets focus to the last focusable element in a container\n   * @param container - Container element\n   */\n  focusLastElement(container: Element): void {\n    const focusableElements = this.getFocusableElements(container);\n    if (focusableElements.length > 0) {\n      (focusableElements[focusableElements.length - 1] as HTMLElement).focus();\n    }\n  },\n\n  /**\n   * Sets focus to the next focusable element\n   * @param currentElement - Current element\n   */\n  focusNextElement(currentElement: Element): void {\n    const focusableElements = this.getFocusableElements(document.body);\n    const currentIndex = focusableElements.indexOf(currentElement);\n    if (currentIndex < focusableElements.length - 1) {\n      (focusableElements[currentIndex + 1] as HTMLElement).focus();\n    }\n  },\n\n  /**\n   * Sets focus to the previous focusable element\n   * @param currentElement - Current element\n   */\n  focusPreviousElement(currentElement: Element): void {\n    const focusableElements = this.getFocusableElements(document.body);\n    const currentIndex = focusableElements.indexOf(currentElement);\n    if (currentIndex > 0) {\n      (focusableElements[currentIndex - 1] as HTMLElement).focus();\n    }\n  },\n\n  /**\n   * Gets all focusable elements within a container\n   * @param container - Container element\n   * @returns Array of focusable elements\n   */\n  getFocusableElements(container: Element): Element[] {\n    const elements = container.querySelectorAll(\"*\");\n    return Array.from(elements).filter((element) => this.isFocusable(element));\n  },\n\n  /**\n   * Gets the current focus element\n   * @returns Currently focused element\n   */\n  getFocusedElement(): Element | null {\n    return document.activeElement;\n  },\n\n  /**\n   * Checks if an element is focusable\n   * @param element - Element to check\n   * @returns True if element is focusable\n   */\n  isFocusable(element: Element): boolean {\n    if (!(element instanceof HTMLElement)) {\n      return false;\n    }\n\n    if (element.tabIndex < 0) {\n      return false;\n    }\n\n    if (\"disabled\" in element && element.disabled) {\n      return false;\n    }\n\n    switch (element.tagName.toLowerCase()) {\n      case \"a\":\n      case \"button\":\n      case \"input\":\n      case \"select\":\n      case \"textarea\":\n        return true;\n      default:\n        return false;\n    }\n  },\n\n  /**\n   * Traps focus within a container element, typically used for modals or dialogs\n   * @param container - The container element to trap focus within\n   * @returns A function that removes the focus trap when called\n   * @example\n   * ```typescript\n   * const modal = document.querySelector('.modal');\n   * const removeTrap = AccessibilityUtils.trapFocus(modal);\n   *\n   * // Later, when the modal is closed:\n   * removeTrap();\n   * ```\n   */\n  trapFocus(container: Element): () => void {\n    const focusableElements = this.getFocusableElements(container);\n    const firstFocusableElement = focusableElements[0];\n    const lastFocusableElement =\n      focusableElements[focusableElements.length - 1];\n\n    const handleKeyDown = (event: Event) => {\n      if (!(event instanceof KeyboardEvent) || event.key !== \"Tab\") {\n        return;\n      }\n\n      if (event.shiftKey) {\n        if (document.activeElement === firstFocusableElement) {\n          event.preventDefault();\n          (lastFocusableElement as HTMLElement).focus();\n        }\n      } else {\n        if (document.activeElement === lastFocusableElement) {\n          event.preventDefault();\n          (firstFocusableElement as HTMLElement).focus();\n        }\n      }\n    };\n\n    container.addEventListener(\"keydown\", handleKeyDown as EventListener);\n    return () =>\n      container.removeEventListener(\"keydown\", handleKeyDown as EventListener);\n  },\n};\n"],"names":[],"mappings":"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"}