@ckeditor/ckeditor5-ui 41.3.0 → 41.4.0-alpha.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 (251) hide show
  1. package/dist/index-content.css +4 -0
  2. package/dist/index-editor.css +497 -0
  3. package/dist/index.css +942 -0
  4. package/dist/index.css.map +1 -0
  5. package/dist/index.js +14319 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/translations/af.d.ts +8 -0
  8. package/dist/translations/af.js +5 -0
  9. package/dist/translations/ar.d.ts +8 -0
  10. package/dist/translations/ar.js +5 -0
  11. package/dist/translations/ast.d.ts +8 -0
  12. package/dist/translations/ast.js +5 -0
  13. package/dist/translations/az.d.ts +8 -0
  14. package/dist/translations/az.js +5 -0
  15. package/dist/translations/bg.d.ts +8 -0
  16. package/dist/translations/bg.js +5 -0
  17. package/dist/translations/bn.d.ts +8 -0
  18. package/dist/translations/bn.js +5 -0
  19. package/dist/translations/bs.d.ts +8 -0
  20. package/dist/translations/bs.js +5 -0
  21. package/dist/translations/ca.d.ts +8 -0
  22. package/dist/translations/ca.js +5 -0
  23. package/dist/translations/cs.d.ts +8 -0
  24. package/dist/translations/cs.js +5 -0
  25. package/dist/translations/da.d.ts +8 -0
  26. package/dist/translations/da.js +5 -0
  27. package/dist/translations/de-ch.d.ts +8 -0
  28. package/dist/translations/de-ch.js +5 -0
  29. package/dist/translations/de.d.ts +8 -0
  30. package/dist/translations/de.js +5 -0
  31. package/dist/translations/el.d.ts +8 -0
  32. package/dist/translations/el.js +5 -0
  33. package/dist/translations/en-au.d.ts +8 -0
  34. package/dist/translations/en-au.js +5 -0
  35. package/dist/translations/en-gb.d.ts +8 -0
  36. package/dist/translations/en-gb.js +5 -0
  37. package/dist/translations/en.d.ts +8 -0
  38. package/dist/translations/en.js +5 -0
  39. package/dist/translations/eo.d.ts +8 -0
  40. package/dist/translations/eo.js +5 -0
  41. package/dist/translations/es-co.d.ts +8 -0
  42. package/dist/translations/es-co.js +5 -0
  43. package/dist/translations/es.d.ts +8 -0
  44. package/dist/translations/es.js +5 -0
  45. package/dist/translations/et.d.ts +8 -0
  46. package/dist/translations/et.js +5 -0
  47. package/dist/translations/eu.d.ts +8 -0
  48. package/dist/translations/eu.js +5 -0
  49. package/dist/translations/fa.d.ts +8 -0
  50. package/dist/translations/fa.js +5 -0
  51. package/dist/translations/fi.d.ts +8 -0
  52. package/dist/translations/fi.js +5 -0
  53. package/dist/translations/fr.d.ts +8 -0
  54. package/dist/translations/fr.js +5 -0
  55. package/dist/translations/gl.d.ts +8 -0
  56. package/dist/translations/gl.js +5 -0
  57. package/dist/translations/he.d.ts +8 -0
  58. package/dist/translations/he.js +5 -0
  59. package/dist/translations/hi.d.ts +8 -0
  60. package/dist/translations/hi.js +5 -0
  61. package/dist/translations/hr.d.ts +8 -0
  62. package/dist/translations/hr.js +5 -0
  63. package/dist/translations/hu.d.ts +8 -0
  64. package/dist/translations/hu.js +5 -0
  65. package/dist/translations/id.d.ts +8 -0
  66. package/dist/translations/id.js +5 -0
  67. package/dist/translations/it.d.ts +8 -0
  68. package/dist/translations/it.js +5 -0
  69. package/dist/translations/ja.d.ts +8 -0
  70. package/dist/translations/ja.js +5 -0
  71. package/dist/translations/jv.d.ts +8 -0
  72. package/dist/translations/jv.js +5 -0
  73. package/dist/translations/km.d.ts +8 -0
  74. package/dist/translations/km.js +5 -0
  75. package/dist/translations/kn.d.ts +8 -0
  76. package/dist/translations/kn.js +5 -0
  77. package/dist/translations/ko.d.ts +8 -0
  78. package/dist/translations/ko.js +5 -0
  79. package/dist/translations/ku.d.ts +8 -0
  80. package/dist/translations/ku.js +5 -0
  81. package/dist/translations/lt.d.ts +8 -0
  82. package/dist/translations/lt.js +5 -0
  83. package/dist/translations/lv.d.ts +8 -0
  84. package/dist/translations/lv.js +5 -0
  85. package/dist/translations/ms.d.ts +8 -0
  86. package/dist/translations/ms.js +5 -0
  87. package/dist/translations/nb.d.ts +8 -0
  88. package/dist/translations/nb.js +5 -0
  89. package/dist/translations/ne.d.ts +8 -0
  90. package/dist/translations/ne.js +5 -0
  91. package/dist/translations/nl.d.ts +8 -0
  92. package/dist/translations/nl.js +5 -0
  93. package/dist/translations/no.d.ts +8 -0
  94. package/dist/translations/no.js +5 -0
  95. package/dist/translations/pl.d.ts +8 -0
  96. package/dist/translations/pl.js +5 -0
  97. package/dist/translations/pt-br.d.ts +8 -0
  98. package/dist/translations/pt-br.js +5 -0
  99. package/dist/translations/pt.d.ts +8 -0
  100. package/dist/translations/pt.js +5 -0
  101. package/dist/translations/ro.d.ts +8 -0
  102. package/dist/translations/ro.js +5 -0
  103. package/dist/translations/ru.d.ts +8 -0
  104. package/dist/translations/ru.js +5 -0
  105. package/dist/translations/sk.d.ts +8 -0
  106. package/dist/translations/sk.js +5 -0
  107. package/dist/translations/sl.d.ts +8 -0
  108. package/dist/translations/sl.js +5 -0
  109. package/dist/translations/sq.d.ts +8 -0
  110. package/dist/translations/sq.js +5 -0
  111. package/dist/translations/sr-latn.d.ts +8 -0
  112. package/dist/translations/sr-latn.js +5 -0
  113. package/dist/translations/sr.d.ts +8 -0
  114. package/dist/translations/sr.js +5 -0
  115. package/dist/translations/sv.d.ts +8 -0
  116. package/dist/translations/sv.js +5 -0
  117. package/dist/translations/th.d.ts +8 -0
  118. package/dist/translations/th.js +5 -0
  119. package/dist/translations/tk.d.ts +8 -0
  120. package/dist/translations/tk.js +5 -0
  121. package/dist/translations/tr.d.ts +8 -0
  122. package/dist/translations/tr.js +5 -0
  123. package/dist/translations/tt.d.ts +8 -0
  124. package/dist/translations/tt.js +5 -0
  125. package/dist/translations/ug.d.ts +8 -0
  126. package/dist/translations/ug.js +5 -0
  127. package/dist/translations/uk.d.ts +8 -0
  128. package/dist/translations/uk.js +5 -0
  129. package/dist/translations/ur.d.ts +8 -0
  130. package/dist/translations/ur.js +5 -0
  131. package/dist/translations/uz.d.ts +8 -0
  132. package/dist/translations/uz.js +5 -0
  133. package/dist/translations/vi.d.ts +8 -0
  134. package/dist/translations/vi.js +5 -0
  135. package/dist/translations/zh-cn.d.ts +8 -0
  136. package/dist/translations/zh-cn.js +5 -0
  137. package/dist/translations/zh.d.ts +8 -0
  138. package/dist/translations/zh.js +5 -0
  139. package/dist/types/arialiveannouncer.d.ts +113 -0
  140. package/dist/types/augmentation.d.ts +92 -0
  141. package/dist/types/autocomplete/autocompleteview.d.ts +85 -0
  142. package/dist/types/bindings/addkeyboardhandlingforgrid.d.ts +31 -0
  143. package/dist/types/bindings/clickoutsidehandler.d.ts +32 -0
  144. package/dist/types/bindings/csstransitiondisablermixin.d.ts +44 -0
  145. package/dist/types/bindings/draggableviewmixin.d.ts +50 -0
  146. package/dist/types/bindings/injectcsstransitiondisabler.d.ts +63 -0
  147. package/dist/types/bindings/preventdefault.d.ts +37 -0
  148. package/dist/types/bindings/submithandler.d.ts +61 -0
  149. package/dist/types/button/button.d.ts +185 -0
  150. package/dist/types/button/buttonlabel.d.ts +38 -0
  151. package/dist/types/button/buttonlabelview.d.ts +35 -0
  152. package/dist/types/button/buttonview.d.ts +189 -0
  153. package/dist/types/button/filedialogbuttonview.d.ts +84 -0
  154. package/dist/types/button/switchbuttonview.d.ts +49 -0
  155. package/dist/types/collapsible/collapsibleview.d.ts +74 -0
  156. package/dist/types/colorgrid/colorgridview.d.ts +136 -0
  157. package/dist/types/colorgrid/colortileview.d.ts +32 -0
  158. package/dist/types/colorgrid/utils.d.ts +51 -0
  159. package/dist/types/colorpicker/colorpickerview.d.ts +177 -0
  160. package/dist/types/colorpicker/utils.d.ts +52 -0
  161. package/dist/types/colorselector/colorgridsfragmentview.d.ts +199 -0
  162. package/dist/types/colorselector/colorpickerfragmentview.d.ts +137 -0
  163. package/dist/types/colorselector/colorselectorview.d.ts +246 -0
  164. package/dist/types/colorselector/documentcolorcollection.d.ts +74 -0
  165. package/dist/types/componentfactory.d.ts +85 -0
  166. package/dist/types/dialog/dialog.d.ts +277 -0
  167. package/dist/types/dialog/dialogactionsview.d.ts +73 -0
  168. package/dist/types/dialog/dialogcontentview.d.ts +31 -0
  169. package/dist/types/dialog/dialogview.d.ts +260 -0
  170. package/dist/types/dropdown/button/dropdownbutton.d.ts +29 -0
  171. package/dist/types/dropdown/button/dropdownbuttonview.d.ts +52 -0
  172. package/dist/types/dropdown/button/splitbuttonview.d.ts +166 -0
  173. package/dist/types/dropdown/dropdownpanelfocusable.d.ts +25 -0
  174. package/dist/types/dropdown/dropdownpanelview.d.ts +66 -0
  175. package/dist/types/dropdown/dropdownview.d.ts +319 -0
  176. package/dist/types/dropdown/utils.d.ts +239 -0
  177. package/dist/types/editableui/editableuiview.d.ts +76 -0
  178. package/dist/types/editableui/inline/inlineeditableuiview.d.ts +44 -0
  179. package/dist/types/editorui/accessibilityhelp/accessibilityhelp.d.ts +55 -0
  180. package/dist/types/editorui/accessibilityhelp/accessibilityhelpcontentview.d.ts +39 -0
  181. package/dist/types/editorui/bodycollection.d.ts +59 -0
  182. package/dist/types/editorui/boxed/boxededitoruiview.d.ts +44 -0
  183. package/dist/types/editorui/editorui.d.ts +292 -0
  184. package/dist/types/editorui/editoruiview.d.ts +43 -0
  185. package/dist/types/editorui/poweredby.d.ts +75 -0
  186. package/dist/types/focuscycler.d.ts +249 -0
  187. package/dist/types/formheader/formheaderview.d.ts +63 -0
  188. package/dist/types/highlightedtext/highlightedtextview.d.ts +42 -0
  189. package/dist/types/icon/iconview.d.ts +92 -0
  190. package/dist/types/iframe/iframeview.d.ts +54 -0
  191. package/dist/types/index.d.ts +87 -0
  192. package/dist/types/input/inputbase.d.ts +123 -0
  193. package/dist/types/input/inputview.d.ts +40 -0
  194. package/dist/types/inputnumber/inputnumberview.d.ts +53 -0
  195. package/dist/types/inputtext/inputtextview.d.ts +22 -0
  196. package/dist/types/label/labelview.d.ts +40 -0
  197. package/dist/types/labeledfield/labeledfieldview.d.ts +191 -0
  198. package/dist/types/labeledfield/utils.d.ts +127 -0
  199. package/dist/types/labeledinput/labeledinputview.d.ts +129 -0
  200. package/dist/types/list/listitemgroupview.d.ts +63 -0
  201. package/dist/types/list/listitemview.d.ts +40 -0
  202. package/dist/types/list/listseparatorview.d.ts +22 -0
  203. package/dist/types/list/listview.d.ts +128 -0
  204. package/dist/types/menubar/menubarmenubuttonview.d.ts +39 -0
  205. package/dist/types/menubar/menubarmenulistitembuttonview.d.ts +25 -0
  206. package/dist/types/menubar/menubarmenulistitemfiledialogbuttonview.d.ts +27 -0
  207. package/dist/types/menubar/menubarmenulistitemview.d.ts +29 -0
  208. package/dist/types/menubar/menubarmenulistview.d.ts +28 -0
  209. package/dist/types/menubar/menubarmenupanelview.d.ts +57 -0
  210. package/dist/types/menubar/menubarmenuview.d.ts +113 -0
  211. package/dist/types/menubar/menubarview.d.ts +168 -0
  212. package/dist/types/menubar/utils.d.ts +436 -0
  213. package/dist/types/model.d.ts +26 -0
  214. package/dist/types/notification/notification.d.ts +215 -0
  215. package/dist/types/panel/balloon/balloonpanelview.d.ts +689 -0
  216. package/dist/types/panel/balloon/contextualballoon.d.ts +303 -0
  217. package/dist/types/panel/sticky/stickypanelview.d.ts +160 -0
  218. package/dist/types/search/filteredview.d.ts +35 -0
  219. package/dist/types/search/searchinfoview.d.ts +49 -0
  220. package/dist/types/search/searchresultsview.d.ts +58 -0
  221. package/dist/types/search/text/searchtextqueryview.d.ts +80 -0
  222. package/dist/types/search/text/searchtextview.d.ts +223 -0
  223. package/dist/types/spinner/spinnerview.d.ts +29 -0
  224. package/dist/types/template.d.ts +946 -0
  225. package/dist/types/textarea/textareaview.d.ts +108 -0
  226. package/dist/types/toolbar/balloon/balloontoolbar.d.ts +121 -0
  227. package/dist/types/toolbar/block/blockbuttonview.d.ts +39 -0
  228. package/dist/types/toolbar/block/blocktoolbar.d.ts +157 -0
  229. package/dist/types/toolbar/normalizetoolbarconfig.d.ts +44 -0
  230. package/dist/types/toolbar/toolbarlinebreakview.d.ts +22 -0
  231. package/dist/types/toolbar/toolbarseparatorview.d.ts +22 -0
  232. package/dist/types/toolbar/toolbarview.d.ts +271 -0
  233. package/dist/types/tooltipmanager.d.ts +199 -0
  234. package/dist/types/view.d.ts +426 -0
  235. package/dist/types/viewcollection.d.ts +143 -0
  236. package/lang/contexts.json +2 -1
  237. package/package.json +4 -3
  238. package/src/arialiveannouncer.d.ts +34 -23
  239. package/src/arialiveannouncer.js +80 -30
  240. package/src/colorpicker/colorpickerview.d.ts +32 -1
  241. package/src/colorpicker/colorpickerview.js +62 -13
  242. package/src/colorselector/colorpickerfragmentview.d.ts +4 -0
  243. package/src/colorselector/colorpickerfragmentview.js +13 -5
  244. package/src/colorselector/colorselectorview.js +1 -0
  245. package/src/index.d.ts +3 -3
  246. package/src/index.js +2 -1
  247. package/src/toolbar/toolbarview.js +2 -3
  248. package/theme/components/arialiveannouncer/arialiveannouncer.css +4 -0
  249. package/theme/components/tooltip/tooltip.css +4 -0
  250. package/theme/globals/globals.css +0 -1
  251. package/theme/globals/_reset.css +0 -13
