@ministryofjustice/frontend 5.0.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/moj/all.bundle.js +1549 -1062
- package/moj/all.bundle.js.map +1 -1
- package/moj/all.bundle.mjs +1845 -1054
- package/moj/all.bundle.mjs.map +1 -1
- package/moj/all.mjs +7 -90
- package/moj/all.mjs.map +1 -1
- package/moj/all.scss +1 -0
- package/moj/all.scss.map +1 -1
- package/moj/common/index.mjs +57 -0
- package/moj/common/index.mjs.map +1 -0
- package/moj/common/moj-frontend-version.mjs +14 -0
- package/moj/common/moj-frontend-version.mjs.map +1 -0
- package/moj/components/add-another/add-another.bundle.js +105 -76
- package/moj/components/add-another/add-another.bundle.js.map +1 -1
- package/moj/components/add-another/add-another.bundle.mjs +222 -71
- package/moj/components/add-another/add-another.bundle.mjs.map +1 -1
- package/moj/components/add-another/add-another.mjs +103 -72
- package/moj/components/add-another/add-another.mjs.map +1 -1
- package/moj/components/alert/alert.bundle.js +115 -191
- package/moj/components/alert/alert.bundle.js.map +1 -1
- package/moj/components/alert/alert.bundle.mjs +354 -186
- package/moj/components/alert/alert.bundle.mjs.map +1 -1
- package/moj/components/alert/alert.mjs +55 -140
- package/moj/components/alert/alert.mjs.map +1 -1
- package/moj/components/button-menu/README.md +3 -1
- package/moj/components/button-menu/button-menu.bundle.js +91 -120
- package/moj/components/button-menu/button-menu.bundle.js.map +1 -1
- package/moj/components/button-menu/button-menu.bundle.mjs +329 -114
- package/moj/components/button-menu/button-menu.bundle.mjs.map +1 -1
- package/moj/components/button-menu/button-menu.mjs +89 -116
- package/moj/components/button-menu/button-menu.mjs.map +1 -1
- package/moj/components/date-picker/date-picker.bundle.js +174 -154
- package/moj/components/date-picker/date-picker.bundle.js.map +1 -1
- package/moj/components/date-picker/date-picker.bundle.mjs +411 -147
- package/moj/components/date-picker/date-picker.bundle.mjs.map +1 -1
- package/moj/components/date-picker/date-picker.mjs +172 -150
- package/moj/components/date-picker/date-picker.mjs.map +1 -1
- package/moj/components/filter/template.njk +1 -1
- package/moj/components/filter-toggle-button/filter-toggle-button.bundle.js +133 -44
- package/moj/components/filter-toggle-button/filter-toggle-button.bundle.js.map +1 -1
- package/moj/components/filter-toggle-button/filter-toggle-button.bundle.mjs +374 -41
- package/moj/components/filter-toggle-button/filter-toggle-button.bundle.mjs.map +1 -1
- package/moj/components/filter-toggle-button/filter-toggle-button.mjs +131 -40
- package/moj/components/filter-toggle-button/filter-toggle-button.mjs.map +1 -1
- package/moj/components/form-validator/form-validator.bundle.js +159 -69
- package/moj/components/form-validator/form-validator.bundle.js.map +1 -1
- package/moj/components/form-validator/form-validator.bundle.mjs +399 -65
- package/moj/components/form-validator/form-validator.bundle.mjs.map +1 -1
- package/moj/components/form-validator/form-validator.mjs +134 -54
- package/moj/components/form-validator/form-validator.mjs.map +1 -1
- package/moj/components/multi-file-upload/multi-file-upload.bundle.js +291 -117
- package/moj/components/multi-file-upload/multi-file-upload.bundle.js.map +1 -1
- package/moj/components/multi-file-upload/multi-file-upload.bundle.mjs +527 -109
- package/moj/components/multi-file-upload/multi-file-upload.bundle.mjs.map +1 -1
- package/moj/components/multi-file-upload/multi-file-upload.mjs +288 -101
- package/moj/components/multi-file-upload/multi-file-upload.mjs.map +1 -1
- package/moj/components/multi-file-upload/template.njk +1 -1
- package/moj/components/multi-select/multi-select.bundle.js +106 -41
- package/moj/components/multi-select/multi-select.bundle.js.map +1 -1
- package/moj/components/multi-select/multi-select.bundle.mjs +346 -37
- package/moj/components/multi-select/multi-select.bundle.mjs.map +1 -1
- package/moj/components/multi-select/multi-select.mjs +104 -37
- package/moj/components/multi-select/multi-select.mjs.map +1 -1
- package/moj/components/password-reveal/_password-reveal.scss +3 -1
- package/moj/components/password-reveal/_password-reveal.scss.map +1 -1
- package/moj/components/password-reveal/password-reveal.bundle.js +32 -29
- package/moj/components/password-reveal/password-reveal.bundle.js.map +1 -1
- package/moj/components/password-reveal/password-reveal.bundle.mjs +149 -24
- package/moj/components/password-reveal/password-reveal.bundle.mjs.map +1 -1
- package/moj/components/password-reveal/password-reveal.mjs +30 -25
- package/moj/components/password-reveal/password-reveal.mjs.map +1 -1
- package/moj/components/rich-text-editor/README.md +4 -3
- package/moj/components/rich-text-editor/rich-text-editor.bundle.js +127 -62
- package/moj/components/rich-text-editor/rich-text-editor.bundle.js.map +1 -1
- package/moj/components/rich-text-editor/rich-text-editor.bundle.mjs +367 -58
- package/moj/components/rich-text-editor/rich-text-editor.bundle.mjs.map +1 -1
- package/moj/components/rich-text-editor/rich-text-editor.mjs +125 -58
- package/moj/components/rich-text-editor/rich-text-editor.mjs.map +1 -1
- package/moj/components/search-toggle/search-toggle.bundle.js +94 -26
- package/moj/components/search-toggle/search-toggle.bundle.js.map +1 -1
- package/moj/components/search-toggle/search-toggle.bundle.mjs +334 -22
- package/moj/components/search-toggle/search-toggle.bundle.mjs.map +1 -1
- package/moj/components/search-toggle/search-toggle.mjs +92 -22
- package/moj/components/search-toggle/search-toggle.mjs.map +1 -1
- package/moj/components/sortable-table/sortable-table.bundle.js +151 -83
- package/moj/components/sortable-table/sortable-table.bundle.js.map +1 -1
- package/moj/components/sortable-table/sortable-table.bundle.mjs +390 -78
- package/moj/components/sortable-table/sortable-table.bundle.mjs.map +1 -1
- package/moj/components/sortable-table/sortable-table.mjs +149 -79
- package/moj/components/sortable-table/sortable-table.mjs.map +1 -1
- package/moj/core/_all.scss +3 -0
- package/moj/core/_all.scss.map +1 -0
- package/moj/core/_moj-frontend-properties.scss +7 -0
- package/moj/core/_moj-frontend-properties.scss.map +1 -0
- package/moj/filters/prototype-kit-13-filters.js +4 -3
- package/moj/helpers.bundle.js +22 -77
- package/moj/helpers.bundle.js.map +1 -1
- package/moj/helpers.bundle.mjs +23 -74
- package/moj/helpers.bundle.mjs.map +1 -1
- package/moj/helpers.mjs +23 -74
- package/moj/helpers.mjs.map +1 -1
- package/moj/moj-frontend.min.css +1 -1
- package/moj/moj-frontend.min.css.map +1 -1
- package/moj/moj-frontend.min.js +1 -1
- package/moj/moj-frontend.min.js.map +1 -1
- package/package.json +1 -1
- package/moj/version.bundle.js +0 -12
- package/moj/version.bundle.js.map +0 -1
- package/moj/version.bundle.mjs +0 -4
- package/moj/version.bundle.mjs.map +0 -1
- package/moj/version.mjs +0 -4
- package/moj/version.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rich-text-editor.bundle.js","sources":["../../../../src/moj/components/rich-text-editor/rich-text-editor.mjs"],"sourcesContent":["export class RichTextEditor {\n /**\n * @param {RichTextEditorConfig} options\n */\n constructor(options = {}) {\n const { textarea } = options\n\n if (\n !textarea ||\n !textarea.parentElement ||\n !(textarea instanceof HTMLTextAreaElement) ||\n !('contentEditable' in document.documentElement)\n ) {\n return this\n }\n\n options.toolbar = options.toolbar || {\n bold: false,\n italic: false,\n underline: false,\n bullets: true,\n numbers: true\n }\n\n this.textarea = textarea\n this.container = this.textarea.parentElement\n this.options = options\n\n if (this.container.hasAttribute('data-rich-text-editor-init')) {\n return this\n }\n\n this.container.setAttribute('data-rich-text-editor-init', '')\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.container\n .querySelector('label')\n .addEventListener('click', this.onLabelClick.bind(this))\n\n this.toolbar.addEventListener('keydown', this.onToolbarKeydown.bind(this))\n }\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 const nextButton = focusableButton.nextElementSibling\n if (nextButton instanceof HTMLButtonElement) {\n nextButton.focus()\n focusableButton.setAttribute('tabindex', '-1')\n nextButton.setAttribute('tabindex', '0')\n }\n break\n }\n case this.keys.left:\n case this.keys.up: {\n focusableButton = this.buttons.find(\n (button) => button.getAttribute('tabindex') === '0'\n )\n const previousButton = focusableButton.previousElementSibling\n if (previousButton instanceof HTMLButtonElement) {\n previousButton.focus()\n focusableButton.setAttribute('tabindex', '-1')\n previousButton.setAttribute('tabindex', '0')\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.options.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.options.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.options.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.options.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.options.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.container.append(this.toolbar)\n\n this.content = /** @type {HTMLDivElement} */ (\n this.container.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.container.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 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 onLabelClick(event) {\n event.preventDefault()\n this.content.focus()\n }\n}\n"],"names":["RichTextEditor","constructor","options","textarea","parentElement","HTMLTextAreaElement","document","documentElement","toolbar","bold","italic","underline","bullets","numbers","container","hasAttribute","setAttribute","createToolbar","hideDefault","configureToolbar","keys","left","right","up","down","content","addEventListener","onEditorInput","bind","querySelector","onLabelClick","onToolbarKeydown","event","focusableButton","keyCode","buttons","find","button","getAttribute","nextButton","nextElementSibling","HTMLButtonElement","focus","previousButton","previousElementSibling","getToolbarHtml","html","getEnhancedHtml","classList","add","createElement","className","innerHTML","append","$textarea","value","Array","from","querySelectorAll","forEach","index","onButtonClick","currentTarget","HTMLElement","execCommand","undefined","getContent","updateTextarea","preventDefault"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rich-text-editor.bundle.js","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":";;;;;;EAEA;EACA;EACA;EACO,MAAMA,cAAc,SAASC,mCAAqB,CAAC;EACxD;EACF;EACA;EACA;EACEC,EAAAA,WAAWA,CAACC,KAAK,EAAEC,MAAM,GAAG,EAAE,EAAE;EAC9B,IAAA,KAAK,CAACD,KAAK,EAAEC,MAAM,CAAC;EAEpB,IAAA,IAAI,CAACJ,cAAc,CAACK,WAAW,EAAE,EAAE;EACjC,MAAA,OAAO,IAAI;EACb;MAEA,MAAMC,SAAS,GAAG,IAAI,CAACH,KAAK,CAACI,aAAa,CAAC,iBAAiB,CAAC;MAC7D,IAAI,CAACD,SAAS,IAAI,EAAEA,SAAS,YAAYE,mBAAmB,CAAC,EAAE;EAC7D,MAAA,OAAO,IAAI;EACb;MAEA,IAAI,CAACF,SAAS,GAAGA,SAAS;MAE1B,IAAI,CAACG,aAAa,EAAE;MACpB,IAAI,CAACC,WAAW,EAAE;MAClB,IAAI,CAACC,gBAAgB,EAAE;MAEvB,IAAI,CAACC,IAAI,GAAG;EACVC,MAAAA,IAAI,EAAE,EAAE;EACRC,MAAAA,KAAK,EAAE,EAAE;EACTC,MAAAA,EAAE,EAAE,EAAE;EACNC,MAAAA,IAAI,EAAE;OACP;EAED,IAAA,IAAI,CAACC,QAAQ,CAACC,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAACC,aAAa,CAACC,IAAI,CAAC,IAAI,CAAC,CAAC;MAEtE,IAAI,CAACjB,KAAK,CACPI,aAAa,CAAC,OAAO,CAAC,CACtBW,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAACG,YAAY,CAACD,IAAI,CAAC,IAAI,CAAC,CAAC;EAE1D,IAAA,IAAI,CAACE,QAAQ,CAACJ,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAACK,gBAAgB,CAACH,IAAI,CAAC,IAAI,CAAC,CAAC;EAC7E;;EAEA;EACF;EACA;IACEG,gBAAgBA,CAACC,KAAK,EAAE;EACtB,IAAA,IAAIC,gBAAgB;MACpB,QAAQD,KAAK,CAACE,OAAO;EACnB,MAAA,KAAK,IAAI,CAACd,IAAI,CAACE,KAAK;EACpB,MAAA,KAAK,IAAI,CAACF,IAAI,CAACI,IAAI;EAAE,QAAA;EACnBS,UAAAA,gBAAgB,GAAG,IAAI,CAACE,QAAQ,CAACC,IAAI,CAClCC,MAAM,IAAKA,MAAM,CAACC,YAAY,CAAC,UAAU,CAAC,KAAK,GAClD,CAAC;EAED,UAAA,IAAIL,gBAAgB,EAAE;EACpB,YAAA,MAAMM,WAAW,GAAGN,gBAAgB,CAACO,kBAAkB;EAEvD,YAAA,IAAID,WAAW,IAAIA,WAAW,YAAYE,iBAAiB,EAAE;gBAC3DF,WAAW,CAACG,KAAK,EAAE;EACnBT,cAAAA,gBAAgB,CAACU,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC;EAC/CJ,cAAAA,WAAW,CAACI,YAAY,CAAC,UAAU,EAAE,GAAG,CAAC;EAC3C;EACF;EAEA,UAAA;EACF;EAEA,MAAA,KAAK,IAAI,CAACvB,IAAI,CAACC,IAAI;EACnB,MAAA,KAAK,IAAI,CAACD,IAAI,CAACG,EAAE;EAAE,QAAA;EACjBU,UAAAA,gBAAgB,GAAG,IAAI,CAACE,QAAQ,CAACC,IAAI,CAClCC,MAAM,IAAKA,MAAM,CAACC,YAAY,CAAC,UAAU,CAAC,KAAK,GAClD,CAAC;EAED,UAAA,IAAIL,gBAAgB,EAAE;EACpB,YAAA,MAAMW,eAAe,GAAGX,gBAAgB,CAACY,sBAAsB;EAE/D,YAAA,IAAID,eAAe,IAAIA,eAAe,YAAYH,iBAAiB,EAAE;gBACnEG,eAAe,CAACF,KAAK,EAAE;EACvBT,cAAAA,gBAAgB,CAACU,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC;EAC/CC,cAAAA,eAAe,CAACD,YAAY,CAAC,UAAU,EAAE,GAAG,CAAC;EAC/C;EACF;EAEA,UAAA;EACF;EACF;EACF;EAEAG,EAAAA,cAAcA,GAAG;MACf,IAAIC,IAAI,GAAG,EAAE;EAEbA,IAAAA,IAAI,IAAI,4DAA4D;EAEpE,IAAA,IAAI,IAAI,CAACnC,MAAM,CAACoC,OAAO,CAACC,IAAI,EAAE;EAC5BF,MAAAA,IAAI,IACF,4LAA4L;EAChM;EAEA,IAAA,IAAI,IAAI,CAACnC,MAAM,CAACoC,OAAO,CAACE,MAAM,EAAE;EAC9BH,MAAAA,IAAI,IACF,kMAAkM;EACtM;EAEA,IAAA,IAAI,IAAI,CAACnC,MAAM,CAACoC,OAAO,CAACG,SAAS,EAAE;EACjCJ,MAAAA,IAAI,IACF,2MAA2M;EAC/M;EAEA,IAAA,IAAI,IAAI,CAACnC,MAAM,CAACoC,OAAO,CAACI,OAAO,EAAE;EAC/BL,MAAAA,IAAI,IACF,+NAA+N;EACnO;EAEA,IAAA,IAAI,IAAI,CAACnC,MAAM,CAACoC,OAAO,CAACK,OAAO,EAAE;EAC/BN,MAAAA,IAAI,IACF,yNAAyN;EAC7N;EAEAA,IAAAA,IAAI,IAAI,QAAQ;EAChB,IAAA,OAAOA,IAAI;EACb;EAEAO,EAAAA,eAAeA,GAAG;EAChB,IAAA,OAAO,GAAG,IAAI,CAACR,cAAc,EAAE,CAA4G,0GAAA,CAAA;EAC7I;EAEA5B,EAAAA,WAAWA,GAAG;MACZ,IAAI,CAACJ,SAAS,CAACyC,SAAS,CAACC,GAAG,CAAC,uBAAuB,CAAC;MACrD,IAAI,CAAC1C,SAAS,CAAC6B,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC;MAClD,IAAI,CAAC7B,SAAS,CAAC6B,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC;EAC/C;EAEA1B,EAAAA,aAAaA,GAAG;MACd,IAAI,CAACa,QAAQ,GAAG2B,QAAQ,CAACC,aAAa,CAAC,KAAK,CAAC;EAC7C,IAAA,IAAI,CAAC5B,QAAQ,CAAC6B,SAAS,GAAG,sBAAsB;MAChD,IAAI,CAAC7B,QAAQ,CAAC8B,SAAS,GAAG,IAAI,CAACN,eAAe,EAAE;MAChD,IAAI,CAAC3C,KAAK,CAACkD,MAAM,CAAC,IAAI,CAAC/B,QAAQ,CAAC;MAEhC,IAAI,CAACL,QAAQ;EACX,IAAA,IAAI,CAACd,KAAK,CAACI,aAAa,CAAC,gCAAgC,CAC1D;MAED,IAAI,CAACU,QAAQ,CAACmC,SAAS,GAAG,IAAI,CAAC9C,SAAS,CAACgD,KAAK;EAChD;EAEA3C,EAAAA,gBAAgBA,GAAG;EACjB,IAAA,IAAI,CAACgB,QAAQ,GAAG4B,KAAK,CAACC,IAAI;EAEvB,IAAA,IAAI,CAACrD,KAAK,CAACsD,gBAAgB,CAAC,uCAAuC,CACtE,CAAC;MAED,IAAI,CAAC9B,QAAQ,CAAC+B,OAAO,CAAC,CAACC,OAAO,EAAEC,KAAK,KAAK;QACxCD,OAAO,CAACxB,YAAY,CAAC,UAAU,EAAE,CAACyB,KAAK,GAAG,GAAG,GAAG,IAAI,CAAC;EACrDD,MAAAA,OAAO,CAACzC,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC2C,aAAa,CAACzC,IAAI,CAAC,IAAI,CAAC,CAAC;EAClE,KAAC,CAAC;EACJ;;EAEA;EACF;EACA;IACEyC,aAAaA,CAACrC,KAAK,EAAE;EACnB,IAAA,IAAI,EAAEA,KAAK,CAACsC,aAAa,YAAYC,WAAW,CAAC,EAAE;EACjD,MAAA;EACF;EAEAd,IAAAA,QAAQ,CAACe,WAAW,CAClBxC,KAAK,CAACsC,aAAa,CAAChC,YAAY,CAAC,cAAc,CAAC,EAChD,KAAK,EACLmC,SACF,CAAC;EACH;EAEAC,EAAAA,UAAUA,GAAG;EACX,IAAA,OAAO,IAAI,CAACjD,QAAQ,CAACmC,SAAS;EAChC;EAEAjC,EAAAA,aAAaA,GAAG;MACd,IAAI,CAACgD,cAAc,EAAE;EACvB;EAEAA,EAAAA,cAAcA,GAAG;MACflB,QAAQ,CAACe,WAAW,CAAC,2BAA2B,EAAE,KAAK,EAAE,GAAG,CAAC;MAC7D,IAAI,CAAC1D,SAAS,CAACgD,KAAK,GAAG,IAAI,CAACY,UAAU,EAAE;EAC1C;;EAEA;EACF;EACA;IACE7C,YAAYA,CAACG,KAAK,EAAE;MAClBA,KAAK,CAAC4C,cAAc,EAAE;EACtB,IAAA,IAAI,CAACnD,QAAQ,CAACiB,KAAK,EAAE;EACvB;IAEA,OAAO7B,WAAWA,GAAG;EACnB,IAAA,OAAO,iBAAiB,IAAI4C,QAAQ,CAACoB,eAAe;EACtD;;EAEA;EACF;EACA;EA8BA;;EAEA;EACA;EACA;EACA;EACA;EACA;;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;EAEA;EACA;EACA;EAxParE,cAAc,CAqMlBsE,UAAU,GAAG,sBAAsB;EAE1C;EACF;EACA;EACA;EACA;EA3MatE,cAAc,CA4MlBuE,QAAQ,GAAGC,MAAM,CAACC,MAAM,CAAC;EAC9BjC,EAAAA,OAAO,EAAE;EACPC,IAAAA,IAAI,EAAE,KAAK;EACXC,IAAAA,MAAM,EAAE,KAAK;EACbC,IAAAA,SAAS,EAAE,KAAK;EAChBC,IAAAA,OAAO,EAAE,IAAI;EACbC,IAAAA,OAAO,EAAE;EACX;EACF,CAAC,CAAC;EAEF;EACF;EACA;EACA;EACA;EA1Na7C,cAAc,CA2NlB0E,MAAM,GAAGF,MAAM,CAACC,MAAM,qBACL;EACpBE,EAAAA,UAAU,EAAE;EACVnC,IAAAA,OAAO,EAAE;EAAEoC,MAAAA,IAAI,EAAE;EAAS;EAC5B;EACF,CACF,CAAC;;;;;;;;"}
|
|
@@ -1,28 +1,265 @@
|
|
|
1
|
-
|
|
1
|
+
function isInitialised($root, moduleName) {
|
|
2
|
+
return $root instanceof HTMLElement && $root.hasAttribute(`data-${moduleName}-init`);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Checks if GOV.UK Frontend is supported on this page
|
|
7
|
+
*
|
|
8
|
+
* Some browsers will load and run our JavaScript but GOV.UK Frontend
|
|
9
|
+
* won't be supported.
|
|
10
|
+
*
|
|
11
|
+
* @param {HTMLElement | null} [$scope] - (internal) `<body>` HTML element checked for browser support
|
|
12
|
+
* @returns {boolean} Whether GOV.UK Frontend is supported on this page
|
|
13
|
+
*/
|
|
14
|
+
function isSupported($scope = document.body) {
|
|
15
|
+
if (!$scope) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return $scope.classList.contains('govuk-frontend-supported');
|
|
19
|
+
}
|
|
20
|
+
function isArray(option) {
|
|
21
|
+
return Array.isArray(option);
|
|
22
|
+
}
|
|
23
|
+
function isObject(option) {
|
|
24
|
+
return !!option && typeof option === 'object' && !isArray(option);
|
|
25
|
+
}
|
|
26
|
+
function formatErrorMessage(Component, message) {
|
|
27
|
+
return `${Component.moduleName}: ${message}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
class GOVUKFrontendError extends Error {
|
|
31
|
+
constructor(...args) {
|
|
32
|
+
super(...args);
|
|
33
|
+
this.name = 'GOVUKFrontendError';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
class SupportError extends GOVUKFrontendError {
|
|
37
|
+
/**
|
|
38
|
+
* Checks if GOV.UK Frontend is supported on this page
|
|
39
|
+
*
|
|
40
|
+
* @param {HTMLElement | null} [$scope] - HTML element `<body>` checked for browser support
|
|
41
|
+
*/
|
|
42
|
+
constructor($scope = document.body) {
|
|
43
|
+
const supportMessage = 'noModule' in HTMLScriptElement.prototype ? 'GOV.UK Frontend initialised without `<body class="govuk-frontend-supported">` from template `<script>` snippet' : 'GOV.UK Frontend is not supported in this browser';
|
|
44
|
+
super($scope ? supportMessage : 'GOV.UK Frontend initialised without `<script type="module">`');
|
|
45
|
+
this.name = 'SupportError';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
class ConfigError extends GOVUKFrontendError {
|
|
49
|
+
constructor(...args) {
|
|
50
|
+
super(...args);
|
|
51
|
+
this.name = 'ConfigError';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
class ElementError extends GOVUKFrontendError {
|
|
55
|
+
constructor(messageOrOptions) {
|
|
56
|
+
let message = typeof messageOrOptions === 'string' ? messageOrOptions : '';
|
|
57
|
+
if (typeof messageOrOptions === 'object') {
|
|
58
|
+
const {
|
|
59
|
+
component,
|
|
60
|
+
identifier,
|
|
61
|
+
element,
|
|
62
|
+
expectedType
|
|
63
|
+
} = messageOrOptions;
|
|
64
|
+
message = identifier;
|
|
65
|
+
message += element ? ` is not of type ${expectedType != null ? expectedType : 'HTMLElement'}` : ' not found';
|
|
66
|
+
message = formatErrorMessage(component, message);
|
|
67
|
+
}
|
|
68
|
+
super(message);
|
|
69
|
+
this.name = 'ElementError';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
class InitError extends GOVUKFrontendError {
|
|
73
|
+
constructor(componentOrMessage) {
|
|
74
|
+
const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
|
|
75
|
+
super(message);
|
|
76
|
+
this.name = 'InitError';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
class Component {
|
|
81
|
+
/**
|
|
82
|
+
* Returns the root element of the component
|
|
83
|
+
*
|
|
84
|
+
* @protected
|
|
85
|
+
* @returns {RootElementType} - the root element of component
|
|
86
|
+
*/
|
|
87
|
+
get $root() {
|
|
88
|
+
return this._$root;
|
|
89
|
+
}
|
|
90
|
+
constructor($root) {
|
|
91
|
+
this._$root = void 0;
|
|
92
|
+
const childConstructor = this.constructor;
|
|
93
|
+
if (typeof childConstructor.moduleName !== 'string') {
|
|
94
|
+
throw new InitError(`\`moduleName\` not defined in component`);
|
|
95
|
+
}
|
|
96
|
+
if (!($root instanceof childConstructor.elementType)) {
|
|
97
|
+
throw new ElementError({
|
|
98
|
+
element: $root,
|
|
99
|
+
component: childConstructor,
|
|
100
|
+
identifier: 'Root element (`$root`)',
|
|
101
|
+
expectedType: childConstructor.elementType.name
|
|
102
|
+
});
|
|
103
|
+
} else {
|
|
104
|
+
this._$root = $root;
|
|
105
|
+
}
|
|
106
|
+
childConstructor.checkSupport();
|
|
107
|
+
this.checkInitialised();
|
|
108
|
+
const moduleName = childConstructor.moduleName;
|
|
109
|
+
this.$root.setAttribute(`data-${moduleName}-init`, '');
|
|
110
|
+
}
|
|
111
|
+
checkInitialised() {
|
|
112
|
+
const constructor = this.constructor;
|
|
113
|
+
const moduleName = constructor.moduleName;
|
|
114
|
+
if (moduleName && isInitialised(this.$root, moduleName)) {
|
|
115
|
+
throw new InitError(constructor);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
static checkSupport() {
|
|
119
|
+
if (!isSupported()) {
|
|
120
|
+
throw new SupportError();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* @typedef ChildClass
|
|
127
|
+
* @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
|
|
128
|
+
*/
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @typedef {typeof Component & ChildClass} ChildClassConstructor
|
|
132
|
+
*/
|
|
133
|
+
Component.elementType = HTMLElement;
|
|
134
|
+
|
|
135
|
+
const configOverride = Symbol.for('configOverride');
|
|
136
|
+
class ConfigurableComponent extends Component {
|
|
137
|
+
[configOverride](param) {
|
|
138
|
+
return {};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Returns the root element of the component
|
|
143
|
+
*
|
|
144
|
+
* @protected
|
|
145
|
+
* @returns {ConfigurationType} - the root element of component
|
|
146
|
+
*/
|
|
147
|
+
get config() {
|
|
148
|
+
return this._config;
|
|
149
|
+
}
|
|
150
|
+
constructor($root, config) {
|
|
151
|
+
super($root);
|
|
152
|
+
this._config = void 0;
|
|
153
|
+
const childConstructor = this.constructor;
|
|
154
|
+
if (!isObject(childConstructor.defaults)) {
|
|
155
|
+
throw new ConfigError(formatErrorMessage(childConstructor, 'Config passed as parameter into constructor but no defaults defined'));
|
|
156
|
+
}
|
|
157
|
+
const datasetConfig = normaliseDataset(childConstructor, this._$root.dataset);
|
|
158
|
+
this._config = mergeConfigs(childConstructor.defaults, config != null ? config : {}, this[configOverride](datasetConfig), datasetConfig);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function normaliseString(value, property) {
|
|
162
|
+
const trimmedValue = value ? value.trim() : '';
|
|
163
|
+
let output;
|
|
164
|
+
let outputType = property == null ? void 0 : property.type;
|
|
165
|
+
if (!outputType) {
|
|
166
|
+
if (['true', 'false'].includes(trimmedValue)) {
|
|
167
|
+
outputType = 'boolean';
|
|
168
|
+
}
|
|
169
|
+
if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
|
|
170
|
+
outputType = 'number';
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
switch (outputType) {
|
|
174
|
+
case 'boolean':
|
|
175
|
+
output = trimmedValue === 'true';
|
|
176
|
+
break;
|
|
177
|
+
case 'number':
|
|
178
|
+
output = Number(trimmedValue);
|
|
179
|
+
break;
|
|
180
|
+
default:
|
|
181
|
+
output = value;
|
|
182
|
+
}
|
|
183
|
+
return output;
|
|
184
|
+
}
|
|
185
|
+
function normaliseDataset(Component, dataset) {
|
|
186
|
+
if (!isObject(Component.schema)) {
|
|
187
|
+
throw new ConfigError(formatErrorMessage(Component, 'Config passed as parameter into constructor but no schema defined'));
|
|
188
|
+
}
|
|
189
|
+
const out = {};
|
|
190
|
+
const entries = Object.entries(Component.schema.properties);
|
|
191
|
+
for (const entry of entries) {
|
|
192
|
+
const [namespace, property] = entry;
|
|
193
|
+
const field = namespace.toString();
|
|
194
|
+
if (field in dataset) {
|
|
195
|
+
out[field] = normaliseString(dataset[field], property);
|
|
196
|
+
}
|
|
197
|
+
if ((property == null ? void 0 : property.type) === 'object') {
|
|
198
|
+
out[field] = extractConfigByNamespace(Component.schema, dataset, namespace);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return out;
|
|
202
|
+
}
|
|
203
|
+
function mergeConfigs(...configObjects) {
|
|
204
|
+
const formattedConfigObject = {};
|
|
205
|
+
for (const configObject of configObjects) {
|
|
206
|
+
for (const key of Object.keys(configObject)) {
|
|
207
|
+
const option = formattedConfigObject[key];
|
|
208
|
+
const override = configObject[key];
|
|
209
|
+
if (isObject(option) && isObject(override)) {
|
|
210
|
+
formattedConfigObject[key] = mergeConfigs(option, override);
|
|
211
|
+
} else {
|
|
212
|
+
formattedConfigObject[key] = override;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return formattedConfigObject;
|
|
217
|
+
}
|
|
218
|
+
function extractConfigByNamespace(schema, dataset, namespace) {
|
|
219
|
+
const property = schema.properties[namespace];
|
|
220
|
+
if ((property == null ? void 0 : property.type) !== 'object') {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const newObject = {
|
|
224
|
+
[namespace]: {}
|
|
225
|
+
};
|
|
226
|
+
for (const [key, value] of Object.entries(dataset)) {
|
|
227
|
+
let current = newObject;
|
|
228
|
+
const keyParts = key.split('.');
|
|
229
|
+
for (const [index, name] of keyParts.entries()) {
|
|
230
|
+
if (isObject(current)) {
|
|
231
|
+
if (index < keyParts.length - 1) {
|
|
232
|
+
if (!isObject(current[name])) {
|
|
233
|
+
current[name] = {};
|
|
234
|
+
}
|
|
235
|
+
current = current[name];
|
|
236
|
+
} else if (key !== namespace) {
|
|
237
|
+
current[name] = normaliseString(value);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return newObject[namespace];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* @augments {ConfigurableComponent<RichTextEditorConfig>}
|
|
247
|
+
*/
|
|
248
|
+
class RichTextEditor extends ConfigurableComponent {
|
|
2
249
|
/**
|
|
3
|
-
* @param {
|
|
250
|
+
* @param {Element | null} $root - HTML element to use for rich text editor
|
|
251
|
+
* @param {RichTextEditorConfig} config
|
|
4
252
|
*/
|
|
5
|
-
constructor(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} = options;
|
|
9
|
-
if (!textarea || !textarea.parentElement || !(textarea instanceof HTMLTextAreaElement) || !('contentEditable' in document.documentElement)) {
|
|
253
|
+
constructor($root, config = {}) {
|
|
254
|
+
super($root, config);
|
|
255
|
+
if (!RichTextEditor.isSupported()) {
|
|
10
256
|
return this;
|
|
11
257
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
italic: false,
|
|
15
|
-
underline: false,
|
|
16
|
-
bullets: true,
|
|
17
|
-
numbers: true
|
|
18
|
-
};
|
|
19
|
-
this.textarea = textarea;
|
|
20
|
-
this.container = this.textarea.parentElement;
|
|
21
|
-
this.options = options;
|
|
22
|
-
if (this.container.hasAttribute('data-rich-text-editor-init')) {
|
|
258
|
+
const $textarea = this.$root.querySelector('.govuk-textarea');
|
|
259
|
+
if (!$textarea || !($textarea instanceof HTMLTextAreaElement)) {
|
|
23
260
|
return this;
|
|
24
261
|
}
|
|
25
|
-
this
|
|
262
|
+
this.$textarea = $textarea;
|
|
26
263
|
this.createToolbar();
|
|
27
264
|
this.hideDefault();
|
|
28
265
|
this.configureToolbar();
|
|
@@ -32,34 +269,42 @@ class RichTextEditor {
|
|
|
32
269
|
up: 38,
|
|
33
270
|
down: 40
|
|
34
271
|
};
|
|
35
|
-
this
|
|
36
|
-
this.
|
|
37
|
-
this
|
|
272
|
+
this.$content.addEventListener('input', this.onEditorInput.bind(this));
|
|
273
|
+
this.$root.querySelector('label').addEventListener('click', this.onLabelClick.bind(this));
|
|
274
|
+
this.$toolbar.addEventListener('keydown', this.onToolbarKeydown.bind(this));
|
|
38
275
|
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* @param {KeyboardEvent} event - Click event
|
|
279
|
+
*/
|
|
39
280
|
onToolbarKeydown(event) {
|
|
40
|
-
let focusableButton;
|
|
281
|
+
let $focusableButton;
|
|
41
282
|
switch (event.keyCode) {
|
|
42
283
|
case this.keys.right:
|
|
43
284
|
case this.keys.down:
|
|
44
285
|
{
|
|
45
|
-
focusableButton = this
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
nextButton
|
|
49
|
-
|
|
50
|
-
|
|
286
|
+
$focusableButton = this.$buttons.find(button => button.getAttribute('tabindex') === '0');
|
|
287
|
+
if ($focusableButton) {
|
|
288
|
+
const $nextButton = $focusableButton.nextElementSibling;
|
|
289
|
+
if ($nextButton && $nextButton instanceof HTMLButtonElement) {
|
|
290
|
+
$nextButton.focus();
|
|
291
|
+
$focusableButton.setAttribute('tabindex', '-1');
|
|
292
|
+
$nextButton.setAttribute('tabindex', '0');
|
|
293
|
+
}
|
|
51
294
|
}
|
|
52
295
|
break;
|
|
53
296
|
}
|
|
54
297
|
case this.keys.left:
|
|
55
298
|
case this.keys.up:
|
|
56
299
|
{
|
|
57
|
-
focusableButton = this
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
previousButton
|
|
61
|
-
|
|
62
|
-
|
|
300
|
+
$focusableButton = this.$buttons.find(button => button.getAttribute('tabindex') === '0');
|
|
301
|
+
if ($focusableButton) {
|
|
302
|
+
const $previousButton = $focusableButton.previousElementSibling;
|
|
303
|
+
if ($previousButton && $previousButton instanceof HTMLButtonElement) {
|
|
304
|
+
$previousButton.focus();
|
|
305
|
+
$focusableButton.setAttribute('tabindex', '-1');
|
|
306
|
+
$previousButton.setAttribute('tabindex', '0');
|
|
307
|
+
}
|
|
63
308
|
}
|
|
64
309
|
break;
|
|
65
310
|
}
|
|
@@ -68,19 +313,19 @@ class RichTextEditor {
|
|
|
68
313
|
getToolbarHtml() {
|
|
69
314
|
let html = '';
|
|
70
315
|
html += '<div class="moj-rich-text-editor__toolbar" role="toolbar">';
|
|
71
|
-
if (this.
|
|
316
|
+
if (this.config.toolbar.bold) {
|
|
72
317
|
html += '<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>';
|
|
73
318
|
}
|
|
74
|
-
if (this.
|
|
319
|
+
if (this.config.toolbar.italic) {
|
|
75
320
|
html += '<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>';
|
|
76
321
|
}
|
|
77
|
-
if (this.
|
|
322
|
+
if (this.config.toolbar.underline) {
|
|
78
323
|
html += '<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>';
|
|
79
324
|
}
|
|
80
|
-
if (this.
|
|
325
|
+
if (this.config.toolbar.bullets) {
|
|
81
326
|
html += '<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>';
|
|
82
327
|
}
|
|
83
|
-
if (this.
|
|
328
|
+
if (this.config.toolbar.numbers) {
|
|
84
329
|
html += '<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>';
|
|
85
330
|
}
|
|
86
331
|
html += '</div>';
|
|
@@ -90,27 +335,31 @@ class RichTextEditor {
|
|
|
90
335
|
return `${this.getToolbarHtml()}<div class="govuk-textarea moj-rich-text-editor__content" contenteditable="true" spellcheck="false"></div>`;
|
|
91
336
|
}
|
|
92
337
|
hideDefault() {
|
|
93
|
-
this
|
|
94
|
-
this
|
|
95
|
-
this
|
|
338
|
+
this.$textarea.classList.add('govuk-visually-hidden');
|
|
339
|
+
this.$textarea.setAttribute('aria-hidden', 'true');
|
|
340
|
+
this.$textarea.setAttribute('tabindex', '-1');
|
|
96
341
|
}
|
|
97
342
|
createToolbar() {
|
|
98
|
-
this
|
|
99
|
-
this
|
|
100
|
-
this
|
|
101
|
-
this.
|
|
102
|
-
this
|
|
103
|
-
this.
|
|
104
|
-
this
|
|
343
|
+
this.$toolbar = document.createElement('div');
|
|
344
|
+
this.$toolbar.className = 'moj-rich-text-editor';
|
|
345
|
+
this.$toolbar.innerHTML = this.getEnhancedHtml();
|
|
346
|
+
this.$root.append(this.$toolbar);
|
|
347
|
+
this.$content = /** @type {HTMLElement} */
|
|
348
|
+
this.$root.querySelector('.moj-rich-text-editor__content');
|
|
349
|
+
this.$content.innerHTML = this.$textarea.value;
|
|
105
350
|
}
|
|
106
351
|
configureToolbar() {
|
|
107
|
-
this
|
|
108
|
-
this.
|
|
109
|
-
this
|
|
110
|
-
button.setAttribute('tabindex', !index ? '0' : '-1');
|
|
111
|
-
button.addEventListener('click', this.onButtonClick.bind(this));
|
|
352
|
+
this.$buttons = Array.from(/** @type {NodeListOf<HTMLButtonElement>} */
|
|
353
|
+
this.$root.querySelectorAll('.moj-rich-text-editor__toolbar-button'));
|
|
354
|
+
this.$buttons.forEach(($button, index) => {
|
|
355
|
+
$button.setAttribute('tabindex', !index ? '0' : '-1');
|
|
356
|
+
$button.addEventListener('click', this.onButtonClick.bind(this));
|
|
112
357
|
});
|
|
113
358
|
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* @param {MouseEvent} event - Click event
|
|
362
|
+
*/
|
|
114
363
|
onButtonClick(event) {
|
|
115
364
|
if (!(event.currentTarget instanceof HTMLElement)) {
|
|
116
365
|
return;
|
|
@@ -118,20 +367,80 @@ class RichTextEditor {
|
|
|
118
367
|
document.execCommand(event.currentTarget.getAttribute('data-command'), false, undefined);
|
|
119
368
|
}
|
|
120
369
|
getContent() {
|
|
121
|
-
return this
|
|
370
|
+
return this.$content.innerHTML;
|
|
122
371
|
}
|
|
123
372
|
onEditorInput() {
|
|
124
373
|
this.updateTextarea();
|
|
125
374
|
}
|
|
126
375
|
updateTextarea() {
|
|
127
376
|
document.execCommand('defaultParagraphSeparator', false, 'p');
|
|
128
|
-
this
|
|
377
|
+
this.$textarea.value = this.getContent();
|
|
129
378
|
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* @param {MouseEvent} event - Click event
|
|
382
|
+
*/
|
|
130
383
|
onLabelClick(event) {
|
|
131
384
|
event.preventDefault();
|
|
132
|
-
this
|
|
385
|
+
this.$content.focus();
|
|
133
386
|
}
|
|
387
|
+
static isSupported() {
|
|
388
|
+
return 'contentEditable' in document.documentElement;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Name for the component used when initialising using data-module attributes.
|
|
393
|
+
*/
|
|
134
394
|
}
|
|
135
395
|
|
|
396
|
+
/**
|
|
397
|
+
* Rich text editor config
|
|
398
|
+
*
|
|
399
|
+
* @typedef {object} RichTextEditorConfig
|
|
400
|
+
* @property {RichTextEditorToolbar} [toolbar] - Toolbar options
|
|
401
|
+
*/
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Rich text editor toolbar options
|
|
405
|
+
*
|
|
406
|
+
* @typedef {object} RichTextEditorToolbar
|
|
407
|
+
* @property {boolean} [bold] - Show the bold button
|
|
408
|
+
* @property {boolean} [italic] - Show the italic button
|
|
409
|
+
* @property {boolean} [underline] - Show the underline button
|
|
410
|
+
* @property {boolean} [bullets] - Show the bullets button
|
|
411
|
+
* @property {boolean} [numbers] - Show the numbers button
|
|
412
|
+
*/
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* @import { Schema } from 'govuk-frontend/dist/govuk/common/configuration.mjs'
|
|
416
|
+
*/
|
|
417
|
+
RichTextEditor.moduleName = 'moj-rich-text-editor';
|
|
418
|
+
/**
|
|
419
|
+
* Rich text editor config
|
|
420
|
+
*
|
|
421
|
+
* @type {RichTextEditorConfig}
|
|
422
|
+
*/
|
|
423
|
+
RichTextEditor.defaults = Object.freeze({
|
|
424
|
+
toolbar: {
|
|
425
|
+
bold: false,
|
|
426
|
+
italic: false,
|
|
427
|
+
underline: false,
|
|
428
|
+
bullets: true,
|
|
429
|
+
numbers: true
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
/**
|
|
433
|
+
* Rich text editor config schema
|
|
434
|
+
*
|
|
435
|
+
* @satisfies {Schema<RichTextEditorConfig>}
|
|
436
|
+
*/
|
|
437
|
+
RichTextEditor.schema = Object.freeze(/** @type {const} */{
|
|
438
|
+
properties: {
|
|
439
|
+
toolbar: {
|
|
440
|
+
type: 'object'
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
|
|
136
445
|
export { RichTextEditor };
|
|
137
446
|
//# sourceMappingURL=rich-text-editor.bundle.mjs.map
|