{"version":3,"file":"service-navigation.mjs","sources":["../../../../src/govuk/components/service-navigation/service-navigation.mjs"],"sourcesContent":["import { getBreakpoint } from '../../common/index.mjs'\nimport { Component } from '../../component.mjs'\nimport { ElementError } from '../../errors/index.mjs'\n\n/**\n * Service Navigation component\n *\n * @preserve\n */\nexport class ServiceNavigation extends Component {\n  /** @private */\n  $menuButton\n\n  /** @private */\n  $menu\n\n  /**\n   * Remember the open/closed state of the nav so we can maintain it when the\n   * screen is resized.\n   *\n   * @private\n   */\n  menuIsOpen = false\n\n  /**\n   * A global const for storing a matchMedia instance which we'll use to detect\n   * when a screen size change happens. We rely on it being null if the feature\n   * isn't available to initially apply hidden attributes\n   *\n   * @private\n   * @type {MediaQueryList | null}\n   */\n  mql = null\n\n  /**\n   * @param {Element | null} $root - HTML element to use for header\n   */\n  constructor($root) {\n    super($root)\n\n    const $menuButton = this.$root.querySelector(\n      '.govuk-js-service-navigation-toggle'\n    )\n\n    // Headers don't necessarily have a navigation. When they don't, the menu\n    // toggle won't be rendered by our macro (or may be omitted when writing\n    // plain HTML)\n    if (!$menuButton) {\n      return this\n    }\n\n    const menuId = $menuButton.getAttribute('aria-controls')\n    if (!menuId) {\n      throw new ElementError({\n        component: ServiceNavigation,\n        identifier:\n          'Navigation button (`<button class=\"govuk-js-service-navigation-toggle\">`) attribute (`aria-controls`)'\n      })\n    }\n\n    const $menu = document.getElementById(menuId)\n    if (!$menu) {\n      throw new ElementError({\n        component: ServiceNavigation,\n        element: $menu,\n        identifier: `Navigation (\\`<ul id=\"${menuId}\">\\`)`\n      })\n    }\n\n    this.$menu = $menu\n    this.$menuButton = $menuButton\n\n    this.setupResponsiveChecks()\n\n    this.$menuButton.addEventListener('click', () =>\n      this.handleMenuButtonClick()\n    )\n  }\n\n  /**\n   * Setup viewport resize check\n   *\n   * @private\n   */\n  setupResponsiveChecks() {\n    const breakpoint = getBreakpoint('tablet')\n\n    if (!breakpoint.value) {\n      throw new ElementError({\n        component: ServiceNavigation,\n        identifier: `CSS custom property (\\`${breakpoint.property}\\`) on pseudo-class \\`:root\\``\n      })\n    }\n\n    // Media query list for GOV.UK Frontend desktop breakpoint\n    this.mql = window.matchMedia(`(min-width: ${breakpoint.value})`)\n\n    // MediaQueryList.addEventListener isn't supported by Safari < 14 so we need\n    // to be able to fall back to the deprecated MediaQueryList.addListener\n    if ('addEventListener' in this.mql) {\n      this.mql.addEventListener('change', () => this.checkMode())\n    } else {\n      // @ts-expect-error Property 'addListener' does not exist\n      // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n      this.mql.addListener(() => this.checkMode())\n    }\n\n    this.checkMode()\n  }\n\n  /**\n   * Sync menu state\n   *\n   * Uses the global variable menuIsOpen to correctly set the accessible and\n   * visual states of the menu and the menu button.\n   * Additionally will force the menu to be visible and the menu button to be\n   * hidden if the matchMedia is triggered to desktop.\n   *\n   * @private\n   */\n  checkMode() {\n    if (!this.mql || !this.$menu || !this.$menuButton) {\n      return\n    }\n\n    if (this.mql.matches) {\n      this.$menu.removeAttribute('hidden')\n      setAttributes(this.$menuButton, attributesForHidingButton)\n    } else {\n      removeAttributes(this.$menuButton, Object.keys(attributesForHidingButton))\n      this.$menuButton.setAttribute('aria-expanded', this.menuIsOpen.toString())\n\n      if (this.menuIsOpen) {\n        this.$menu.removeAttribute('hidden')\n      } else {\n        this.$menu.setAttribute('hidden', '')\n      }\n    }\n  }\n\n  /**\n   * Handle menu button click\n   *\n   * When the menu button is clicked, change the visibility of the menu and then\n   * sync the accessibility state and menu button state\n   *\n   * @private\n   */\n  handleMenuButtonClick() {\n    this.menuIsOpen = !this.menuIsOpen\n    this.checkMode()\n  }\n\n  /**\n   * Name for the component used when initialising using data-module attributes.\n   */\n  static moduleName = 'govuk-service-navigation'\n}\n\n/**\n * Collection of attributes that needs setting on a `<button>`\n * to fully hide it, both visually and from screen-readers,\n * and prevent its activation while hidden\n */\nconst attributesForHidingButton = {\n  hidden: '',\n  // Fix button still appearing in VoiceOver's form control's menu despite being hidden\n  // https://bugs.webkit.org/show_bug.cgi?id=300899\n  'aria-hidden': 'true'\n}\n\n/**\n * Sets a group of attributes on the given element\n *\n * @param {Element} $element - The element to set the attribute on\n * @param {{[attributeName: string]: string}} attributes - The attributes to set\n */\nfunction setAttributes($element, attributes) {\n  for (const attributeName in attributes) {\n    $element.setAttribute(attributeName, attributes[attributeName])\n  }\n}\n\n/**\n * Removes a list of attributes from the given element\n *\n * @param {Element} $element - The element to remove the attributes from\n * @param {string[]} attributeNames - The names of the attributes to remove\n */\nfunction removeAttributes($element, attributeNames) {\n  for (const attributeName of attributeNames) {\n    $element.removeAttribute(attributeName)\n  }\n}\n"],"names":["ServiceNavigation","Component","constructor","$root","$menuButton","$menu","menuIsOpen","mql","querySelector","menuId","getAttribute","ElementError","component","identifier","document","getElementById","element","setupResponsiveChecks","addEventListener","handleMenuButtonClick","breakpoint","getBreakpoint","value","property","window","matchMedia","checkMode","addListener","matches","removeAttribute","setAttributes","attributesForHidingButton","removeAttributes","Object","keys","setAttribute","toString","moduleName","hidden","$element","attributes","attributeName","attributeNames"],"mappings":"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"}