@@ -2,9 +2,6 @@
2
2
  * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
- /**
6
- * @module ui/arialiveannouncer
7
- */
8
5
  import type { Editor } from '@ckeditor/ckeditor5-core';
9
6
  import type { Locale } from '@ckeditor/ckeditor5-utils';
10
7
  import type ViewCollection from './viewcollection.js';
@@ -30,9 +27,10 @@ export declare const AriaLiveAnnouncerPoliteness: {
30
27
  * These announcements are consumed and propagated by screen readers and give users a better understanding of the current
31
28
  * state of the editor.
32
29
  *
33
- * To announce a state change to an editor feature named `'Some feature'`, use the {@link #announce} method:
30
+ * To announce a state change to an editor use the {@link #announce} method:
31
+ *
34
32
  * ```ts
35
- * editor.ui.ariaLiveAnnouncer.announce( 'Some feature', 'Text of an announcement.' );
33
+ * editor.ui.ariaLiveAnnouncer.announce( 'Text of an announcement.' );
36
34
  * ```
37
35
  */
38
36
  export default class AriaLiveAnnouncer {
@@ -49,24 +47,19 @@ export default class AriaLiveAnnouncer {
49
47
  */
50
48
  constructor(editor: Editor);
51
49
  /**
52
- * Sets an announcement text to an aria region associated with a specific editor feature. The text is then
53
- * announced by a screen reader to the user.
54
- *
55
- * If the aria region of a given name does not exist, it will be created and can be re-used later. The name of the region
56
- * groups announcements originating from a specific editor feature and does not get announced by a screen reader.
50
+ * Sets an announcement text to an aria region that is then announced by a screen reader to the user.
57
51
  *
58
- * Using multiple regions allows for many announcements to be emitted in a short period of time. Changes to ARIA-live announcements
59
- * are captured by a screen reader and read out in the order they were emitted.
52
+ * If the aria region of a specified politeness does not exist, it will be created and can be re-used later.
60
53
  *
61
54
  * The default announcement politeness level is `'polite'`.
62
55
  *
63
56
  * ```ts
64
57
  * // Most screen readers will queue announcements from multiple aria-live regions and read them out in the order they were emitted.
65
- * editor.ui.ariaLiveAnnouncer.announce( 'image', 'Image uploaded.' );
66
- * editor.ui.ariaLiveAnnouncer.announce( 'network', 'Connection lost. Reconnecting.' );
58
+ * editor.ui.ariaLiveAnnouncer.announce( 'Image uploaded.' );
59
+ * editor.ui.ariaLiveAnnouncer.announce( 'Connection lost. Reconnecting.' );
67
60
  * ```
68
61
  */
69
- announce(regionName: string, announcementText: string, politeness?: typeof AriaLiveAnnouncerPoliteness[keyof typeof AriaLiveAnnouncerPoliteness]): void;
62
+ announce(announcement: string, attributes?: AriaLiveAnnouncerPolitenessValue | AriaLiveAnnounceConfig): void;
70
63
  }
71
64
  /**
72
65
  * The view that aggregates all `aria-live` regions.
@@ -79,20 +72,38 @@ export declare class AriaLiveAnnouncerView extends View {
79
72
  constructor(locale: Locale);
80
73
  }
81
74
  /**
82
- * The view that represents a single `aria-live` region (e.g. for a specific editor feature) and its text.
75
+ * The view that represents a single `aria-live`.
83
76
  */
84
77
  export declare class AriaLiveAnnouncerRegionView extends View {
85
78
  /**
86
- * Current text of the region.
79
+ * Current politeness level of the region.
87
80
  */
88
- text: string;
81
+ readonly politeness: AriaLiveAnnouncerPolitenessValue;
89
82
  /**
90
- * Current politeness level of the region.
83
+ * DOM converter used to sanitize unsafe HTML passed to {@link #announce} method.
91
84
  */
92
- politeness: typeof AriaLiveAnnouncerPoliteness[keyof typeof AriaLiveAnnouncerPoliteness];
85
+ private _domConverter;
93
86
  /**
94
- * A unique name of the region, usually associated with a specific editor feature or system.
87
+ * Interval used to remove additions. It prevents accumulation of added nodes in region.
95
88
  */
96
- regionName: string;
97
- constructor(locale: Locale);
89
+ private _pruneAnnouncementsInterval;
90
+ constructor(editor: Editor, politeness: AriaLiveAnnouncerPolitenessValue);
91
+ /**
92
+ * Appends new announcement to region.
93
+ */
94
+ announce({ announcement, isUnsafeHTML }: AriaLiveAppendContentAttributes): void;
95
+ /**
96
+ * Return current announcements list HTML element.
97
+ */
98
+ private get _listElement();
98
99
  }
100
+ type AriaLiveAnnouncerPolitenessValue = typeof AriaLiveAnnouncerPoliteness[keyof typeof AriaLiveAnnouncerPoliteness];
101
+ type AriaLiveAppendContentAttributes = {
102
+ announcement: string;
103
+ isUnsafeHTML?: boolean;
104
+ };
105
+ type AriaLiveAnnounceConfig = {
106
+ politeness: AriaLiveAnnouncerPolitenessValue;
107
+ isUnsafeHTML?: boolean;
108
+ };
109
+ export {};
@@ -24,9 +24,10 @@ export const AriaLiveAnnouncerPoliteness = {
24
24
  * These announcements are consumed and propagated by screen readers and give users a better understanding of the current
25
25
  * state of the editor.
26
26
  *
27
- * To announce a state change to an editor feature named `'Some feature'`, use the {@link #announce} method:
27
+ * To announce a state change to an editor use the {@link #announce} method:
28
+ *
28
29
  * ```ts
29
- * editor.ui.ariaLiveAnnouncer.announce( 'Some feature', 'Text of an announcement.' );
30
+ * editor.ui.ariaLiveAnnouncer.announce( 'Text of an announcement.' );
30
31
  * ```
31
32
  */
32
33
  export default class AriaLiveAnnouncer {
@@ -35,40 +36,50 @@ export default class AriaLiveAnnouncer {
35
36
  */
36
37
  constructor(editor) {
37
38
  this.editor = editor;
39
+ /**
40
+ * Some screen readers only look at changes in the aria-live region.
41
+ * They might not read a region that already has content when it is added.
42
+ * To stop this problem, make sure to set up regions for all politeness settings when the editor starts.
43
+ */
44
+ editor.once('ready', () => {
45
+ for (const politeness of Object.values(AriaLiveAnnouncerPoliteness)) {
46
+ this.announce('', politeness);
47
+ }
48
+ });
38
49
  }
39
50
  /**
40
- * Sets an announcement text to an aria region associated with a specific editor feature. The text is then
41
- * announced by a screen reader to the user.
51
+ * Sets an announcement text to an aria region that is then announced by a screen reader to the user.
42
52
  *
43
- * If the aria region of a given name does not exist, it will be created and can be re-used later. The name of the region
44
- * groups announcements originating from a specific editor feature and does not get announced by a screen reader.
45
- *
46
- * Using multiple regions allows for many announcements to be emitted in a short period of time. Changes to ARIA-live announcements
47
- * are captured by a screen reader and read out in the order they were emitted.
53
+ * If the aria region of a specified politeness does not exist, it will be created and can be re-used later.
48
54
  *
49
55
  * The default announcement politeness level is `'polite'`.
50
56
  *
51
57
  * ```ts
52
58
  * // Most screen readers will queue announcements from multiple aria-live regions and read them out in the order they were emitted.
53
- * editor.ui.ariaLiveAnnouncer.announce( 'image', 'Image uploaded.' );
54
- * editor.ui.ariaLiveAnnouncer.announce( 'network', 'Connection lost. Reconnecting.' );
59
+ * editor.ui.ariaLiveAnnouncer.announce( 'Image uploaded.' );
60
+ * editor.ui.ariaLiveAnnouncer.announce( 'Connection lost. Reconnecting.' );
55
61
  * ```
56
62
  */
57
- announce(regionName, announcementText, politeness = AriaLiveAnnouncerPoliteness.POLITE) {
63
+ announce(announcement, attributes = AriaLiveAnnouncerPoliteness.POLITE) {
58
64
  const editor = this.editor;
65
+ if (!editor.ui.view) {
66
+ return;
67
+ }
59
68
  if (!this.view) {
60
69
  this.view = new AriaLiveAnnouncerView(editor.locale);
61
70
  editor.ui.view.body.add(this.view);
62
71
  }
63
- let regionView = this.view.regionViews.find(view => view.regionName === regionName);
64
- if (!regionView) {
65
- regionView = new AriaLiveAnnouncerRegionView(this.view.locale);
66
- this.view.regionViews.add(regionView);
72
+ const { politeness, isUnsafeHTML } = typeof attributes === 'string' ? {
73
+ politeness: attributes
74
+ } : attributes;
75
+ let politenessRegionView = this.view.regionViews.find(view => view.politeness === politeness);
76
+ if (!politenessRegionView) {
77
+ politenessRegionView = new AriaLiveAnnouncerRegionView(editor, politeness);
78
+ this.view.regionViews.add(politenessRegionView);
67
79
  }
68
- regionView.set({
69
- regionName,
70
- text: announcementText,
71
- politeness
80
+ politenessRegionView.announce({
81
+ announcement,
82
+ isUnsafeHTML
72
83
  });
73
84
  }
74
85
  }
@@ -92,25 +103,64 @@ export class AriaLiveAnnouncerView extends View {
92
103
  }
93
104
  }
94
105
  /**
95
- * The view that represents a single `aria-live` region (e.g. for a specific editor feature) and its text.
106
+ * The view that represents a single `aria-live`.
96
107
  */
97
108
  export class AriaLiveAnnouncerRegionView extends View {
98
- constructor(locale) {
99
- super(locale);
100
- const bind = this.bindTemplate;
101
- this.set('regionName', '');
102
- this.set('text', '');
103
- this.set('politeness', AriaLiveAnnouncerPoliteness.POLITE);
109
+ constructor(editor, politeness) {
110
+ super(editor.locale);
104
111
  this.setTemplate({
105
112
  tag: 'div',
106
113
  attributes: {
107
114
  role: 'region',
108
- 'data-region': bind.to('regionName'),
109
- 'aria-live': bind.to('politeness')
115
+ 'aria-live': politeness,
116
+ 'aria-relevant': 'additions'
110
117
  },
111
118
  children: [
112
- { text: bind.to('text') }
119
+ {
120
+ tag: 'ul',
121
+ attributes: {
122
+ class: [
123
+ 'ck',
124
+ 'ck-aria-live-region-list'
125
+ ]
126
+ }
127
+ }
113
128
  ]
114
129
  });
130
+ editor.on('destroy', () => {
131
+ if (this._pruneAnnouncementsInterval !== null) {
132
+ clearInterval(this._pruneAnnouncementsInterval);
133
+ this._pruneAnnouncementsInterval = null;
134
+ }
135
+ });
136
+ this.politeness = politeness;
137
+ this._domConverter = editor.data.htmlProcessor.domConverter;
138
+ this._pruneAnnouncementsInterval = setInterval(() => {
139
+ if (this.element && this._listElement.firstChild) {
140
+ this._listElement.firstChild.remove();
141
+ }
142
+ }, 5000);
143
+ }
144
+ /**
145
+ * Appends new announcement to region.
146
+ */
147
+ announce({ announcement, isUnsafeHTML }) {
148
+ if (!announcement.trim().length) {
149
+ return;
150
+ }
151
+ const messageListItem = document.createElement('li');
152
+ if (isUnsafeHTML) {
153
+ this._domConverter.setContentOf(messageListItem, announcement);
154
+ }
155
+ else {
156
+ messageListItem.innerText = announcement;
157
+ }
158
+ this._listElement.appendChild(messageListItem);
159
+ }
160
+ /**
161
+ * Return current announcements list HTML element.
162
+ */
163
+ get _listElement() {
164
+ return this.element.querySelector('ul');
115
165
  }
116
166
  }
@@ -6,9 +6,12 @@
6
6
  * @module ui/colorpicker/colorpickerview
7
7
  */
8
8
  import { type ColorPickerViewConfig } from './utils.js';
9
+ import type { HexColor } from '@ckeditor/ckeditor5-core';
9
10
  import { type Locale } from '@ckeditor/ckeditor5-utils';
10
11
  import View from '../view.js';
12
+ import type InputTextView from '../inputtext/inputtextview.js';
11
13
  import type ViewCollection from '../viewcollection.js';
14
+ import LabeledFieldView from '../labeledfield/labeledfieldview.js';
12
15
  import { HexBase } from 'vanilla-colorful/lib/entrypoints/hex';
13
16
  import '../../theme/components/colorpicker/colorpicker.css';
14
17
  declare global {
@@ -98,6 +101,17 @@ export default class ColorPickerView extends View {
98
101
  * @private
99
102
  */
100
103
  private _createColorInput;
104
+ /**
105
+ * Validates the view and returns `false` when some fields are invalid.
106
+ */
107
+ isValid(): boolean;
108
+ /**
109
+ * Cleans up the supplementary error and information text of input inside the {@link #hexInputRow}
110
+ * bringing them back to the state when the form has been displayed for the first time.
111
+ *
112
+ * See {@link #isValid}.
113
+ */
114
+ resetValidationStatus(): void;
101
115
  }
102
116
  declare class SliderView extends View {
103
117
  /**
@@ -114,12 +128,21 @@ declare class ColorPickerInputRowView extends View {
114
128
  * A collection of row items (buttons, dropdowns, etc.).
115
129
  */
116
130
  readonly children: ViewCollection;
131
+ /**
132
+ * Hex input view element.
133
+ */
134
+ readonly inputView: LabeledFieldView<InputTextView>;
117
135
  /**
118
136
  * Creates an instance of the form row class.
119
137
  *
120
138
  * @param locale The locale instance.
139
+ * @param inputView Hex color input element.
140
+ */
141
+ constructor(locale: Locale, inputView: LabeledFieldView<InputTextView>);
142
+ /**
143
+ * Returns false if color input value is not in hex format.
121
144
  */
122
- constructor(locale: Locale, children?: Array<View>);
145
+ getParsedColor(): HexColor | null;
123
146
  }
124
147
  /**
125
148
  * An event fired whenever the color was selected through the color picker palette
@@ -139,4 +162,12 @@ export type ColorPickerColorSelectedEvent = {
139
162
  }
140
163
  ];
141
164
  };
165
+ /**
166
+ * Trim spaces from provided color and check if hex is valid.
167
+ *
168
+ * @param color Unsafe color string.
169
+ * @returns Null if provided color is not hex value.
170
+ * @export
171
+ */
172
+ export declare function tryParseHexColor<S extends string>(color: S | null | undefined): HexColor<S> | null;
142
173
  export {};
@@ -150,9 +150,8 @@ export default class ColorPickerView extends View {
150
150
  * @private
151
151
  */
152
152
  _createInputRow() {
153
- const hashView = new HashView();
154
153
  const colorInput = this._createColorInput();
155
- return new ColorPickerInputRowView(this.locale, [hashView, colorInput]);
154
+ return new ColorPickerInputRowView(this.locale, colorInput);
156
155
  }
157
156
  /**
158
157
  * Creates the input where user can type or paste the color in hex format.
@@ -180,22 +179,39 @@ export default class ColorPickerView extends View {
180
179
  labeledInput.fieldView.on('input', () => {
181
180
  const inputValue = labeledInput.fieldView.element.value;
182
181
  if (inputValue) {
183
- // Trim the whitespace.
184
- const trimmedValue = inputValue.trim();
185
- // Drop the `#` from the beginning if present.
186
- const hashlessInput = trimmedValue.startsWith('#') ? trimmedValue.substring(1) : trimmedValue;
187
- // Check if it's a hex color (3,4,6 or 8 chars long and with proper characters).
188
- const isValidHexColor = [3, 4, 6, 8].includes(hashlessInput.length) &&
189
- /(([0-9a-fA-F]{2}){3,4}|([0-9a-fA-F]){3,4})/.test(hashlessInput);
190
- if (isValidHexColor) {
182
+ const maybeHexColor = tryParseHexColor(inputValue);
183
+ if (maybeHexColor) {
191
184
  // If so, set the color.
192
185
  // Otherwise, do nothing.
193
- this._debounceColorPickerEvent('#' + hashlessInput);
186
+ this._debounceColorPickerEvent(maybeHexColor);
194
187
  }
195
188
  }
196
189
  });
197
190
  return labeledInput;
198
191
  }
192
+ /**
193
+ * Validates the view and returns `false` when some fields are invalid.
194
+ */
195
+ isValid() {
196
+ const { t } = this.locale;
197
+ this.resetValidationStatus();
198
+ // One error per field is enough.
199
+ if (!this.hexInputRow.getParsedColor()) {
200
+ // Apply updated error.
201
+ this.hexInputRow.inputView.errorText = t('Please enter a valid color (e.g. "ff0000").');
202
+ return false;
203
+ }
204
+ return true;
205
+ }
206
+ /**
207
+ * Cleans up the supplementary error and information text of input inside the {@link #hexInputRow}
208
+ * bringing them back to the state when the form has been displayed for the first time.
209
+ *
210
+ * See {@link #isValid}.
211
+ */
212
+ resetValidationStatus() {
213
+ this.hexInputRow.inputView.errorText = null;
214
+ }
199
215
  }
200
216
  // Converts any color format to a unified hex format.
201
217
  //
@@ -254,10 +270,15 @@ class ColorPickerInputRowView extends View {
254
270
  * Creates an instance of the form row class.
255
271
  *
256
272
  * @param locale The locale instance.
273
+ * @param inputView Hex color input element.
257
274
  */
258
- constructor(locale, children) {
275
+ constructor(locale, inputView) {
259
276
  super(locale);
260
- this.children = this.createCollection(children);
277
+ this.inputView = inputView;
278
+ this.children = this.createCollection([
279
+ new HashView(),
280
+ this.inputView
281
+ ]);
261
282
  this.setTemplate({
262
283
  tag: 'div',
263
284
  attributes: {
@@ -269,4 +290,32 @@ class ColorPickerInputRowView extends View {
269
290
  children: this.children
270
291
  });
271
292
  }
293
+ /**
294
+ * Returns false if color input value is not in hex format.
295
+ */
296
+ getParsedColor() {
297
+ return tryParseHexColor(this.inputView.fieldView.element.value);
298
+ }
299
+ }
300
+ /**
301
+ * Trim spaces from provided color and check if hex is valid.
302
+ *
303
+ * @param color Unsafe color string.
304
+ * @returns Null if provided color is not hex value.
305
+ * @export
306
+ */
307
+ export function tryParseHexColor(color) {
308
+ if (!color) {
309
+ return null;
310
+ }
311
+ const hashLessColor = color.trim().replace(/^#/, '');
312
+ // Incorrect length.
313
+ if (![3, 4, 6, 8].includes(hashLessColor.length)) {
314
+ return null;
315
+ }
316
+ // Incorrect characters.
317
+ if (!/^(([0-9a-fA-F]{2}){3,4}|([0-9a-fA-F]){3,4})$/.test(hashLessColor)) {
318
+ return null;
319
+ }
320
+ return `#${hashLessColor}`;
272
321
  }
@@ -101,6 +101,10 @@ export default class ColorPickerFragmentView extends View {
101
101
  * Focuses the color picker.
102
102
  */
103
103
  focus(): void;
104
+ /**
105
+ * Reset validation messages.
106
+ */
107
+ resetValidationStatus(): void;
104
108
  /**
105
109
  * When color picker is focused and "enter" is pressed it executes command.
106
110
  */
@@ -91,12 +91,18 @@ export default class ColorPickerFragmentView extends View {
91
91
  focus() {
92
92
  this.colorPickerView.focus();
93
93
  }
94
+ /**
95
+ * Reset validation messages.
96
+ */
97
+ resetValidationStatus() {
98
+ this.colorPickerView.resetValidationStatus();
99
+ }
94
100
  /**
95
101
  * When color picker is focused and "enter" is pressed it executes command.
96
102
  */
97
103
  _executeOnEnterPress() {
98
104
  this.keystrokes.set('enter', evt => {
99
- if (this.isVisible && this.focusTracker.focusedElement !== this.cancelButtonView.element) {
105
+ if (this.isVisible && this.focusTracker.focusedElement !== this.cancelButtonView.element && this.colorPickerView.isValid()) {
100
106
  this.fire('execute', {
101
107
  value: this.selectedColor
102
108
  });
@@ -176,10 +182,12 @@ export default class ColorPickerFragmentView extends View {
176
182
  label: t('Cancel')
177
183
  });
178
184
  saveButtonView.on('execute', () => {
179
- this.fire('execute', {
180
- source: 'colorPickerSaveButton',
181
- value: this.selectedColor
182
- });
185
+ if (this.colorPickerView.isValid()) {
186
+ this.fire('execute', {
187
+ source: 'colorPickerSaveButton',
188
+ value: this.selectedColor
189
+ });
190
+ }
183
191
  });
184
192
  cancelButtonView.on('execute', () => {
185
193
  this.fire('colorPicker:cancel');
@@ -176,6 +176,7 @@ export default class ColorSelectorView extends View {
176
176
  }
177
177
  this._isColorPickerFragmentVisible = true;
178
178
  this.colorPickerFragmentView.focus();
179
+ this.colorPickerFragmentView.resetValidationStatus();
179
180
  this._isColorGridsFragmentVisible = false;
180
181
  }
181
182
  /**
package/src/index.d.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  /**
6
6
  * @module ui
7
7
  */
8
+ export { default as View, type UIViewRenderEvent } from './view.js';
8
9
  export { default as clickOutsideHandler } from './bindings/clickoutsidehandler.js';
9
10
  export { default as injectCssTransitionDisabler } from './bindings/injectcsstransitiondisabler.js';
10
11
  export { default as CssTransitionDisablerMixin, type ViewWithCssTransitionDisabler } from './bindings/csstransitiondisablermixin.js';
@@ -12,7 +13,7 @@ export { default as submitHandler } from './bindings/submithandler.js';
12
13
  export { default as addKeyboardHandlingForGrid } from './bindings/addkeyboardhandlingforgrid.js';
13
14
  export { default as AccessibilityHelp } from './editorui/accessibilityhelp/accessibilityhelp.js';
14
15
  export { default as BodyCollection } from './editorui/bodycollection.js';
15
- export { default as Button, type ButtonExecuteEvent } from './button/button.js';
16
+ export type { default as Button, ButtonExecuteEvent } from './button/button.js';
16
17
  export type { default as ButtonLabel } from './button/buttonlabel.js';
17
18
  export { default as ButtonView } from './button/buttonview.js';
18
19
  export { default as ButtonLabelView } from './button/buttonlabelview.js';
@@ -60,7 +61,7 @@ export { default as StickyPanelView } from './panel/sticky/stickypanelview.js';
60
61
  export { default as AutocompleteView, type AutocompleteViewConfig, type AutocompleteResultsView } from './autocomplete/autocompleteview.js';
61
62
  export { default as SearchTextView, type SearchTextViewSearchEvent, type SearchTextViewConfig } from './search/text/searchtextview.js';
62
63
  export { default as SearchInfoView } from './search/searchinfoview.js';
63
- export { default as FilteredView, type FilteredViewExecuteEvent } from './search/filteredview.js';
64
+ export type { default as FilteredView, FilteredViewExecuteEvent } from './search/filteredview.js';
64
65
  export { default as HighlightedTextView } from './highlightedtext/highlightedtextview.js';
65
66
  export { default as TooltipManager } from './tooltipmanager.js';
66
67
  export { default as Template, type TemplateDefinition } from './template.js';
@@ -71,7 +72,6 @@ export { default as ToolbarSeparatorView } from './toolbar/toolbarseparatorview.
71
72
  export { default as normalizeToolbarConfig } from './toolbar/normalizetoolbarconfig.js';
72
73
  export { default as BalloonToolbar, type BalloonToolbarShowEvent } from './toolbar/balloon/balloontoolbar.js';
73
74
  export { default as BlockToolbar } from './toolbar/block/blocktoolbar.js';
74
- export { default as View, type UIViewRenderEvent } from './view.js';
75
75
  export { default as ViewCollection } from './viewcollection.js';
76
76
  export { default as MenuBarView, type MenuBarConfig } from './menubar/menubarview.js';
77
77
  export { default as MenuBarMenuView } from './menubar/menubarmenuview.js';
package/src/index.js CHANGED
@@ -5,6 +5,8 @@
5
5
  /**
6
6
  * @module ui
7
7
  */
8
+ // This import must be at the top to ensure that `globals.css` is imported first
9
+ export { default as View } from './view.js';
8
10
  export { default as clickOutsideHandler } from './bindings/clickoutsidehandler.js';
9
11
  export { default as injectCssTransitionDisabler } from './bindings/injectcsstransitiondisabler.js';
10
12
  export { default as CssTransitionDisablerMixin } from './bindings/csstransitiondisablermixin.js';
@@ -67,7 +69,6 @@ export { default as ToolbarSeparatorView } from './toolbar/toolbarseparatorview.
67
69
  export { default as normalizeToolbarConfig } from './toolbar/normalizetoolbarconfig.js';
68
70
  export { default as BalloonToolbar } from './toolbar/balloon/balloontoolbar.js';
69
71
  export { default as BlockToolbar } from './toolbar/block/blocktoolbar.js';
70
- export { default as View } from './view.js';
71
72
  export { default as ViewCollection } from './viewcollection.js';
72
73
  export { default as MenuBarView } from './menubar/menubarview.js';
73
74
  export { default as MenuBarMenuView } from './menubar/menubarmenuview.js';
@@ -16,7 +16,6 @@ import { FocusTracker, KeystrokeHandler, Rect, ResizeObserver, global, isVisible
16
16
  import { icons } from '@ckeditor/ckeditor5-core';
17
17
  import { isObject } from 'lodash-es';
18
18
  import '../../theme/components/toolbar/toolbar.css';
19
- const { threeVerticalDots } = icons;
20
19
  export const NESTED_TOOLBAR_ICONS = {
21
20
  alignLeft: icons.alignLeft,
22
21
  bold: icons.bold,
@@ -336,7 +335,7 @@ export default class ToolbarView extends View {
336
335
  // Allow disabling icon by passing false.
337
336
  if (icon !== false) {
338
337
  // A pre-defined icon picked by name, SVG string, a fallback (default) icon.
339
- dropdownView.buttonView.icon = NESTED_TOOLBAR_ICONS[icon] || icon || threeVerticalDots;
338
+ dropdownView.buttonView.icon = NESTED_TOOLBAR_ICONS[icon] || icon || icons.threeVerticalDots;
340
339
  }
341
340
  // If the icon is disabled, display the label automatically.
342
341
  else {
@@ -694,7 +693,7 @@ class DynamicGrouping {
694
693
  label: t('Show more items'),
695
694
  tooltip: true,
696
695
  tooltipPosition: locale.uiLanguageDirection === 'rtl' ? 'se' : 'sw',
697
- icon: threeVerticalDots
696
+ icon: icons.threeVerticalDots
698
697
  });
699
698
  return dropdown;
700
699
  }
@@ -8,3 +8,7 @@
8
8
  left: -10000px;
9
9
  top: -10000px;
10
10
  }
11
+
12
+ .ck.ck-aria-live-region-list {
13
+ list-style-type: none;
14
+ }
@@ -3,6 +3,10 @@
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
6
+ @import "../../mixins/_unselectable.css";
7
+
6
8
  .ck.ck-balloon-panel.ck-tooltip {
9
+ @mixin ck-unselectable;
10
+
7
11
  z-index: calc( var(--ck-z-dialog) + 100 );
8
12
  }
@@ -4,7 +4,6 @@
4
4
  */
5
5
 
6
6
  @import "./_hidden.css";
7
- @import "./_reset.css";
8
7
  @import "./_zindex.css";
9
8
  @import "./_transition.css";
10
9
  @import "./_poweredby.css";
@@ -1,13 +0,0 @@
1
- /*
2
- * Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
-
6
- .ck.ck-reset,
7
- .ck.ck-reset_all,
8
- .ck-reset_all *:not(.ck-reset_all-excluded *) {
9
- box-sizing: border-box;
10
- width: auto;
11
- height: auto;
12
- position: static;
13
- }