@ministryofjustice/frontend 4.0.1 → 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 (244) 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 +4 -1
  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/pagination/_pagination.scss +2 -2
  107. package/moj/components/pagination/_pagination.scss.map +1 -0
  108. package/moj/components/password-reveal/_password-reveal.scss +2 -0
  109. package/moj/components/password-reveal/_password-reveal.scss.map +1 -0
  110. package/moj/components/password-reveal/password-reveal.bundle.js +49 -0
  111. package/moj/components/password-reveal/password-reveal.bundle.js.map +1 -0
  112. package/moj/components/password-reveal/password-reveal.bundle.mjs +41 -0
  113. package/moj/components/password-reveal/password-reveal.bundle.mjs.map +1 -0
  114. package/moj/components/password-reveal/password-reveal.mjs +36 -31
  115. package/moj/components/password-reveal/password-reveal.mjs.map +1 -1
  116. package/moj/components/primary-navigation/_primary-navigation.scss +2 -0
  117. package/moj/components/primary-navigation/_primary-navigation.scss.map +1 -0
  118. package/moj/components/progress-bar/_progress-bar.scss +2 -0
  119. package/moj/components/progress-bar/_progress-bar.scss.map +1 -0
  120. package/moj/components/rich-text-editor/README.md +15 -9
  121. package/moj/components/rich-text-editor/_rich-text-editor.scss +2 -0
  122. package/moj/components/rich-text-editor/_rich-text-editor.scss.map +1 -0
  123. package/moj/components/rich-text-editor/rich-text-editor.bundle.js +145 -0
  124. package/moj/components/rich-text-editor/rich-text-editor.bundle.js.map +1 -0
  125. package/moj/components/rich-text-editor/rich-text-editor.bundle.mjs +137 -0
  126. package/moj/components/rich-text-editor/rich-text-editor.bundle.mjs.map +1 -0
  127. package/moj/components/rich-text-editor/rich-text-editor.mjs +124 -145
  128. package/moj/components/rich-text-editor/rich-text-editor.mjs.map +1 -1
  129. package/moj/components/search/_search.scss +2 -0
  130. package/moj/components/search/_search.scss.map +1 -0
  131. package/moj/components/search-toggle/{search-toggle.scss → _search-toggle.scss} +2 -0
  132. package/moj/components/search-toggle/_search-toggle.scss.map +1 -0
  133. package/moj/components/search-toggle/search-toggle.bundle.js +54 -0
  134. package/moj/components/search-toggle/search-toggle.bundle.js.map +1 -0
  135. package/moj/components/search-toggle/search-toggle.bundle.mjs +46 -0
  136. package/moj/components/search-toggle/search-toggle.bundle.mjs.map +1 -0
  137. package/moj/components/search-toggle/search-toggle.mjs +40 -49
  138. package/moj/components/search-toggle/search-toggle.mjs.map +1 -1
  139. package/moj/components/side-navigation/_side-navigation.scss +2 -0
  140. package/moj/components/side-navigation/_side-navigation.scss.map +1 -0
  141. package/moj/components/sortable-table/_sortable-table.scss +2 -2
  142. package/moj/components/sortable-table/_sortable-table.scss.map +1 -0
  143. package/moj/components/sortable-table/sortable-table.bundle.js +134 -0
  144. package/moj/components/sortable-table/sortable-table.bundle.js.map +1 -0
  145. package/moj/components/sortable-table/sortable-table.bundle.mjs +126 -0
  146. package/moj/components/sortable-table/sortable-table.bundle.mjs.map +1 -0
  147. package/moj/components/sortable-table/sortable-table.mjs +117 -130
  148. package/moj/components/sortable-table/sortable-table.mjs.map +1 -1
  149. package/moj/components/sub-navigation/_sub-navigation.scss +2 -0
  150. package/moj/components/sub-navigation/_sub-navigation.scss.map +1 -0
  151. package/moj/components/tag/_tag.scss +2 -0
  152. package/moj/components/tag/_tag.scss.map +1 -0
  153. package/moj/components/task-list/_task-list.scss +2 -0
  154. package/moj/components/task-list/_task-list.scss.map +1 -0
  155. package/moj/components/ticket-panel/_ticket-panel.scss +2 -0
  156. package/moj/components/ticket-panel/_ticket-panel.scss.map +1 -0
  157. package/moj/components/timeline/_timeline.scss +2 -0
  158. package/moj/components/timeline/_timeline.scss.map +1 -0
  159. package/moj/filters/all.js +44 -22
  160. package/moj/helpers/_all.scss +2 -0
  161. package/moj/helpers/_all.scss.map +1 -0
  162. package/moj/helpers/_hidden.scss +2 -0
  163. package/moj/helpers/_hidden.scss.map +1 -0
  164. package/moj/helpers/_links.scss +2 -0
  165. package/moj/helpers/_links.scss.map +1 -0
  166. package/moj/{helpers.js → helpers.bundle.js} +37 -42
  167. package/moj/helpers.bundle.js.map +1 -0
  168. package/moj/helpers.bundle.mjs +179 -0
  169. package/moj/helpers.bundle.mjs.map +1 -0
  170. package/moj/helpers.mjs +52 -28
  171. package/moj/helpers.mjs.map +1 -1
  172. package/moj/init.js +11 -2
  173. package/moj/moj-frontend.min.css +1 -1
  174. package/moj/moj-frontend.min.css.map +1 -1
  175. package/moj/moj-frontend.min.js +1 -1
  176. package/moj/moj-frontend.min.js.map +1 -1
  177. package/moj/objects/_all.scss +2 -0
  178. package/moj/objects/_all.scss.map +1 -0
  179. package/moj/objects/_button-group.scss +2 -0
  180. package/moj/objects/_button-group.scss.map +1 -0
  181. package/moj/objects/_filter-layout.scss +2 -0
  182. package/moj/objects/_filter-layout.scss.map +1 -0
  183. package/moj/objects/_scrollable-pane.scss +2 -0
  184. package/moj/objects/_scrollable-pane.scss.map +1 -0
  185. package/moj/objects/_width-container.scss +2 -0
  186. package/moj/objects/_width-container.scss.map +1 -0
  187. package/moj/settings/_all.scss +2 -0
  188. package/moj/settings/_all.scss.map +1 -0
  189. package/moj/settings/_assets.scss +2 -0
  190. package/moj/settings/_assets.scss.map +1 -0
  191. package/moj/settings/_colours.scss +2 -0
  192. package/moj/settings/_colours.scss.map +1 -0
  193. package/moj/settings/_measurements.scss +2 -0
  194. package/moj/settings/_measurements.scss.map +1 -0
  195. package/moj/settings/_typography.scss +2 -0
  196. package/moj/settings/_typography.scss.map +1 -0
  197. package/moj/template.njk +13 -0
  198. package/moj/utilities/_all.scss +2 -0
  199. package/moj/utilities/_all.scss.map +1 -0
  200. package/moj/utilities/_hidden.scss +2 -0
  201. package/moj/utilities/_hidden.scss.map +1 -0
  202. package/moj/utilities/_width-container.scss +2 -0
  203. package/moj/utilities/_width-container.scss.map +1 -0
  204. package/moj/vendor/govuk-frontend/_base.scss +2 -0
  205. package/moj/vendor/govuk-frontend/_base.scss.map +1 -0
  206. package/moj/vendor/govuk-frontend/_index.scss +2 -0
  207. package/moj/vendor/govuk-frontend/_index.scss.map +1 -0
  208. package/moj/{version.js → version.bundle.js} +1 -1
  209. package/moj/version.bundle.js.map +1 -0
  210. package/moj/version.bundle.mjs +4 -0
  211. package/moj/version.bundle.mjs.map +1 -0
  212. package/moj/version.mjs.map +1 -1
  213. package/package.json +5 -6
  214. package/moj/all.jquery.min.js +0 -1
  215. package/moj/all.jquery.min.js.map +0 -1
  216. package/moj/all.js +0 -2662
  217. package/moj/all.js.map +0 -1
  218. package/moj/components/add-another/add-another.js +0 -115
  219. package/moj/components/add-another/add-another.js.map +0 -1
  220. package/moj/components/alert/alert.js +0 -356
  221. package/moj/components/alert/alert.js.map +0 -1
  222. package/moj/components/alert/alert.spec.helper.js.map +0 -1
  223. package/moj/components/button-menu/button-menu.js.map +0 -1
  224. package/moj/components/date-picker/date-picker.js.map +0 -1
  225. package/moj/components/filter-toggle-button/filter-toggle-button.js +0 -102
  226. package/moj/components/filter-toggle-button/filter-toggle-button.js.map +0 -1
  227. package/moj/components/form-validator/form-validator.js +0 -205
  228. package/moj/components/form-validator/form-validator.js.map +0 -1
  229. package/moj/components/multi-file-upload/multi-file-upload.js +0 -241
  230. package/moj/components/multi-file-upload/multi-file-upload.js.map +0 -1
  231. package/moj/components/multi-select/multi-select.js +0 -86
  232. package/moj/components/multi-select/multi-select.js.map +0 -1
  233. package/moj/components/password-reveal/password-reveal.js +0 -44
  234. package/moj/components/password-reveal/password-reveal.js.map +0 -1
  235. package/moj/components/rich-text-editor/rich-text-editor.js +0 -166
  236. package/moj/components/rich-text-editor/rich-text-editor.js.map +0 -1
  237. package/moj/components/search-toggle/search-toggle.js +0 -63
  238. package/moj/components/search-toggle/search-toggle.js.map +0 -1
  239. package/moj/components/sortable-table/sortable-table.js +0 -147
  240. package/moj/components/sortable-table/sortable-table.js.map +0 -1
  241. package/moj/helpers.js.map +0 -1
  242. package/moj/vendor/html5shiv.js +0 -326
  243. package/moj/vendor/jquery.js +0 -9300
  244. package/moj/version.js.map +0 -1
