{"version":3,"file":"checkboxes.mjs","sources":["../../../../src/govuk/components/checkboxes/checkboxes.mjs"],"sourcesContent":["import { Component } from '../../component.mjs'\nimport { ElementError } from '../../errors/index.mjs'\n\n/**\n * Checkboxes component\n *\n * @preserve\n */\nexport class Checkboxes extends Component {\n  /** @private */\n  $inputs\n\n  /**\n   * Checkboxes can be associated with a 'conditionally revealed' content block\n   * – for example, a checkbox for 'Phone' could reveal an additional form field\n   * for the user to enter their phone number.\n   *\n   * These associations are made using a `data-aria-controls` attribute, which\n   * is promoted to an aria-controls attribute during initialisation.\n   *\n   * We also need to restore the state of any conditional reveals on the page\n   * (for example if the user has navigated back), and set up event handlers to\n   * keep the reveal in sync with the checkbox state.\n   *\n   * @param {Element | null} $root - HTML element to use for checkboxes\n   */\n  constructor($root) {\n    super($root)\n\n    const $inputs = this.$root.querySelectorAll('input[type=\"checkbox\"]')\n    if (!$inputs.length) {\n      throw new ElementError({\n        component: Checkboxes,\n        identifier: 'Form inputs (`<input type=\"checkbox\">`)'\n      })\n    }\n\n    this.$inputs = $inputs\n\n    this.$inputs.forEach(($input) => {\n      const targetId = $input.getAttribute('data-aria-controls')\n\n      // Skip radios without data-aria-controls attributes\n      if (!targetId) {\n        return\n      }\n\n      // Throw if target conditional element does not exist.\n      if (!document.getElementById(targetId)) {\n        throw new ElementError({\n          component: Checkboxes,\n          identifier: `Conditional reveal (\\`id=\"${targetId}\"\\`)`\n        })\n      }\n\n      // Promote the data-aria-controls attribute to a aria-controls attribute\n      // so that the relationship is exposed in the AOM\n      $input.setAttribute('aria-controls', targetId)\n      $input.removeAttribute('data-aria-controls')\n    })\n\n    // When the page is restored after navigating 'back' in some browsers the\n    // state of form controls is not restored until *after* the DOMContentLoaded\n    // event is fired, so we need to sync after the pageshow event.\n    window.addEventListener('pageshow', () => this.syncAllConditionalReveals())\n\n    // Although we've set up handlers to sync state on the pageshow event, init\n    // could be called after those events have fired, for example if they are\n    // added to the page dynamically, so sync now too.\n    this.syncAllConditionalReveals()\n\n    // Handle events\n    this.$root.addEventListener('click', (event) => this.handleClick(event))\n  }\n\n  /**\n   * Sync the conditional reveal states for all checkboxes in this component.\n   *\n   * @private\n   */\n  syncAllConditionalReveals() {\n    this.$inputs.forEach(($input) =>\n      this.syncConditionalRevealWithInputState($input)\n    )\n  }\n\n  /**\n   * Sync conditional reveal with the input state\n   *\n   * Synchronise the visibility of the conditional reveal, and its accessible\n   * state, with the input's checked state.\n   *\n   * @private\n   * @param {HTMLInputElement} $input - Checkbox input\n   */\n  syncConditionalRevealWithInputState($input) {\n    const targetId = $input.getAttribute('aria-controls')\n    if (!targetId) {\n      return\n    }\n\n    const $target = document.getElementById(targetId)\n    if ($target?.classList.contains('govuk-checkboxes__conditional')) {\n      const inputIsChecked = $input.checked\n\n      $input.setAttribute('aria-expanded', inputIsChecked.toString())\n      $target.classList.toggle(\n        'govuk-checkboxes__conditional--hidden',\n        !inputIsChecked\n      )\n    }\n  }\n\n  /**\n   * Uncheck other checkboxes\n   *\n   * Find any other checkbox inputs with the same name value, and uncheck them.\n   * This is useful for when a “None of these\" checkbox is checked.\n   *\n   * @private\n   * @param {HTMLInputElement} $input - Checkbox input\n   */\n  unCheckAllInputsExcept($input) {\n    const allInputsWithSameName = document.querySelectorAll(\n      `input[type=\"checkbox\"][name=\"${$input.name}\"]`\n    )\n\n    allInputsWithSameName.forEach(($inputWithSameName) => {\n      const hasSameFormOwner = $input.form === $inputWithSameName.form\n      if (hasSameFormOwner && $inputWithSameName !== $input) {\n        $inputWithSameName.checked = false\n        this.syncConditionalRevealWithInputState($inputWithSameName)\n      }\n    })\n  }\n\n  /**\n   * Uncheck exclusive checkboxes\n   *\n   * Find any checkbox inputs with the same name value and the 'exclusive'\n   * behaviour, and uncheck them. This helps prevent someone checking both a\n   * regular checkbox and a \"None of these\" checkbox in the same fieldset.\n   *\n   * @private\n   * @param {HTMLInputElement} $input - Checkbox input\n   */\n  unCheckExclusiveInputs($input) {\n    const allInputsWithSameNameAndExclusiveBehaviour =\n      document.querySelectorAll(\n        `input[data-behaviour=\"exclusive\"][type=\"checkbox\"][name=\"${$input.name}\"]`\n      )\n\n    allInputsWithSameNameAndExclusiveBehaviour.forEach(($exclusiveInput) => {\n      const hasSameFormOwner = $input.form === $exclusiveInput.form\n      if (hasSameFormOwner) {\n        $exclusiveInput.checked = false\n        this.syncConditionalRevealWithInputState($exclusiveInput)\n      }\n    })\n  }\n\n  /**\n   * Click event handler\n   *\n   * Handle a click within the component root – if the click occurred on a checkbox,\n   * sync the state of any associated conditional reveal with the checkbox\n   * state.\n   *\n   * @private\n   * @param {MouseEvent} event - Click event\n   */\n  handleClick(event) {\n    const $clickedInput = event.target\n\n    // Ignore clicks on things that aren't checkbox inputs\n    if (\n      !($clickedInput instanceof HTMLInputElement) ||\n      $clickedInput.type !== 'checkbox'\n    ) {\n      return\n    }\n\n    // If the checkbox conditionally-reveals some content, sync the state\n    const hasAriaControls = $clickedInput.getAttribute('aria-controls')\n    if (hasAriaControls) {\n      this.syncConditionalRevealWithInputState($clickedInput)\n    }\n\n    // No further behaviour needed for unchecking\n    if (!$clickedInput.checked) {\n      return\n    }\n\n    // Handle 'exclusive' checkbox behaviour (ie \"None of these\")\n    const hasBehaviourExclusive =\n      $clickedInput.getAttribute('data-behaviour') === 'exclusive'\n    if (hasBehaviourExclusive) {\n      this.unCheckAllInputsExcept($clickedInput)\n    } else {\n      this.unCheckExclusiveInputs($clickedInput)\n    }\n  }\n\n  /**\n   * Name for the component used when initialising using data-module attributes.\n   */\n  static moduleName = 'govuk-checkboxes'\n}\n"],"names":["Checkboxes","Component","constructor","$root","$inputs","querySelectorAll","length","ElementError","component","identifier","forEach","$input","targetId","getAttribute","document","getElementById","setAttribute","removeAttribute","window","addEventListener","syncAllConditionalReveals","event","handleClick","syncConditionalRevealWithInputState","$target","classList","contains","inputIsChecked","checked","toString","toggle","unCheckAllInputsExcept","allInputsWithSameName","name","$inputWithSameName","hasSameFormOwner","form","unCheckExclusiveInputs","allInputsWithSameNameAndExclusiveBehaviour","$exclusiveInput","$clickedInput","target","HTMLInputElement","type","hasAriaControls","hasBehaviourExclusive","moduleName"],"mappings":"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"}