@ministryofjustice/frontend 4.0.0 → 5.0.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.
Files changed (245) hide show
  1. package/govuk-prototype-kit.config.json +19 -4
  2. package/moj/_base.scss +2 -0
  3. package/moj/_base.scss.map +1 -0
  4. package/moj/all.bundle.js +2523 -0
  5. package/moj/all.bundle.js.map +1 -0
  6. package/moj/all.bundle.mjs +2502 -0
  7. package/moj/all.bundle.mjs.map +1 -0
  8. package/moj/all.mjs +59 -69
  9. package/moj/all.mjs.map +1 -1
  10. package/moj/all.scss +2 -0
  11. package/moj/all.scss.map +1 -0
  12. package/moj/components/_all.scss +2 -0
  13. package/moj/components/_all.scss.map +1 -0
  14. package/moj/components/action-bar/_action-bar.scss +2 -0
  15. package/moj/components/action-bar/_action-bar.scss.map +1 -0
  16. package/moj/components/add-another/_add-another.scss +2 -0
  17. package/moj/components/add-another/_add-another.scss.map +1 -0
  18. package/moj/components/add-another/add-another.bundle.js +128 -0
  19. package/moj/components/add-another/add-another.bundle.js.map +1 -0
  20. package/moj/components/add-another/add-another.bundle.mjs +120 -0
  21. package/moj/components/add-another/add-another.bundle.mjs.map +1 -0
  22. package/moj/components/add-another/add-another.mjs +112 -99
  23. package/moj/components/add-another/add-another.mjs.map +1 -1
  24. package/moj/components/alert/_alert.scss +4 -0
  25. package/moj/components/alert/_alert.scss.map +1 -0
  26. package/moj/components/alert/alert.bundle.js +330 -0
  27. package/moj/components/alert/alert.bundle.js.map +1 -0
  28. package/moj/components/alert/alert.bundle.mjs +322 -0
  29. package/moj/components/alert/alert.bundle.mjs.map +1 -0
  30. package/moj/components/alert/alert.mjs +181 -217
  31. package/moj/components/alert/alert.mjs.map +1 -1
  32. package/moj/components/alert/{alert.spec.helper.js → alert.spec.helper.bundle.js} +1 -1
  33. package/moj/components/alert/alert.spec.helper.bundle.js.map +1 -0
  34. package/moj/components/alert/alert.spec.helper.bundle.mjs +67 -0
  35. package/moj/components/alert/alert.spec.helper.bundle.mjs.map +1 -0
  36. package/moj/components/alert/alert.spec.helper.mjs.map +1 -1
  37. package/moj/components/badge/_badge.scss +2 -0
  38. package/moj/components/badge/_badge.scss.map +1 -0
  39. package/moj/components/banner/_banner.scss +2 -0
  40. package/moj/components/banner/_banner.scss.map +1 -0
  41. package/moj/components/button-menu/README.md +10 -6
  42. package/moj/components/button-menu/_button-menu.scss +10 -3
  43. package/moj/components/button-menu/_button-menu.scss.map +1 -0
  44. package/moj/components/button-menu/button-menu.bundle.js +299 -0
  45. package/moj/components/button-menu/button-menu.bundle.js.map +1 -0
  46. package/moj/components/button-menu/{button-menu.js → button-menu.bundle.mjs} +74 -121
  47. package/moj/components/button-menu/button-menu.bundle.mjs.map +1 -0
  48. package/moj/components/button-menu/button-menu.mjs +246 -285
  49. package/moj/components/button-menu/button-menu.mjs.map +1 -1
  50. package/moj/components/cookie-banner/_cookie-banner.scss +2 -0
  51. package/moj/components/cookie-banner/_cookie-banner.scss.map +1 -0
  52. package/moj/components/currency-input/_currency-input.scss +2 -0
  53. package/moj/components/currency-input/_currency-input.scss.map +1 -0
  54. package/moj/components/date-picker/_date-picker.scss +2 -0
  55. package/moj/components/date-picker/_date-picker.scss.map +1 -0
  56. package/moj/components/date-picker/date-picker.bundle.js +784 -0
  57. package/moj/components/date-picker/date-picker.bundle.js.map +1 -0
  58. package/moj/components/date-picker/{date-picker.js → date-picker.bundle.mjs} +245 -439
  59. package/moj/components/date-picker/date-picker.bundle.mjs.map +1 -0
  60. package/moj/components/date-picker/date-picker.mjs +654 -840
  61. package/moj/components/date-picker/date-picker.mjs.map +1 -1
  62. package/moj/components/filter/_filter.scss +2 -0
  63. package/moj/components/filter/_filter.scss.map +1 -0
  64. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.js +96 -0
  65. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.js.map +1 -0
  66. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.mjs +88 -0
  67. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.mjs.map +1 -0
  68. package/moj/components/filter-toggle-button/filter-toggle-button.mjs +78 -84
  69. package/moj/components/filter-toggle-button/filter-toggle-button.mjs.map +1 -1
  70. package/moj/components/form-validator/form-validator.bundle.js +198 -0
  71. package/moj/components/form-validator/form-validator.bundle.js.map +1 -0
  72. package/moj/components/form-validator/form-validator.bundle.mjs +190 -0
  73. package/moj/components/form-validator/form-validator.bundle.mjs.map +1 -0
  74. package/moj/components/form-validator/form-validator.mjs +149 -152
  75. package/moj/components/form-validator/form-validator.mjs.map +1 -1
  76. package/moj/components/header/_header.scss +2 -0
  77. package/moj/components/header/_header.scss.map +1 -0
  78. package/moj/components/identity-bar/_identity-bar.scss +2 -0
  79. package/moj/components/identity-bar/_identity-bar.scss.map +1 -0
  80. package/moj/components/interruption-card/_interruption-card.scss +2 -0
  81. package/moj/components/interruption-card/_interruption-card.scss.map +1 -0
  82. package/moj/components/messages/_messages.scss +2 -0
  83. package/moj/components/messages/_messages.scss.map +1 -0
  84. package/moj/components/multi-file-upload/_multi-file-upload.scss +2 -0
  85. package/moj/components/multi-file-upload/_multi-file-upload.scss.map +1 -0
  86. package/moj/components/multi-file-upload/multi-file-upload.bundle.js +223 -0
  87. package/moj/components/multi-file-upload/multi-file-upload.bundle.js.map +1 -0
  88. package/moj/components/multi-file-upload/multi-file-upload.bundle.mjs +215 -0
  89. package/moj/components/multi-file-upload/multi-file-upload.bundle.mjs.map +1 -0
  90. package/moj/components/multi-file-upload/multi-file-upload.mjs +193 -209
  91. package/moj/components/multi-file-upload/multi-file-upload.mjs.map +1 -1
  92. package/moj/components/multi-select/_multi-select.scss +2 -0
  93. package/moj/components/multi-select/_multi-select.scss.map +1 -0
  94. package/moj/components/multi-select/multi-select.bundle.js +78 -0
  95. package/moj/components/multi-select/multi-select.bundle.js.map +1 -0
  96. package/moj/components/multi-select/multi-select.bundle.mjs +70 -0
  97. package/moj/components/multi-select/multi-select.bundle.mjs.map +1 -0
  98. package/moj/components/multi-select/multi-select.mjs +59 -67
  99. package/moj/components/multi-select/multi-select.mjs.map +1 -1
  100. package/moj/components/notification-badge/_notification-badge.scss +2 -0
  101. package/moj/components/notification-badge/_notification-badge.scss.map +1 -0
  102. package/moj/components/organisation-switcher/_organisation-switcher.scss +2 -0
  103. package/moj/components/organisation-switcher/_organisation-switcher.scss.map +1 -0
  104. package/moj/components/page-header-actions/_page-header-actions.scss +2 -0
  105. package/moj/components/page-header-actions/_page-header-actions.scss.map +1 -0
  106. package/moj/components/page-header-actions/template.njk +1 -1
  107. package/moj/components/pagination/_pagination.scss +2 -2
  108. package/moj/components/pagination/_pagination.scss.map +1 -0
  109. package/moj/components/password-reveal/_password-reveal.scss +2 -0
  110. package/moj/components/password-reveal/_password-reveal.scss.map +1 -0
  111. package/moj/components/password-reveal/password-reveal.bundle.js +49 -0
  112. package/moj/components/password-reveal/password-reveal.bundle.js.map +1 -0
  113. package/moj/components/password-reveal/password-reveal.bundle.mjs +41 -0
  114. package/moj/components/password-reveal/password-reveal.bundle.mjs.map +1 -0
  115. package/moj/components/password-reveal/password-reveal.mjs +36 -31
  116. package/moj/components/password-reveal/password-reveal.mjs.map +1 -1
  117. package/moj/components/primary-navigation/_primary-navigation.scss +2 -0
  118. package/moj/components/primary-navigation/_primary-navigation.scss.map +1 -0
  119. package/moj/components/progress-bar/_progress-bar.scss +2 -0
  120. package/moj/components/progress-bar/_progress-bar.scss.map +1 -0
  121. package/moj/components/rich-text-editor/README.md +15 -9
  122. package/moj/components/rich-text-editor/_rich-text-editor.scss +2 -0
  123. package/moj/components/rich-text-editor/_rich-text-editor.scss.map +1 -0
  124. package/moj/components/rich-text-editor/rich-text-editor.bundle.js +145 -0
  125. package/moj/components/rich-text-editor/rich-text-editor.bundle.js.map +1 -0
  126. package/moj/components/rich-text-editor/rich-text-editor.bundle.mjs +137 -0
  127. package/moj/components/rich-text-editor/rich-text-editor.bundle.mjs.map +1 -0
  128. package/moj/components/rich-text-editor/rich-text-editor.mjs +124 -145
  129. package/moj/components/rich-text-editor/rich-text-editor.mjs.map +1 -1
  130. package/moj/components/search/_search.scss +2 -0
  131. package/moj/components/search/_search.scss.map +1 -0
  132. package/moj/components/search-toggle/{search-toggle.scss → _search-toggle.scss} +2 -0
  133. package/moj/components/search-toggle/_search-toggle.scss.map +1 -0
  134. package/moj/components/search-toggle/search-toggle.bundle.js +54 -0
  135. package/moj/components/search-toggle/search-toggle.bundle.js.map +1 -0
  136. package/moj/components/search-toggle/search-toggle.bundle.mjs +46 -0
  137. package/moj/components/search-toggle/search-toggle.bundle.mjs.map +1 -0
  138. package/moj/components/search-toggle/search-toggle.mjs +40 -49
  139. package/moj/components/search-toggle/search-toggle.mjs.map +1 -1
  140. package/moj/components/side-navigation/_side-navigation.scss +2 -0
  141. package/moj/components/side-navigation/_side-navigation.scss.map +1 -0
  142. package/moj/components/sortable-table/_sortable-table.scss +2 -2
  143. package/moj/components/sortable-table/_sortable-table.scss.map +1 -0
  144. package/moj/components/sortable-table/sortable-table.bundle.js +134 -0
  145. package/moj/components/sortable-table/sortable-table.bundle.js.map +1 -0
  146. package/moj/components/sortable-table/sortable-table.bundle.mjs +126 -0
  147. package/moj/components/sortable-table/sortable-table.bundle.mjs.map +1 -0
  148. package/moj/components/sortable-table/sortable-table.mjs +117 -130
  149. package/moj/components/sortable-table/sortable-table.mjs.map +1 -1
  150. package/moj/components/sub-navigation/_sub-navigation.scss +2 -0
  151. package/moj/components/sub-navigation/_sub-navigation.scss.map +1 -0
  152. package/moj/components/tag/_tag.scss +2 -0
  153. package/moj/components/tag/_tag.scss.map +1 -0
  154. package/moj/components/task-list/_task-list.scss +2 -0
  155. package/moj/components/task-list/_task-list.scss.map +1 -0
  156. package/moj/components/ticket-panel/_ticket-panel.scss +2 -0
  157. package/moj/components/ticket-panel/_ticket-panel.scss.map +1 -0
  158. package/moj/components/timeline/_timeline.scss +2 -0
  159. package/moj/components/timeline/_timeline.scss.map +1 -0
  160. package/moj/filters/all.js +44 -22
  161. package/moj/helpers/_all.scss +2 -0
  162. package/moj/helpers/_all.scss.map +1 -0
  163. package/moj/helpers/_hidden.scss +2 -0
  164. package/moj/helpers/_hidden.scss.map +1 -0
  165. package/moj/helpers/_links.scss +2 -0
  166. package/moj/helpers/_links.scss.map +1 -0
  167. package/moj/{helpers.js → helpers.bundle.js} +37 -42
  168. package/moj/helpers.bundle.js.map +1 -0
  169. package/moj/helpers.bundle.mjs +179 -0
  170. package/moj/helpers.bundle.mjs.map +1 -0
  171. package/moj/helpers.mjs +52 -28
  172. package/moj/helpers.mjs.map +1 -1
  173. package/moj/init.js +11 -2
  174. package/moj/moj-frontend.min.css +1 -1
  175. package/moj/moj-frontend.min.css.map +1 -1
  176. package/moj/moj-frontend.min.js +1 -1
  177. package/moj/moj-frontend.min.js.map +1 -1
  178. package/moj/objects/_all.scss +2 -0
  179. package/moj/objects/_all.scss.map +1 -0
  180. package/moj/objects/_button-group.scss +17 -1
  181. package/moj/objects/_button-group.scss.map +1 -0
  182. package/moj/objects/_filter-layout.scss +2 -0
  183. package/moj/objects/_filter-layout.scss.map +1 -0
  184. package/moj/objects/_scrollable-pane.scss +2 -0
  185. package/moj/objects/_scrollable-pane.scss.map +1 -0
  186. package/moj/objects/_width-container.scss +2 -0
  187. package/moj/objects/_width-container.scss.map +1 -0
  188. package/moj/settings/_all.scss +2 -0
  189. package/moj/settings/_all.scss.map +1 -0
  190. package/moj/settings/_assets.scss +2 -0
  191. package/moj/settings/_assets.scss.map +1 -0
  192. package/moj/settings/_colours.scss +2 -0
  193. package/moj/settings/_colours.scss.map +1 -0
  194. package/moj/settings/_measurements.scss +2 -0
  195. package/moj/settings/_measurements.scss.map +1 -0
  196. package/moj/settings/_typography.scss +2 -0
  197. package/moj/settings/_typography.scss.map +1 -0
  198. package/moj/template.njk +13 -0
  199. package/moj/utilities/_all.scss +2 -0
  200. package/moj/utilities/_all.scss.map +1 -0
  201. package/moj/utilities/_hidden.scss +2 -0
  202. package/moj/utilities/_hidden.scss.map +1 -0
  203. package/moj/utilities/_width-container.scss +2 -0
  204. package/moj/utilities/_width-container.scss.map +1 -0
  205. package/moj/vendor/govuk-frontend/_base.scss +2 -0
  206. package/moj/vendor/govuk-frontend/_base.scss.map +1 -0
  207. package/moj/vendor/govuk-frontend/_index.scss +2 -0
  208. package/moj/vendor/govuk-frontend/_index.scss.map +1 -0
  209. package/moj/{version.js → version.bundle.js} +1 -1
  210. package/moj/version.bundle.js.map +1 -0
  211. package/moj/version.bundle.mjs +4 -0
  212. package/moj/version.bundle.mjs.map +1 -0
  213. package/moj/version.mjs.map +1 -1
  214. package/package.json +5 -6
  215. package/moj/all.jquery.min.js +0 -1
  216. package/moj/all.jquery.min.js.map +0 -1
  217. package/moj/all.js +0 -2662
  218. package/moj/all.js.map +0 -1
  219. package/moj/components/add-another/add-another.js +0 -115
  220. package/moj/components/add-another/add-another.js.map +0 -1
  221. package/moj/components/alert/alert.js +0 -356
  222. package/moj/components/alert/alert.js.map +0 -1
  223. package/moj/components/alert/alert.spec.helper.js.map +0 -1
  224. package/moj/components/button-menu/button-menu.js.map +0 -1
  225. package/moj/components/date-picker/date-picker.js.map +0 -1
  226. package/moj/components/filter-toggle-button/filter-toggle-button.js +0 -102
  227. package/moj/components/filter-toggle-button/filter-toggle-button.js.map +0 -1
  228. package/moj/components/form-validator/form-validator.js +0 -205
  229. package/moj/components/form-validator/form-validator.js.map +0 -1
  230. package/moj/components/multi-file-upload/multi-file-upload.js +0 -241
  231. package/moj/components/multi-file-upload/multi-file-upload.js.map +0 -1
  232. package/moj/components/multi-select/multi-select.js +0 -86
  233. package/moj/components/multi-select/multi-select.js.map +0 -1
  234. package/moj/components/password-reveal/password-reveal.js +0 -44
  235. package/moj/components/password-reveal/password-reveal.js.map +0 -1
  236. package/moj/components/rich-text-editor/rich-text-editor.js +0 -166
  237. package/moj/components/rich-text-editor/rich-text-editor.js.map +0 -1
  238. package/moj/components/search-toggle/search-toggle.js +0 -63
  239. package/moj/components/search-toggle/search-toggle.js.map +0 -1
  240. package/moj/components/sortable-table/sortable-table.js +0 -147
  241. package/moj/components/sortable-table/sortable-table.js.map +0 -1
  242. package/moj/helpers.js.map +0 -1
  243. package/moj/vendor/html5shiv.js +0 -326
  244. package/moj/vendor/jquery.js +0 -9300
  245. package/moj/version.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alert.bundle.js","sources":["../../../../src/moj/helpers.mjs","../../../../src/moj/components/alert/alert.mjs"],"sourcesContent":["export function removeAttributeValue(el, attr, value) {\n let re, m\n if (el.getAttribute(attr)) {\n if (el.getAttribute(attr) === value) {\n el.removeAttribute(attr)\n } else {\n re = new RegExp(`(^|\\\\s)${value}(\\\\s|$)`)\n m = el.getAttribute(attr).match(re)\n if (m && m.length === 3) {\n el.setAttribute(\n attr,\n el.getAttribute(attr).replace(re, m[1] && m[2] ? ' ' : '')\n )\n }\n }\n }\n}\n\nexport function addAttributeValue(el, attr, value) {\n let re\n if (!el.getAttribute(attr)) {\n el.setAttribute(attr, value)\n } else {\n re = new RegExp(`(^|\\\\s)${value}(\\\\s|$)`)\n if (!re.test(el.getAttribute(attr))) {\n el.setAttribute(attr, `${el.getAttribute(attr)} ${value}`)\n }\n }\n}\n\nexport function dragAndDropSupported() {\n const div = document.createElement('div')\n return typeof div.ondrop !== 'undefined'\n}\n\nexport function formDataSupported() {\n return typeof FormData === 'function'\n}\n\nexport function fileApiSupported() {\n const input = document.createElement('input')\n input.type = 'file'\n return typeof input.files !== 'undefined'\n}\n\n/**\n * Find an elements next sibling\n *\n * Utility function to find an elements next sibling matching the provided\n * selector.\n *\n * @param {Element | null} $element - Element to find siblings for\n * @param {string} [selector] - selector for required sibling\n */\nexport function getNextSibling($element, selector) {\n if (!$element || !($element instanceof HTMLElement)) {\n return\n }\n\n // Get the next sibling element\n let $sibling = $element.nextElementSibling\n\n // If there's no selector, return the first sibling\n if (!selector) return $sibling\n\n // If the sibling matches our selector, use it\n // If not, jump to the next sibling and continue the loop\n while ($sibling) {\n if ($sibling.matches(selector)) return $sibling\n $sibling = $sibling.nextElementSibling\n }\n}\n\n/**\n * Find an elements preceding sibling\n *\n * Utility function to find an elements previous sibling matching the provided\n * selector.\n *\n * @param {Element | null} $element - Element to find siblings for\n * @param {string} [selector] - selector for required sibling\n */\nexport function getPreviousSibling($element, selector) {\n if (!$element || !($element instanceof HTMLElement)) {\n return\n }\n\n // Get the previous sibling element\n let $sibling = $element.previousElementSibling\n\n // If there's no selector, return the first sibling\n if (!selector) return $sibling\n\n // If the sibling matches our selector, use it\n // If not, jump to the next sibling and continue the loop\n while ($sibling) {\n if ($sibling.matches(selector)) return $sibling\n $sibling = $sibling.previousElementSibling\n }\n}\n\n/**\n * @param {Element | null} $element\n * @param {string} [selector]\n */\nexport function findNearestMatchingElement($element, selector) {\n // If no element or selector is provided, return\n if (!$element || !($element instanceof HTMLElement) || !selector) {\n return\n }\n\n // Start with the current element\n let $currentElement = $element\n\n while ($currentElement) {\n // First check the current element\n if ($currentElement.matches(selector)) {\n return $currentElement\n }\n\n // Check all previous siblings\n let $sibling = $currentElement.previousElementSibling\n while ($sibling) {\n // Check if the sibling itself is a heading\n if ($sibling.matches(selector)) {\n return $sibling\n }\n $sibling = $sibling.previousElementSibling\n }\n\n // If no match found in siblings, move up to parent\n $currentElement = $currentElement.parentElement\n }\n}\n\n/**\n * Move focus to element\n *\n * Sets tabindex to -1 to make the element programmatically focusable,\n * but removes it on blur as the element doesn't need to be focused again.\n *\n * @param {HTMLElement} $element - HTML element\n * @param {object} [options] - Handler options\n * @param {function(this: HTMLElement): void} [options.onBeforeFocus] - Callback before focus\n * @param {function(this: HTMLElement): void} [options.onBlur] - Callback on blur\n */\nexport function setFocus($element, options = {}) {\n const isFocusable = $element.getAttribute('tabindex')\n\n if (!isFocusable) {\n $element.setAttribute('tabindex', '-1')\n }\n\n /**\n * Handle element focus\n */\n function onFocus() {\n $element.addEventListener('blur', onBlur, { once: true })\n }\n\n /**\n * Handle element blur\n */\n function onBlur() {\n if (options.onBlur) {\n options.onBlur.call($element)\n }\n\n if (!isFocusable) {\n $element.removeAttribute('tabindex')\n }\n }\n\n // Add listener to reset element on blur, after focus\n $element.addEventListener('focus', onFocus, { once: true })\n\n // Focus element\n if (options.onBeforeFocus) {\n options.onBeforeFocus.call($element)\n }\n $element.focus()\n}\n","import {\n findNearestMatchingElement,\n getPreviousSibling,\n setFocus\n} from '../../helpers.mjs'\n\nexport class Alert {\n /**\n * @param {Element | null} $module - HTML element to use for alert\n * @param {AlertConfig} [config] - Alert config\n */\n constructor($module, config = {}) {\n if (!$module || !($module instanceof HTMLElement)) {\n return this\n }\n\n const schema = Object.freeze({\n properties: {\n dismissible: { type: 'boolean' },\n dismissText: { type: 'string' },\n disableAutoFocus: { type: 'boolean' },\n focusOnDismissSelector: { type: 'string' }\n }\n })\n\n const defaults = {\n dismissible: false,\n dismissText: 'Dismiss',\n disableAutoFocus: false\n }\n\n // data attributes override JS config, which overrides defaults\n this.config = this.mergeConfigs(\n defaults,\n config,\n this.parseDataset(schema, $module.dataset)\n )\n\n this.$module = $module\n\n /**\n * Focus the alert\n *\n * If `role=\"alert\"` is set, focus the element to help some assistive\n * technologies prioritise announcing it.\n *\n * You can turn off the auto-focus functionality by setting\n * `data-disable-auto-focus=\"true\"` in the component HTML. You might wish to\n * do this based on user research findings, or to avoid a clash with another\n * element which should be focused when the page loads.\n */\n if (\n this.$module.getAttribute('role') === 'alert' &&\n !this.config.disableAutoFocus\n ) {\n setFocus(this.$module)\n }\n\n this.$dismissButton = this.$module.querySelector('.moj-alert__dismiss')\n\n if (this.config.dismissible && this.$dismissButton) {\n this.$dismissButton.innerHTML = this.config.dismissText\n this.$dismissButton.removeAttribute('hidden')\n\n this.$module.addEventListener('click', (event) => {\n if (\n event.target instanceof Node &&\n this.$dismissButton.contains(event.target)\n ) {\n this.dimiss()\n }\n })\n }\n }\n\n /**\n * Handle dismissing the alert\n */\n dimiss() {\n let $elementToRecieveFocus\n\n // If a selector has been provided, attempt to find that element\n if (this.config.focusOnDismissSelector) {\n $elementToRecieveFocus = document.querySelector(\n this.config.focusOnDismissSelector\n )\n }\n\n // Is the next sibling another alert\n if (!$elementToRecieveFocus) {\n const $nextSibling = this.$module.nextElementSibling\n if ($nextSibling && $nextSibling.matches('.moj-alert')) {\n $elementToRecieveFocus = $nextSibling\n }\n }\n\n // Else try to find any preceding sibling alert or heading\n if (!$elementToRecieveFocus) {\n $elementToRecieveFocus = getPreviousSibling(\n this.$module,\n '.moj-alert, h1, h2, h3, h4, h5, h6'\n )\n }\n\n // Else find the closest ancestor heading, or fallback to main, or last resort\n // use the body element\n if (!$elementToRecieveFocus) {\n $elementToRecieveFocus = findNearestMatchingElement(\n this.$module,\n 'h1, h2, h3, h4, h5, h6, main, body'\n )\n }\n\n // If we have an element, place focus on it\n if ($elementToRecieveFocus instanceof HTMLElement) {\n setFocus($elementToRecieveFocus)\n }\n\n // Remove the alert\n this.$module.remove()\n }\n\n /**\n * Normalise string\n *\n * 'If it looks like a duck, and it quacks like a duck…' 🦆\n *\n * If the passed value looks like a boolean or a number, convert it to a boolean\n * or number.\n *\n * Designed to be used to convert config passed via data attributes (which are\n * always strings) into something sensible.\n *\n * @internal\n * @param {DOMStringMap[string]} value - The value to normalise\n * @param {SchemaProperty} [property] - Component schema property\n * @returns {string | boolean | number | undefined} Normalised data\n */\n normaliseString(value, property) {\n const trimmedValue = value ? value.trim() : ''\n\n let output\n let outputType\n if (property && property.type) {\n outputType = property.type\n }\n\n // No schema type set? Determine automatically\n if (!outputType) {\n if (['true', 'false'].includes(trimmedValue)) {\n outputType = 'boolean'\n }\n\n // Empty / whitespace-only strings are considered finite so we need to check\n // the length of the trimmed string as well\n if (trimmedValue.length > 0 && Number.isFinite(Number(trimmedValue))) {\n outputType = 'number'\n }\n }\n\n switch (outputType) {\n case 'boolean':\n output = trimmedValue === 'true'\n break\n\n case 'number':\n output = Number(trimmedValue)\n break\n\n default:\n output = value\n }\n\n return output\n }\n\n /**\n * Parse dataset\n *\n * Loop over an object and normalise each value using {@link normaliseString},\n * optionally expanding nested `i18n.field`\n *\n * @param {Schema} schema - component schema\n * @param {DOMStringMap} dataset - HTML element dataset\n * @returns {object} Normalised dataset\n */\n parseDataset(schema, dataset) {\n const parsed = {}\n\n for (const [field, property] of Object.entries(schema.properties)) {\n if (field in dataset) {\n if (dataset[field]) {\n parsed[field] = this.normaliseString(dataset[field], property)\n }\n }\n }\n\n return parsed\n }\n\n /**\n * Config merging function\n *\n * Takes any number of objects and combines them together, with\n * greatest priority on the LAST item passed in.\n *\n * @param {...{ [key: string]: unknown }} configObjects - Config objects to merge\n * @returns {{ [key: string]: unknown }} A merged config object\n */\n mergeConfigs(...configObjects) {\n const formattedConfigObject = {}\n\n // Loop through each of the passed objects\n for (const configObject of configObjects) {\n for (const key of Object.keys(configObject)) {\n const option = formattedConfigObject[key]\n const override = configObject[key]\n\n // Push their keys one-by-one into formattedConfigObject. Any duplicate\n // keys with object values will be merged, otherwise the new value will\n // override the existing value.\n if (typeof option === 'object' && typeof override === 'object') {\n // @ts-expect-error Index signature for type 'string' is missing\n formattedConfigObject[key] = this.mergeConfigs(option, override)\n } else {\n formattedConfigObject[key] = override\n }\n }\n }\n\n return formattedConfigObject\n }\n}\n\n/**\n * @typedef {object} AlertConfig\n * @property {boolean} [dismissible=false] - Can the alert be dismissed by the user\n * @property {string} [dismissText=Dismiss] - the label text for the dismiss button\n * @property {boolean} [disableAutoFocus=false] - whether the alert will be autofocused\n * @property {string} [focusOnDismissSelector] - CSS Selector for element to be focused on dismiss\n */\n"],"names":["getPreviousSibling","$element","selector","HTMLElement","$sibling","previousElementSibling","matches","findNearestMatchingElement","$currentElement","parentElement","setFocus","options","isFocusable","getAttribute","setAttribute","onFocus","addEventListener","onBlur","once","call","removeAttribute","onBeforeFocus","focus","Alert","constructor","$module","config","schema","Object","freeze","properties","dismissible","type","dismissText","disableAutoFocus","focusOnDismissSelector","defaults","mergeConfigs","parseDataset","dataset","$dismissButton","querySelector","innerHTML","event","target","Node","contains","dimiss","$elementToRecieveFocus","document","$nextSibling","nextElementSibling","remove","normaliseString","value","property","trimmedValue","trim","output","outputType","includes","length","Number","isFinite","parsed","field","entries","configObjects","formattedConfigObject","configObject","key","keys","option","override"],"mappings":"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"}
@@ -0,0 +1,322 @@
1
+ /**
2
+ * Find an elements preceding sibling
3
+ *
4
+ * Utility function to find an elements previous sibling matching the provided
5
+ * selector.
6
+ *
7
+ * @param {Element | null} $element - Element to find siblings for
8
+ * @param {string} [selector] - selector for required sibling
9
+ */
10
+ function getPreviousSibling($element, selector) {
11
+ if (!$element || !($element instanceof HTMLElement)) {
12
+ return;
13
+ }
14
+
15
+ // Get the previous sibling element
16
+ let $sibling = $element.previousElementSibling;
17
+
18
+ // If the sibling matches our selector, use it
19
+ // If not, jump to the next sibling and continue the loop
20
+ while ($sibling) {
21
+ if ($sibling.matches(selector)) return $sibling;
22
+ $sibling = $sibling.previousElementSibling;
23
+ }
24
+ }
25
+
26
+ /**
27
+ * @param {Element | null} $element
28
+ * @param {string} [selector]
29
+ */
30
+ function findNearestMatchingElement($element, selector) {
31
+ // If no element or selector is provided, return
32
+ if (!$element || !($element instanceof HTMLElement) || false) {
33
+ return;
34
+ }
35
+
36
+ // Start with the current element
37
+ let $currentElement = $element;
38
+ while ($currentElement) {
39
+ // First check the current element
40
+ if ($currentElement.matches(selector)) {
41
+ return $currentElement;
42
+ }
43
+
44
+ // Check all previous siblings
45
+ let $sibling = $currentElement.previousElementSibling;
46
+ while ($sibling) {
47
+ // Check if the sibling itself is a heading
48
+ if ($sibling.matches(selector)) {
49
+ return $sibling;
50
+ }
51
+ $sibling = $sibling.previousElementSibling;
52
+ }
53
+
54
+ // If no match found in siblings, move up to parent
55
+ $currentElement = $currentElement.parentElement;
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Move focus to element
61
+ *
62
+ * Sets tabindex to -1 to make the element programmatically focusable,
63
+ * but removes it on blur as the element doesn't need to be focused again.
64
+ *
65
+ * @param {HTMLElement} $element - HTML element
66
+ * @param {object} [options] - Handler options
67
+ * @param {function(this: HTMLElement): void} [options.onBeforeFocus] - Callback before focus
68
+ * @param {function(this: HTMLElement): void} [options.onBlur] - Callback on blur
69
+ */
70
+ function setFocus($element, options = {}) {
71
+ const isFocusable = $element.getAttribute('tabindex');
72
+ if (!isFocusable) {
73
+ $element.setAttribute('tabindex', '-1');
74
+ }
75
+
76
+ /**
77
+ * Handle element focus
78
+ */
79
+ function onFocus() {
80
+ $element.addEventListener('blur', onBlur, {
81
+ once: true
82
+ });
83
+ }
84
+
85
+ /**
86
+ * Handle element blur
87
+ */
88
+ function onBlur() {
89
+ if (options.onBlur) {
90
+ options.onBlur.call($element);
91
+ }
92
+ if (!isFocusable) {
93
+ $element.removeAttribute('tabindex');
94
+ }
95
+ }
96
+
97
+ // Add listener to reset element on blur, after focus
98
+ $element.addEventListener('focus', onFocus, {
99
+ once: true
100
+ });
101
+
102
+ // Focus element
103
+ if (options.onBeforeFocus) {
104
+ options.onBeforeFocus.call($element);
105
+ }
106
+ $element.focus();
107
+ }
108
+
109
+ class Alert {
110
+ /**
111
+ * @param {Element | null} $module - HTML element to use for alert
112
+ * @param {AlertConfig} [config] - Alert config
113
+ */
114
+ constructor($module, config = {}) {
115
+ if (!$module || !($module instanceof HTMLElement)) {
116
+ return this;
117
+ }
118
+ const schema = Object.freeze({
119
+ properties: {
120
+ dismissible: {
121
+ type: 'boolean'
122
+ },
123
+ dismissText: {
124
+ type: 'string'
125
+ },
126
+ disableAutoFocus: {
127
+ type: 'boolean'
128
+ },
129
+ focusOnDismissSelector: {
130
+ type: 'string'
131
+ }
132
+ }
133
+ });
134
+ const defaults = {
135
+ dismissible: false,
136
+ dismissText: 'Dismiss',
137
+ disableAutoFocus: false
138
+ };
139
+
140
+ // data attributes override JS config, which overrides defaults
141
+ this.config = this.mergeConfigs(defaults, config, this.parseDataset(schema, $module.dataset));
142
+ this.$module = $module;
143
+
144
+ /**
145
+ * Focus the alert
146
+ *
147
+ * If `role="alert"` is set, focus the element to help some assistive
148
+ * technologies prioritise announcing it.
149
+ *
150
+ * You can turn off the auto-focus functionality by setting
151
+ * `data-disable-auto-focus="true"` in the component HTML. You might wish to
152
+ * do this based on user research findings, or to avoid a clash with another
153
+ * element which should be focused when the page loads.
154
+ */
155
+ if (this.$module.getAttribute('role') === 'alert' && !this.config.disableAutoFocus) {
156
+ setFocus(this.$module);
157
+ }
158
+ this.$dismissButton = this.$module.querySelector('.moj-alert__dismiss');
159
+ if (this.config.dismissible && this.$dismissButton) {
160
+ this.$dismissButton.innerHTML = this.config.dismissText;
161
+ this.$dismissButton.removeAttribute('hidden');
162
+ this.$module.addEventListener('click', event => {
163
+ if (event.target instanceof Node && this.$dismissButton.contains(event.target)) {
164
+ this.dimiss();
165
+ }
166
+ });
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Handle dismissing the alert
172
+ */
173
+ dimiss() {
174
+ let $elementToRecieveFocus;
175
+
176
+ // If a selector has been provided, attempt to find that element
177
+ if (this.config.focusOnDismissSelector) {
178
+ $elementToRecieveFocus = document.querySelector(this.config.focusOnDismissSelector);
179
+ }
180
+
181
+ // Is the next sibling another alert
182
+ if (!$elementToRecieveFocus) {
183
+ const $nextSibling = this.$module.nextElementSibling;
184
+ if ($nextSibling && $nextSibling.matches('.moj-alert')) {
185
+ $elementToRecieveFocus = $nextSibling;
186
+ }
187
+ }
188
+
189
+ // Else try to find any preceding sibling alert or heading
190
+ if (!$elementToRecieveFocus) {
191
+ $elementToRecieveFocus = getPreviousSibling(this.$module, '.moj-alert, h1, h2, h3, h4, h5, h6');
192
+ }
193
+
194
+ // Else find the closest ancestor heading, or fallback to main, or last resort
195
+ // use the body element
196
+ if (!$elementToRecieveFocus) {
197
+ $elementToRecieveFocus = findNearestMatchingElement(this.$module, 'h1, h2, h3, h4, h5, h6, main, body');
198
+ }
199
+
200
+ // If we have an element, place focus on it
201
+ if ($elementToRecieveFocus instanceof HTMLElement) {
202
+ setFocus($elementToRecieveFocus);
203
+ }
204
+
205
+ // Remove the alert
206
+ this.$module.remove();
207
+ }
208
+
209
+ /**
210
+ * Normalise string
211
+ *
212
+ * 'If it looks like a duck, and it quacks like a duck…' 🦆
213
+ *
214
+ * If the passed value looks like a boolean or a number, convert it to a boolean
215
+ * or number.
216
+ *
217
+ * Designed to be used to convert config passed via data attributes (which are
218
+ * always strings) into something sensible.
219
+ *
220
+ * @internal
221
+ * @param {DOMStringMap[string]} value - The value to normalise
222
+ * @param {SchemaProperty} [property] - Component schema property
223
+ * @returns {string | boolean | number | undefined} Normalised data
224
+ */
225
+ normaliseString(value, property) {
226
+ const trimmedValue = value ? value.trim() : '';
227
+ let output;
228
+ let outputType;
229
+ if (property && property.type) {
230
+ outputType = property.type;
231
+ }
232
+
233
+ // No schema type set? Determine automatically
234
+ if (!outputType) {
235
+ if (['true', 'false'].includes(trimmedValue)) {
236
+ outputType = 'boolean';
237
+ }
238
+
239
+ // Empty / whitespace-only strings are considered finite so we need to check
240
+ // the length of the trimmed string as well
241
+ if (trimmedValue.length > 0 && Number.isFinite(Number(trimmedValue))) {
242
+ outputType = 'number';
243
+ }
244
+ }
245
+ switch (outputType) {
246
+ case 'boolean':
247
+ output = trimmedValue === 'true';
248
+ break;
249
+ case 'number':
250
+ output = Number(trimmedValue);
251
+ break;
252
+ default:
253
+ output = value;
254
+ }
255
+ return output;
256
+ }
257
+
258
+ /**
259
+ * Parse dataset
260
+ *
261
+ * Loop over an object and normalise each value using {@link normaliseString},
262
+ * optionally expanding nested `i18n.field`
263
+ *
264
+ * @param {Schema} schema - component schema
265
+ * @param {DOMStringMap} dataset - HTML element dataset
266
+ * @returns {object} Normalised dataset
267
+ */
268
+ parseDataset(schema, dataset) {
269
+ const parsed = {};
270
+ for (const [field, property] of Object.entries(schema.properties)) {
271
+ if (field in dataset) {
272
+ if (dataset[field]) {
273
+ parsed[field] = this.normaliseString(dataset[field], property);
274
+ }
275
+ }
276
+ }
277
+ return parsed;
278
+ }
279
+
280
+ /**
281
+ * Config merging function
282
+ *
283
+ * Takes any number of objects and combines them together, with
284
+ * greatest priority on the LAST item passed in.
285
+ *
286
+ * @param {...{ [key: string]: unknown }} configObjects - Config objects to merge
287
+ * @returns {{ [key: string]: unknown }} A merged config object
288
+ */
289
+ mergeConfigs(...configObjects) {
290
+ const formattedConfigObject = {};
291
+
292
+ // Loop through each of the passed objects
293
+ for (const configObject of configObjects) {
294
+ for (const key of Object.keys(configObject)) {
295
+ const option = formattedConfigObject[key];
296
+ const override = configObject[key];
297
+
298
+ // Push their keys one-by-one into formattedConfigObject. Any duplicate
299
+ // keys with object values will be merged, otherwise the new value will
300
+ // override the existing value.
301
+ if (typeof option === 'object' && typeof override === 'object') {
302
+ // @ts-expect-error Index signature for type 'string' is missing
303
+ formattedConfigObject[key] = this.mergeConfigs(option, override);
304
+ } else {
305
+ formattedConfigObject[key] = override;
306
+ }
307
+ }
308
+ }
309
+ return formattedConfigObject;
310
+ }
311
+ }
312
+
313
+ /**
314
+ * @typedef {object} AlertConfig
315
+ * @property {boolean} [dismissible=false] - Can the alert be dismissed by the user
316
+ * @property {string} [dismissText=Dismiss] - the label text for the dismiss button
317
+ * @property {boolean} [disableAutoFocus=false] - whether the alert will be autofocused
318
+ * @property {string} [focusOnDismissSelector] - CSS Selector for element to be focused on dismiss
319
+ */
320
+
321
+ export { Alert };
322
+ //# sourceMappingURL=alert.bundle.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alert.bundle.mjs","sources":["../../../../src/moj/helpers.mjs","../../../../src/moj/components/alert/alert.mjs"],"sourcesContent":["export function removeAttributeValue(el, attr, value) {\n let re, m\n if (el.getAttribute(attr)) {\n if (el.getAttribute(attr) === value) {\n el.removeAttribute(attr)\n } else {\n re = new RegExp(`(^|\\\\s)${value}(\\\\s|$)`)\n m = el.getAttribute(attr).match(re)\n if (m && m.length === 3) {\n el.setAttribute(\n attr,\n el.getAttribute(attr).replace(re, m[1] && m[2] ? ' ' : '')\n )\n }\n }\n }\n}\n\nexport function addAttributeValue(el, attr, value) {\n let re\n if (!el.getAttribute(attr)) {\n el.setAttribute(attr, value)\n } else {\n re = new RegExp(`(^|\\\\s)${value}(\\\\s|$)`)\n if (!re.test(el.getAttribute(attr))) {\n el.setAttribute(attr, `${el.getAttribute(attr)} ${value}`)\n }\n }\n}\n\nexport function dragAndDropSupported() {\n const div = document.createElement('div')\n return typeof div.ondrop !== 'undefined'\n}\n\nexport function formDataSupported() {\n return typeof FormData === 'function'\n}\n\nexport function fileApiSupported() {\n const input = document.createElement('input')\n input.type = 'file'\n return typeof input.files !== 'undefined'\n}\n\n/**\n * Find an elements next sibling\n *\n * Utility function to find an elements next sibling matching the provided\n * selector.\n *\n * @param {Element | null} $element - Element to find siblings for\n * @param {string} [selector] - selector for required sibling\n */\nexport function getNextSibling($element, selector) {\n if (!$element || !($element instanceof HTMLElement)) {\n return\n }\n\n // Get the next sibling element\n let $sibling = $element.nextElementSibling\n\n // If there's no selector, return the first sibling\n if (!selector) return $sibling\n\n // If the sibling matches our selector, use it\n // If not, jump to the next sibling and continue the loop\n while ($sibling) {\n if ($sibling.matches(selector)) return $sibling\n $sibling = $sibling.nextElementSibling\n }\n}\n\n/**\n * Find an elements preceding sibling\n *\n * Utility function to find an elements previous sibling matching the provided\n * selector.\n *\n * @param {Element | null} $element - Element to find siblings for\n * @param {string} [selector] - selector for required sibling\n */\nexport function getPreviousSibling($element, selector) {\n if (!$element || !($element instanceof HTMLElement)) {\n return\n }\n\n // Get the previous sibling element\n let $sibling = $element.previousElementSibling\n\n // If there's no selector, return the first sibling\n if (!selector) return $sibling\n\n // If the sibling matches our selector, use it\n // If not, jump to the next sibling and continue the loop\n while ($sibling) {\n if ($sibling.matches(selector)) return $sibling\n $sibling = $sibling.previousElementSibling\n }\n}\n\n/**\n * @param {Element | null} $element\n * @param {string} [selector]\n */\nexport function findNearestMatchingElement($element, selector) {\n // If no element or selector is provided, return\n if (!$element || !($element instanceof HTMLElement) || !selector) {\n return\n }\n\n // Start with the current element\n let $currentElement = $element\n\n while ($currentElement) {\n // First check the current element\n if ($currentElement.matches(selector)) {\n return $currentElement\n }\n\n // Check all previous siblings\n let $sibling = $currentElement.previousElementSibling\n while ($sibling) {\n // Check if the sibling itself is a heading\n if ($sibling.matches(selector)) {\n return $sibling\n }\n $sibling = $sibling.previousElementSibling\n }\n\n // If no match found in siblings, move up to parent\n $currentElement = $currentElement.parentElement\n }\n}\n\n/**\n * Move focus to element\n *\n * Sets tabindex to -1 to make the element programmatically focusable,\n * but removes it on blur as the element doesn't need to be focused again.\n *\n * @param {HTMLElement} $element - HTML element\n * @param {object} [options] - Handler options\n * @param {function(this: HTMLElement): void} [options.onBeforeFocus] - Callback before focus\n * @param {function(this: HTMLElement): void} [options.onBlur] - Callback on blur\n */\nexport function setFocus($element, options = {}) {\n const isFocusable = $element.getAttribute('tabindex')\n\n if (!isFocusable) {\n $element.setAttribute('tabindex', '-1')\n }\n\n /**\n * Handle element focus\n */\n function onFocus() {\n $element.addEventListener('blur', onBlur, { once: true })\n }\n\n /**\n * Handle element blur\n */\n function onBlur() {\n if (options.onBlur) {\n options.onBlur.call($element)\n }\n\n if (!isFocusable) {\n $element.removeAttribute('tabindex')\n }\n }\n\n // Add listener to reset element on blur, after focus\n $element.addEventListener('focus', onFocus, { once: true })\n\n // Focus element\n if (options.onBeforeFocus) {\n options.onBeforeFocus.call($element)\n }\n $element.focus()\n}\n","import {\n findNearestMatchingElement,\n getPreviousSibling,\n setFocus\n} from '../../helpers.mjs'\n\nexport class Alert {\n /**\n * @param {Element | null} $module - HTML element to use for alert\n * @param {AlertConfig} [config] - Alert config\n */\n constructor($module, config = {}) {\n if (!$module || !($module instanceof HTMLElement)) {\n return this\n }\n\n const schema = Object.freeze({\n properties: {\n dismissible: { type: 'boolean' },\n dismissText: { type: 'string' },\n disableAutoFocus: { type: 'boolean' },\n focusOnDismissSelector: { type: 'string' }\n }\n })\n\n const defaults = {\n dismissible: false,\n dismissText: 'Dismiss',\n disableAutoFocus: false\n }\n\n // data attributes override JS config, which overrides defaults\n this.config = this.mergeConfigs(\n defaults,\n config,\n this.parseDataset(schema, $module.dataset)\n )\n\n this.$module = $module\n\n /**\n * Focus the alert\n *\n * If `role=\"alert\"` is set, focus the element to help some assistive\n * technologies prioritise announcing it.\n *\n * You can turn off the auto-focus functionality by setting\n * `data-disable-auto-focus=\"true\"` in the component HTML. You might wish to\n * do this based on user research findings, or to avoid a clash with another\n * element which should be focused when the page loads.\n */\n if (\n this.$module.getAttribute('role') === 'alert' &&\n !this.config.disableAutoFocus\n ) {\n setFocus(this.$module)\n }\n\n this.$dismissButton = this.$module.querySelector('.moj-alert__dismiss')\n\n if (this.config.dismissible && this.$dismissButton) {\n this.$dismissButton.innerHTML = this.config.dismissText\n this.$dismissButton.removeAttribute('hidden')\n\n this.$module.addEventListener('click', (event) => {\n if (\n event.target instanceof Node &&\n this.$dismissButton.contains(event.target)\n ) {\n this.dimiss()\n }\n })\n }\n }\n\n /**\n * Handle dismissing the alert\n */\n dimiss() {\n let $elementToRecieveFocus\n\n // If a selector has been provided, attempt to find that element\n if (this.config.focusOnDismissSelector) {\n $elementToRecieveFocus = document.querySelector(\n this.config.focusOnDismissSelector\n )\n }\n\n // Is the next sibling another alert\n if (!$elementToRecieveFocus) {\n const $nextSibling = this.$module.nextElementSibling\n if ($nextSibling && $nextSibling.matches('.moj-alert')) {\n $elementToRecieveFocus = $nextSibling\n }\n }\n\n // Else try to find any preceding sibling alert or heading\n if (!$elementToRecieveFocus) {\n $elementToRecieveFocus = getPreviousSibling(\n this.$module,\n '.moj-alert, h1, h2, h3, h4, h5, h6'\n )\n }\n\n // Else find the closest ancestor heading, or fallback to main, or last resort\n // use the body element\n if (!$elementToRecieveFocus) {\n $elementToRecieveFocus = findNearestMatchingElement(\n this.$module,\n 'h1, h2, h3, h4, h5, h6, main, body'\n )\n }\n\n // If we have an element, place focus on it\n if ($elementToRecieveFocus instanceof HTMLElement) {\n setFocus($elementToRecieveFocus)\n }\n\n // Remove the alert\n this.$module.remove()\n }\n\n /**\n * Normalise string\n *\n * 'If it looks like a duck, and it quacks like a duck…' 🦆\n *\n * If the passed value looks like a boolean or a number, convert it to a boolean\n * or number.\n *\n * Designed to be used to convert config passed via data attributes (which are\n * always strings) into something sensible.\n *\n * @internal\n * @param {DOMStringMap[string]} value - The value to normalise\n * @param {SchemaProperty} [property] - Component schema property\n * @returns {string | boolean | number | undefined} Normalised data\n */\n normaliseString(value, property) {\n const trimmedValue = value ? value.trim() : ''\n\n let output\n let outputType\n if (property && property.type) {\n outputType = property.type\n }\n\n // No schema type set? Determine automatically\n if (!outputType) {\n if (['true', 'false'].includes(trimmedValue)) {\n outputType = 'boolean'\n }\n\n // Empty / whitespace-only strings are considered finite so we need to check\n // the length of the trimmed string as well\n if (trimmedValue.length > 0 && Number.isFinite(Number(trimmedValue))) {\n outputType = 'number'\n }\n }\n\n switch (outputType) {\n case 'boolean':\n output = trimmedValue === 'true'\n break\n\n case 'number':\n output = Number(trimmedValue)\n break\n\n default:\n output = value\n }\n\n return output\n }\n\n /**\n * Parse dataset\n *\n * Loop over an object and normalise each value using {@link normaliseString},\n * optionally expanding nested `i18n.field`\n *\n * @param {Schema} schema - component schema\n * @param {DOMStringMap} dataset - HTML element dataset\n * @returns {object} Normalised dataset\n */\n parseDataset(schema, dataset) {\n const parsed = {}\n\n for (const [field, property] of Object.entries(schema.properties)) {\n if (field in dataset) {\n if (dataset[field]) {\n parsed[field] = this.normaliseString(dataset[field], property)\n }\n }\n }\n\n return parsed\n }\n\n /**\n * Config merging function\n *\n * Takes any number of objects and combines them together, with\n * greatest priority on the LAST item passed in.\n *\n * @param {...{ [key: string]: unknown }} configObjects - Config objects to merge\n * @returns {{ [key: string]: unknown }} A merged config object\n */\n mergeConfigs(...configObjects) {\n const formattedConfigObject = {}\n\n // Loop through each of the passed objects\n for (const configObject of configObjects) {\n for (const key of Object.keys(configObject)) {\n const option = formattedConfigObject[key]\n const override = configObject[key]\n\n // Push their keys one-by-one into formattedConfigObject. Any duplicate\n // keys with object values will be merged, otherwise the new value will\n // override the existing value.\n if (typeof option === 'object' && typeof override === 'object') {\n // @ts-expect-error Index signature for type 'string' is missing\n formattedConfigObject[key] = this.mergeConfigs(option, override)\n } else {\n formattedConfigObject[key] = override\n }\n }\n }\n\n return formattedConfigObject\n }\n}\n\n/**\n * @typedef {object} AlertConfig\n * @property {boolean} [dismissible=false] - Can the alert be dismissed by the user\n * @property {string} [dismissText=Dismiss] - the label text for the dismiss button\n * @property {boolean} [disableAutoFocus=false] - whether the alert will be autofocused\n * @property {string} [focusOnDismissSelector] - CSS Selector for element to be focused on dismiss\n */\n"],"names":["getPreviousSibling","$element","selector","HTMLElement","$sibling","previousElementSibling","matches","findNearestMatchingElement","$currentElement","parentElement","setFocus","options","isFocusable","getAttribute","setAttribute","onFocus","addEventListener","onBlur","once","call","removeAttribute","onBeforeFocus","focus","Alert","constructor","$module","config","schema","Object","freeze","properties","dismissible","type","dismissText","disableAutoFocus","focusOnDismissSelector","defaults","mergeConfigs","parseDataset","dataset","$dismissButton","querySelector","innerHTML","event","target","Node","contains","dimiss","$elementToRecieveFocus","document","$nextSibling","nextElementSibling","remove","normaliseString","value","property","trimmedValue","trim","output","outputType","includes","length","Number","isFinite","parsed","field","entries","configObjects","formattedConfigObject","configObject","key","keys","option","override"],"mappings":"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"}