@@ -4,13 +4,19 @@
4
4
 
5
5
  ## Example
6
6
 
7
- ```html
8
- <script>
9
- new MOJFrontend.RichTextEditor({
10
- textarea: $('.js-editor'),
11
- toolbar: { 'bold': true, 'italic': true, 'underline': true, 'bullets': true, 'numbers': true }
12
- })
13
- </script>
14
- ```
7
+ ```mjs
8
+ import { RichTextEditor } from '@ministryofjustice/frontend'
9
+
10
+ const $richTextEditor = document.querySelector('.app-rich-text-editor')
15
11
 
16
- ## Options
12
+ new RichTextEditor({
13
+ textarea: $richTextEditor,
14
+ toolbar: {
15
+ bold: true,
16
+ italic: true,
17
+ underline: true,
18
+ bullets: true,
19
+ numbers: true
20
+ }
21
+ })
22
+ ```
@@ -75,3 +75,5 @@
75
75
  resize: vertical;
76
76
  outline: none;
77
77
  }
78
+
79
+ /*# sourceMappingURL=_rich-text-editor.scss.map */
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/moj/components/rich-text-editor/_rich-text-editor.scss"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,uCAAuC;;AAEvC;;+EAE+E;;AAE/E;EACE,uBAAuB;EACvB,+BAA+B;AACjC;;AAEA;EACE,WAAW;EACX,YAAY;EACZ,iBAAiB;EACjB,WAAW;EACX,uCAAuC;EACvC,UAAU;EACV,4BAA4B;EAC5B,uCAAuC;EACvC,4BAA4B;EAC5B,4BAA4B;EAC5B,0BAA0B;EAC1B,qBAAqB;EACrB,mBAAmB;EACnB,eAAe;;EAEf;IACE,cAAc;EAChB;;EAEA,wCAAwC;EACxC;IACE,UAAU;IACV,SAAS;EACX;;EAEA;IACE,kBAAkB;IAClB,UAAU;IACV,aAAa;IACb,+BAA+B;IAC/B,qCAAqC;IACrC;;oCAEgC;EAClC;AACF;;AAEA;EACE,+DAA+D;AACjE;;AAEA;EACE,iEAAiE;AACnE;;AAEA;EACE,oEAAoE;AACtE;;AAEA;EACE,6BAA6B;EAC7B,yEAAyE;AAC3E;;AAEA;EACE,uEAAuE;AACzE;;AAEA;EACE,iBAAiB;EACjB,cAAc;EACd,gBAAgB;EAChB,aAAa;AACf","file":"_rich-text-editor.scss","sourcesContent":["@use \"../../settings/assets\" as *;\n@use \"../../vendor/govuk-frontend\" as *;\n\n/* ==========================================================================\n #RICH TEXT EDITOR\n ========================================================================== */\n\n.moj-rich-text-editor__toolbar {\n @include govuk-clearfix;\n margin-bottom: govuk-spacing(2);\n}\n\n.moj-rich-text-editor__toolbar-button {\n width: 40px;\n height: 40px;\n margin-left: -2px;\n float: left;\n border: 2px solid govuk-colour(\"black\");\n outline: 0;\n color: govuk-colour(\"black\");\n background-color: govuk-colour(\"white\");\n background-repeat: no-repeat;\n background-position: 50% 50%;\n background-size: 40px 40px;\n text-decoration: none;\n vertical-align: top;\n cursor: pointer;\n\n &:first-child {\n margin-left: 0;\n }\n\n // Fix unwanted button padding in Firefox\n &::-moz-focus-inner {\n padding: 0;\n border: 0;\n }\n\n &:focus {\n position: relative;\n z-index: 2;\n outline: none;\n color: $govuk-focus-text-colour;\n background-color: $govuk-focus-colour;\n box-shadow:\n 0 -2px $govuk-focus-colour,\n 0 4px $govuk-focus-text-colour;\n }\n}\n\n.moj-rich-text-editor__toolbar-button--bold {\n background-image: url(#{$moj-images-path}icon-wysiwyg-bold.svg);\n}\n\n.moj-rich-text-editor__toolbar-button--italic {\n background-image: url(#{$moj-images-path}icon-wysiwyg-italic.svg);\n}\n\n.moj-rich-text-editor__toolbar-button--underline {\n background-image: url(#{$moj-images-path}icon-wysiwyg-underline.svg);\n}\n\n.moj-rich-text-editor__toolbar-button--unordered-list {\n margin-left: govuk-spacing(2);\n background-image: url(#{$moj-images-path}icon-wysiwyg-unordered-list.svg);\n}\n\n.moj-rich-text-editor__toolbar-button--ordered-list {\n background-image: url(#{$moj-images-path}icon-wysiwyg-ordered-list.svg);\n}\n\n.moj-rich-text-editor__content {\n min-height: 130px;\n overflow: auto;\n resize: vertical;\n outline: none;\n}\n"]}
@@ -0,0 +1,145 @@
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.MOJFrontend = global.MOJFrontend || {}));
5
+ })(this, (function (exports) { 'use strict';
6
+
7
+ class RichTextEditor {
8
+ /**
9
+ * @param {RichTextEditorConfig} options
10
+ */
11
+ constructor(options = {}) {
12
+ const {
13
+ textarea
14
+ } = options;
15
+ if (!textarea || !textarea.parentElement || !(textarea instanceof HTMLTextAreaElement) || !('contentEditable' in document.documentElement)) {
16
+ return this;
17
+ }
18
+ options.toolbar = options.toolbar || {
19
+ bold: false,
20
+ italic: false,
21
+ underline: false,
22
+ bullets: true,
23
+ numbers: true
24
+ };
25
+ this.textarea = textarea;
26
+ this.container = this.textarea.parentElement;
27
+ this.options = options;
28
+ if (this.container.hasAttribute('data-rich-text-editor-init')) {
29
+ return this;
30
+ }
31
+ this.container.setAttribute('data-rich-text-editor-init', '');
32
+ this.createToolbar();
33
+ this.hideDefault();
34
+ this.configureToolbar();
35
+ this.keys = {
36
+ left: 37,
37
+ right: 39,
38
+ up: 38,
39
+ down: 40
40
+ };
41
+ this.content.addEventListener('input', this.onEditorInput.bind(this));
42
+ this.container.querySelector('label').addEventListener('click', this.onLabelClick.bind(this));
43
+ this.toolbar.addEventListener('keydown', this.onToolbarKeydown.bind(this));
44
+ }
45
+ onToolbarKeydown(event) {
46
+ let focusableButton;
47
+ switch (event.keyCode) {
48
+ case this.keys.right:
49
+ case this.keys.down:
50
+ {
51
+ focusableButton = this.buttons.find(button => button.getAttribute('tabindex') === '0');
52
+ const nextButton = focusableButton.nextElementSibling;
53
+ if (nextButton instanceof HTMLButtonElement) {
54
+ nextButton.focus();
55
+ focusableButton.setAttribute('tabindex', '-1');
56
+ nextButton.setAttribute('tabindex', '0');
57
+ }
58
+ break;
59
+ }
60
+ case this.keys.left:
61
+ case this.keys.up:
62
+ {
63
+ focusableButton = this.buttons.find(button => button.getAttribute('tabindex') === '0');
64
+ const previousButton = focusableButton.previousElementSibling;
65
+ if (previousButton instanceof HTMLButtonElement) {
66
+ previousButton.focus();
67
+ focusableButton.setAttribute('tabindex', '-1');
68
+ previousButton.setAttribute('tabindex', '0');
69
+ }
70
+ break;
71
+ }
72
+ }
73
+ }
74
+ getToolbarHtml() {
75
+ let html = '';
76
+ html += '<div class="moj-rich-text-editor__toolbar" role="toolbar">';
77
+ if (this.options.toolbar.bold) {
78
+ 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>';
79
+ }
80
+ if (this.options.toolbar.italic) {
81
+ 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>';
82
+ }
83
+ if (this.options.toolbar.underline) {
84
+ 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>';
85
+ }
86
+ if (this.options.toolbar.bullets) {
87
+ 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>';
88
+ }
89
+ if (this.options.toolbar.numbers) {
90
+ 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>';
91
+ }
92
+ html += '</div>';
93
+ return html;
94
+ }
95
+ getEnhancedHtml() {
96
+ return `${this.getToolbarHtml()}<div class="govuk-textarea moj-rich-text-editor__content" contenteditable="true" spellcheck="false"></div>`;
97
+ }
98
+ hideDefault() {
99
+ this.textarea.classList.add('govuk-visually-hidden');
100
+ this.textarea.setAttribute('aria-hidden', 'true');
101
+ this.textarea.setAttribute('tabindex', '-1');
102
+ }
103
+ createToolbar() {
104
+ this.toolbar = document.createElement('div');
105
+ this.toolbar.className = 'moj-rich-text-editor';
106
+ this.toolbar.innerHTML = this.getEnhancedHtml();
107
+ this.container.append(this.toolbar);
108
+ this.content = /** @type {HTMLDivElement} */
109
+ this.container.querySelector('.moj-rich-text-editor__content');
110
+ this.content.innerHTML = this.$textarea.value;
111
+ }
112
+ configureToolbar() {
113
+ this.buttons = Array.from(/** @type {NodeListOf<HTMLButtonElement>} */
114
+ this.container.querySelectorAll('.moj-rich-text-editor__toolbar-button'));
115
+ this.buttons.forEach((button, index) => {
116
+ button.setAttribute('tabindex', !index ? '0' : '-1');
117
+ button.addEventListener('click', this.onButtonClick.bind(this));
118
+ });
119
+ }
120
+ onButtonClick(event) {
121
+ if (!(event.currentTarget instanceof HTMLElement)) {
122
+ return;
123
+ }
124
+ document.execCommand(event.currentTarget.getAttribute('data-command'), false, undefined);
125
+ }
126
+ getContent() {
127
+ return this.content.innerHTML;
128
+ }
129
+ onEditorInput() {
130
+ this.updateTextarea();
131
+ }
132
+ updateTextarea() {
133
+ document.execCommand('defaultParagraphSeparator', false, 'p');
134
+ this.textarea.value = this.getContent();
135
+ }
136
+ onLabelClick(event) {
137
+ event.preventDefault();
138
+ this.content.focus();
139
+ }
140
+ }
141
+
142
+ exports.RichTextEditor = RichTextEditor;
143
+
144
+ }));
145
+ //# sourceMappingURL=rich-text-editor.bundle.js.map
@@ -0,0 +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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"}
@@ -0,0 +1,137 @@
1
+ class RichTextEditor {
2
+ /**
3
+ * @param {RichTextEditorConfig} options
4
+ */
5
+ constructor(options = {}) {
6
+ const {
7
+ textarea
8
+ } = options;
9
+ if (!textarea || !textarea.parentElement || !(textarea instanceof HTMLTextAreaElement) || !('contentEditable' in document.documentElement)) {
10
+ return this;
11
+ }
12
+ options.toolbar = options.toolbar || {
13
+ bold: false,
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')) {
23
+ return this;
24
+ }
25
+ this.container.setAttribute('data-rich-text-editor-init', '');
26
+ this.createToolbar();
27
+ this.hideDefault();
28
+ this.configureToolbar();
29
+ this.keys = {
30
+ left: 37,
31
+ right: 39,
32
+ up: 38,
33
+ down: 40
34
+ };
35
+ this.content.addEventListener('input', this.onEditorInput.bind(this));
36
+ this.container.querySelector('label').addEventListener('click', this.onLabelClick.bind(this));
37
+ this.toolbar.addEventListener('keydown', this.onToolbarKeydown.bind(this));
38
+ }
39
+ onToolbarKeydown(event) {
40
+ let focusableButton;
41
+ switch (event.keyCode) {
42
+ case this.keys.right:
43
+ case this.keys.down:
44
+ {
45
+ focusableButton = this.buttons.find(button => button.getAttribute('tabindex') === '0');
46
+ const nextButton = focusableButton.nextElementSibling;
47
+ if (nextButton instanceof HTMLButtonElement) {
48
+ nextButton.focus();
49
+ focusableButton.setAttribute('tabindex', '-1');
50
+ nextButton.setAttribute('tabindex', '0');
51
+ }
52
+ break;
53
+ }
54
+ case this.keys.left:
55
+ case this.keys.up:
56
+ {
57
+ focusableButton = this.buttons.find(button => button.getAttribute('tabindex') === '0');
58
+ const previousButton = focusableButton.previousElementSibling;
59
+ if (previousButton instanceof HTMLButtonElement) {
60
+ previousButton.focus();
61
+ focusableButton.setAttribute('tabindex', '-1');
62
+ previousButton.setAttribute('tabindex', '0');
63
+ }
64
+ break;
65
+ }
66
+ }
67
+ }
68
+ getToolbarHtml() {
69
+ let html = '';
70
+ html += '<div class="moj-rich-text-editor__toolbar" role="toolbar">';
71
+ if (this.options.toolbar.bold) {
72
+ 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
+ }
74
+ if (this.options.toolbar.italic) {
75
+ 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
+ }
77
+ if (this.options.toolbar.underline) {
78
+ 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
+ }
80
+ if (this.options.toolbar.bullets) {
81
+ 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
+ }
83
+ if (this.options.toolbar.numbers) {
84
+ 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
+ }
86
+ html += '</div>';
87
+ return html;
88
+ }
89
+ getEnhancedHtml() {
90
+ return `${this.getToolbarHtml()}<div class="govuk-textarea moj-rich-text-editor__content" contenteditable="true" spellcheck="false"></div>`;
91
+ }
92
+ hideDefault() {
93
+ this.textarea.classList.add('govuk-visually-hidden');
94
+ this.textarea.setAttribute('aria-hidden', 'true');
95
+ this.textarea.setAttribute('tabindex', '-1');
96
+ }
97
+ createToolbar() {
98
+ this.toolbar = document.createElement('div');
99
+ this.toolbar.className = 'moj-rich-text-editor';
100
+ this.toolbar.innerHTML = this.getEnhancedHtml();
101
+ this.container.append(this.toolbar);
102
+ this.content = /** @type {HTMLDivElement} */
103
+ this.container.querySelector('.moj-rich-text-editor__content');
104
+ this.content.innerHTML = this.$textarea.value;
105
+ }
106
+ configureToolbar() {
107
+ this.buttons = Array.from(/** @type {NodeListOf<HTMLButtonElement>} */
108
+ this.container.querySelectorAll('.moj-rich-text-editor__toolbar-button'));
109
+ this.buttons.forEach((button, index) => {
110
+ button.setAttribute('tabindex', !index ? '0' : '-1');
111
+ button.addEventListener('click', this.onButtonClick.bind(this));
112
+ });
113
+ }
114
+ onButtonClick(event) {
115
+ if (!(event.currentTarget instanceof HTMLElement)) {
116
+ return;
117
+ }
118
+ document.execCommand(event.currentTarget.getAttribute('data-command'), false, undefined);
119
+ }
120
+ getContent() {
121
+ return this.content.innerHTML;
122
+ }
123
+ onEditorInput() {
124
+ this.updateTextarea();
125
+ }
126
+ updateTextarea() {
127
+ document.execCommand('defaultParagraphSeparator', false, 'p');
128
+ this.textarea.value = this.getContent();
129
+ }
130
+ onLabelClick(event) {
131
+ event.preventDefault();
132
+ this.content.focus();
133
+ }
134
+ }
135
+
136
+ export { RichTextEditor };
137
+ //# sourceMappingURL=rich-text-editor.bundle.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rich-text-editor.bundle.mjs","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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"}