{"version":3,"file":"rich-text-editor.mjs","sources":["../../../../src/moj/components/rich-text-editor/rich-text-editor.mjs"],"sourcesContent":["import { ConfigurableComponent } from 'govuk-frontend'\n\n/**\n * @augments {ConfigurableComponent<RichTextEditorConfig>}\n */\nexport class RichTextEditor extends ConfigurableComponent {\n  /**\n   * @param {Element | null} $root - HTML element to use for rich text editor\n   * @param {RichTextEditorConfig} config\n   */\n  constructor($root, config = {}) {\n    super($root, config)\n\n    if (!RichTextEditor.isSupported()) {\n      return this\n    }\n\n    const $textarea = this.$root.querySelector('.govuk-textarea')\n    if (!$textarea || !($textarea instanceof HTMLTextAreaElement)) {\n      return this\n    }\n\n    this.$textarea = $textarea\n\n    this.createToolbar()\n    this.hideDefault()\n    this.configureToolbar()\n\n    this.keys = {\n      left: 37,\n      right: 39,\n      up: 38,\n      down: 40\n    }\n\n    this.$content.addEventListener('input', this.onEditorInput.bind(this))\n\n    this.$root\n      .querySelector('label')\n      .addEventListener('click', this.onLabelClick.bind(this))\n\n    this.$toolbar.addEventListener('keydown', this.onToolbarKeydown.bind(this))\n  }\n\n  /**\n   * @param {KeyboardEvent} event - Click event\n   */\n  onToolbarKeydown(event) {\n    let $focusableButton\n    switch (event.keyCode) {\n      case this.keys.right:\n      case this.keys.down: {\n        $focusableButton = this.$buttons.find(\n          (button) => button.getAttribute('tabindex') === '0'\n        )\n\n        if ($focusableButton) {\n          const $nextButton = $focusableButton.nextElementSibling\n\n          if ($nextButton && $nextButton instanceof HTMLButtonElement) {\n            $nextButton.focus()\n            $focusableButton.setAttribute('tabindex', '-1')\n            $nextButton.setAttribute('tabindex', '0')\n          }\n        }\n\n        break\n      }\n\n      case this.keys.left:\n      case this.keys.up: {\n        $focusableButton = this.$buttons.find(\n          (button) => button.getAttribute('tabindex') === '0'\n        )\n\n        if ($focusableButton) {\n          const $previousButton = $focusableButton.previousElementSibling\n\n          if ($previousButton && $previousButton instanceof HTMLButtonElement) {\n            $previousButton.focus()\n            $focusableButton.setAttribute('tabindex', '-1')\n            $previousButton.setAttribute('tabindex', '0')\n          }\n        }\n\n        break\n      }\n    }\n  }\n\n  getToolbarHtml() {\n    let html = ''\n\n    html += '<div class=\"moj-rich-text-editor__toolbar\" role=\"toolbar\">'\n\n    if (this.config.toolbar.bold) {\n      html +=\n        '<button class=\"moj-rich-text-editor__toolbar-button moj-rich-text-editor__toolbar-button--bold\" type=\"button\" data-command=\"bold\"><span class=\"govuk-visually-hidden\">Bold</span></button>'\n    }\n\n    if (this.config.toolbar.italic) {\n      html +=\n        '<button class=\"moj-rich-text-editor__toolbar-button moj-rich-text-editor__toolbar-button--italic\" type=\"button\" data-command=\"italic\"><span class=\"govuk-visually-hidden\">Italic</span></button>'\n    }\n\n    if (this.config.toolbar.underline) {\n      html +=\n        '<button class=\"moj-rich-text-editor__toolbar-button moj-rich-text-editor__toolbar-button--underline\" type=\"button\" data-command=\"underline\"><span class=\"govuk-visually-hidden\">Underline</span></button>'\n    }\n\n    if (this.config.toolbar.bullets) {\n      html +=\n        '<button class=\"moj-rich-text-editor__toolbar-button moj-rich-text-editor__toolbar-button--unordered-list\" type=\"button\" data-command=\"insertUnorderedList\"><span class=\"govuk-visually-hidden\">Unordered list</span></button>'\n    }\n\n    if (this.config.toolbar.numbers) {\n      html +=\n        '<button class=\"moj-rich-text-editor__toolbar-button moj-rich-text-editor__toolbar-button--ordered-list\" type=\"button\" data-command=\"insertOrderedList\"><span class=\"govuk-visually-hidden\">Ordered list</span></button>'\n    }\n\n    html += '</div>'\n    return html\n  }\n\n  getEnhancedHtml() {\n    return `${this.getToolbarHtml()}<div class=\"govuk-textarea moj-rich-text-editor__content\" contenteditable=\"true\" spellcheck=\"false\"></div>`\n  }\n\n  hideDefault() {\n    this.$textarea.classList.add('govuk-visually-hidden')\n    this.$textarea.setAttribute('aria-hidden', 'true')\n    this.$textarea.setAttribute('tabindex', '-1')\n  }\n\n  createToolbar() {\n    this.$toolbar = document.createElement('div')\n    this.$toolbar.className = 'moj-rich-text-editor'\n    this.$toolbar.innerHTML = this.getEnhancedHtml()\n    this.$root.append(this.$toolbar)\n\n    this.$content = /** @type {HTMLElement} */ (\n      this.$root.querySelector('.moj-rich-text-editor__content')\n    )\n\n    this.$content.innerHTML = this.$textarea.value\n  }\n\n  configureToolbar() {\n    this.$buttons = Array.from(\n      /** @type {NodeListOf<HTMLButtonElement>} */\n      (this.$root.querySelectorAll('.moj-rich-text-editor__toolbar-button'))\n    )\n\n    this.$buttons.forEach(($button, index) => {\n      $button.setAttribute('tabindex', !index ? '0' : '-1')\n      $button.addEventListener('click', this.onButtonClick.bind(this))\n    })\n  }\n\n  /**\n   * @param {MouseEvent} event - Click event\n   */\n  onButtonClick(event) {\n    if (!(event.currentTarget instanceof HTMLElement)) {\n      return\n    }\n\n    document.execCommand(\n      event.currentTarget.getAttribute('data-command'),\n      false,\n      undefined\n    )\n  }\n\n  getContent() {\n    return this.$content.innerHTML\n  }\n\n  onEditorInput() {\n    this.updateTextarea()\n  }\n\n  updateTextarea() {\n    document.execCommand('defaultParagraphSeparator', false, 'p')\n    this.$textarea.value = this.getContent()\n  }\n\n  /**\n   * @param {MouseEvent} event - Click event\n   */\n  onLabelClick(event) {\n    event.preventDefault()\n    this.$content.focus()\n  }\n\n  static isSupported() {\n    return 'contentEditable' in document.documentElement\n  }\n\n  /**\n   * Name for the component used when initialising using data-module attributes.\n   */\n  static moduleName = 'moj-rich-text-editor'\n\n  /**\n   * Rich text editor config\n   *\n   * @type {RichTextEditorConfig}\n   */\n  static defaults = Object.freeze({\n    toolbar: {\n      bold: false,\n      italic: false,\n      underline: false,\n      bullets: true,\n      numbers: true\n    }\n  })\n\n  /**\n   * Rich text editor config schema\n   *\n   * @satisfies {Schema<RichTextEditorConfig>}\n   */\n  static schema = Object.freeze(\n    /** @type {const} */ ({\n      properties: {\n        toolbar: { type: 'object' }\n      }\n    })\n  )\n}\n\n/**\n * Rich text editor config\n *\n * @typedef {object} RichTextEditorConfig\n * @property {RichTextEditorToolbar} [toolbar] - Toolbar options\n */\n\n/**\n * Rich text editor toolbar options\n *\n * @typedef {object} RichTextEditorToolbar\n * @property {boolean} [bold] - Show the bold button\n * @property {boolean} [italic] - Show the italic button\n * @property {boolean} [underline] - Show the underline button\n * @property {boolean} [bullets] - Show the bullets button\n * @property {boolean} [numbers] - Show the numbers button\n */\n\n/**\n * @import { Schema } from 'govuk-frontend/dist/govuk/common/configuration.mjs'\n */\n"],"names":["RichTextEditor","ConfigurableComponent","constructor","$root","config","isSupported","$textarea","querySelector","HTMLTextAreaElement","createToolbar","hideDefault","configureToolbar","keys","left","right","up","down","$content","addEventListener","onEditorInput","bind","onLabelClick","$toolbar","onToolbarKeydown","event","$focusableButton","keyCode","$buttons","find","button","getAttribute","$nextButton","nextElementSibling","HTMLButtonElement","focus","setAttribute","$previousButton","previousElementSibling","getToolbarHtml","html","toolbar","bold","italic","underline","bullets","numbers","getEnhancedHtml","classList","add","document","createElement","className","innerHTML","append","value","Array","from","querySelectorAll","forEach","$button","index","onButtonClick","currentTarget","HTMLElement","execCommand","undefined","getContent","updateTextarea","preventDefault","documentElement","moduleName","defaults","Object","freeze","schema","properties","type"],"mappings":"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"}