@ckeditor/ckeditor5-ui 41.3.0-alpha.4 → 41.3.1

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 (324) hide show
  1. package/ckeditor5-metadata.json +1 -1
  2. package/lang/contexts.json +12 -2
  3. package/lang/translations/af.po +210 -0
  4. package/lang/translations/ar.po +44 -4
  5. package/lang/translations/ast.po +44 -4
  6. package/lang/translations/az.po +44 -4
  7. package/lang/translations/bg.po +44 -4
  8. package/lang/translations/bn.po +44 -4
  9. package/lang/translations/bs.po +210 -0
  10. package/lang/translations/ca.po +44 -4
  11. package/lang/translations/cs.po +44 -4
  12. package/lang/translations/da.po +44 -4
  13. package/lang/translations/de-ch.po +44 -4
  14. package/lang/translations/de.po +44 -4
  15. package/lang/translations/el.po +44 -4
  16. package/lang/translations/en-au.po +44 -4
  17. package/lang/translations/en-gb.po +44 -4
  18. package/lang/translations/en.po +44 -4
  19. package/lang/translations/eo.po +44 -4
  20. package/lang/translations/es-co.po +210 -0
  21. package/lang/translations/es.po +44 -4
  22. package/lang/translations/et.po +44 -4
  23. package/lang/translations/eu.po +44 -4
  24. package/lang/translations/fa.po +44 -4
  25. package/lang/translations/fi.po +44 -4
  26. package/lang/translations/fr.po +44 -4
  27. package/lang/translations/gl.po +44 -4
  28. package/lang/translations/he.po +44 -4
  29. package/lang/translations/hi.po +44 -4
  30. package/lang/translations/hr.po +44 -4
  31. package/lang/translations/hu.po +44 -4
  32. package/lang/translations/id.po +44 -4
  33. package/lang/translations/it.po +44 -4
  34. package/lang/translations/ja.po +44 -4
  35. package/lang/translations/jv.po +210 -0
  36. package/lang/translations/km.po +44 -4
  37. package/lang/translations/kn.po +44 -4
  38. package/lang/translations/ko.po +44 -4
  39. package/lang/translations/ku.po +44 -4
  40. package/lang/translations/lt.po +44 -4
  41. package/lang/translations/lv.po +44 -4
  42. package/lang/translations/ms.po +44 -4
  43. package/lang/translations/nb.po +44 -4
  44. package/lang/translations/ne.po +44 -4
  45. package/lang/translations/nl.po +44 -4
  46. package/lang/translations/no.po +44 -4
  47. package/lang/translations/pl.po +44 -4
  48. package/lang/translations/pt-br.po +44 -4
  49. package/lang/translations/pt.po +44 -4
  50. package/lang/translations/ro.po +44 -4
  51. package/lang/translations/ru.po +44 -4
  52. package/lang/translations/sk.po +44 -4
  53. package/lang/translations/sl.po +44 -4
  54. package/lang/translations/sq.po +44 -4
  55. package/lang/translations/sr-latn.po +44 -4
  56. package/lang/translations/sr.po +44 -4
  57. package/lang/translations/sv.po +44 -4
  58. package/lang/translations/th.po +44 -4
  59. package/lang/translations/tk.po +44 -4
  60. package/lang/translations/tr.po +44 -4
  61. package/lang/translations/tt.po +44 -4
  62. package/lang/translations/ug.po +44 -4
  63. package/lang/translations/uk.po +44 -4
  64. package/lang/translations/ur.po +44 -4
  65. package/lang/translations/uz.po +44 -4
  66. package/lang/translations/vi.po +44 -4
  67. package/lang/translations/zh-cn.po +44 -4
  68. package/lang/translations/zh.po +44 -4
  69. package/package.json +3 -4
  70. package/src/button/button.d.ts +6 -0
  71. package/src/button/buttonview.d.ts +4 -0
  72. package/src/button/buttonview.js +1 -0
  73. package/src/button/filedialogbuttonview.d.ts +80 -0
  74. package/src/button/filedialogbuttonview.js +103 -0
  75. package/src/dropdown/utils.js +1 -5
  76. package/src/editorui/accessibilityhelp/accessibilityhelp.d.ts +4 -0
  77. package/src/editorui/accessibilityhelp/accessibilityhelp.js +25 -9
  78. package/src/index.d.ts +12 -3
  79. package/src/index.js +10 -2
  80. package/src/menubar/menubarmenubuttonview.d.ts +35 -0
  81. package/src/menubar/menubarmenubuttonview.js +64 -0
  82. package/src/menubar/menubarmenulistitembuttonview.d.ts +21 -0
  83. package/src/menubar/menubarmenulistitembuttonview.js +30 -0
  84. package/src/menubar/menubarmenulistitemfiledialogbuttonview.d.ts +23 -0
  85. package/src/menubar/menubarmenulistitemfiledialogbuttonview.js +32 -0
  86. package/src/menubar/menubarmenulistitemview.d.ts +25 -0
  87. package/src/menubar/menubarmenulistitemview.js +34 -0
  88. package/src/menubar/menubarmenulistview.d.ts +24 -0
  89. package/src/menubar/menubarmenulistview.js +23 -0
  90. package/src/menubar/menubarmenupanelview.d.ts +53 -0
  91. package/src/menubar/menubarmenupanelview.js +60 -0
  92. package/src/menubar/menubarmenuview.d.ts +109 -0
  93. package/src/menubar/menubarmenuview.js +159 -0
  94. package/src/menubar/menubarview.d.ts +164 -0
  95. package/src/menubar/menubarview.js +254 -0
  96. package/src/menubar/utils.d.ts +432 -0
  97. package/src/menubar/utils.js +1320 -0
  98. package/src/toolbar/toolbarview.js +5 -3
  99. package/src/tooltipmanager.d.ts +11 -0
  100. package/src/tooltipmanager.js +37 -6
  101. package/theme/components/menubar/menubar.css +10 -0
  102. package/theme/components/menubar/menubarmenu.css +9 -0
  103. package/theme/components/menubar/menubarmenubutton.css +11 -0
  104. package/theme/components/menubar/menubarmenulistitem.css +10 -0
  105. package/theme/components/menubar/menubarmenulistitembutton.css +10 -0
  106. package/theme/components/menubar/menubarmenupanel.css +62 -0
  107. package/theme/components/tooltip/tooltip.css +0 -3
  108. package/theme/globals/_reset.css +13 -0
  109. package/theme/globals/globals.css +1 -0
  110. package/dist/content-index.css +0 -4
  111. package/dist/editor-index.css +0 -445
  112. package/dist/index.css +0 -844
  113. package/dist/index.css.map +0 -1
  114. package/dist/translations/ar.d.ts +0 -8
  115. package/dist/translations/ar.js +0 -5
  116. package/dist/translations/ast.d.ts +0 -8
  117. package/dist/translations/ast.js +0 -5
  118. package/dist/translations/az.d.ts +0 -8
  119. package/dist/translations/az.js +0 -5
  120. package/dist/translations/bg.d.ts +0 -8
  121. package/dist/translations/bg.js +0 -5
  122. package/dist/translations/bn.d.ts +0 -8
  123. package/dist/translations/bn.js +0 -5
  124. package/dist/translations/ca.d.ts +0 -8
  125. package/dist/translations/ca.js +0 -5
  126. package/dist/translations/cs.d.ts +0 -8
  127. package/dist/translations/cs.js +0 -5
  128. package/dist/translations/da.d.ts +0 -8
  129. package/dist/translations/da.js +0 -5
  130. package/dist/translations/de-ch.d.ts +0 -8
  131. package/dist/translations/de-ch.js +0 -5
  132. package/dist/translations/de.d.ts +0 -8
  133. package/dist/translations/de.js +0 -5
  134. package/dist/translations/el.d.ts +0 -8
  135. package/dist/translations/el.js +0 -5
  136. package/dist/translations/en-au.d.ts +0 -8
  137. package/dist/translations/en-au.js +0 -5
  138. package/dist/translations/en-gb.d.ts +0 -8
  139. package/dist/translations/en-gb.js +0 -5
  140. package/dist/translations/en.d.ts +0 -8
  141. package/dist/translations/en.js +0 -5
  142. package/dist/translations/eo.d.ts +0 -8
  143. package/dist/translations/eo.js +0 -5
  144. package/dist/translations/es.d.ts +0 -8
  145. package/dist/translations/es.js +0 -5
  146. package/dist/translations/et.d.ts +0 -8
  147. package/dist/translations/et.js +0 -5
  148. package/dist/translations/eu.d.ts +0 -8
  149. package/dist/translations/eu.js +0 -5
  150. package/dist/translations/fa.d.ts +0 -8
  151. package/dist/translations/fa.js +0 -5
  152. package/dist/translations/fi.d.ts +0 -8
  153. package/dist/translations/fi.js +0 -5
  154. package/dist/translations/fr.d.ts +0 -8
  155. package/dist/translations/fr.js +0 -5
  156. package/dist/translations/gl.d.ts +0 -8
  157. package/dist/translations/gl.js +0 -5
  158. package/dist/translations/he.d.ts +0 -8
  159. package/dist/translations/he.js +0 -5
  160. package/dist/translations/hi.d.ts +0 -8
  161. package/dist/translations/hi.js +0 -5
  162. package/dist/translations/hr.d.ts +0 -8
  163. package/dist/translations/hr.js +0 -5
  164. package/dist/translations/hu.d.ts +0 -8
  165. package/dist/translations/hu.js +0 -5
  166. package/dist/translations/id.d.ts +0 -8
  167. package/dist/translations/id.js +0 -5
  168. package/dist/translations/it.d.ts +0 -8
  169. package/dist/translations/it.js +0 -5
  170. package/dist/translations/ja.d.ts +0 -8
  171. package/dist/translations/ja.js +0 -5
  172. package/dist/translations/km.d.ts +0 -8
  173. package/dist/translations/km.js +0 -5
  174. package/dist/translations/kn.d.ts +0 -8
  175. package/dist/translations/kn.js +0 -5
  176. package/dist/translations/ko.d.ts +0 -8
  177. package/dist/translations/ko.js +0 -5
  178. package/dist/translations/ku.d.ts +0 -8
  179. package/dist/translations/ku.js +0 -5
  180. package/dist/translations/lt.d.ts +0 -8
  181. package/dist/translations/lt.js +0 -5
  182. package/dist/translations/lv.d.ts +0 -8
  183. package/dist/translations/lv.js +0 -5
  184. package/dist/translations/ms.d.ts +0 -8
  185. package/dist/translations/ms.js +0 -5
  186. package/dist/translations/nb.d.ts +0 -8
  187. package/dist/translations/nb.js +0 -5
  188. package/dist/translations/ne.d.ts +0 -8
  189. package/dist/translations/ne.js +0 -5
  190. package/dist/translations/nl.d.ts +0 -8
  191. package/dist/translations/nl.js +0 -5
  192. package/dist/translations/no.d.ts +0 -8
  193. package/dist/translations/no.js +0 -5
  194. package/dist/translations/pl.d.ts +0 -8
  195. package/dist/translations/pl.js +0 -5
  196. package/dist/translations/pt-br.d.ts +0 -8
  197. package/dist/translations/pt-br.js +0 -5
  198. package/dist/translations/pt.d.ts +0 -8
  199. package/dist/translations/pt.js +0 -5
  200. package/dist/translations/ro.d.ts +0 -8
  201. package/dist/translations/ro.js +0 -5
  202. package/dist/translations/ru.d.ts +0 -8
  203. package/dist/translations/ru.js +0 -5
  204. package/dist/translations/sk.d.ts +0 -8
  205. package/dist/translations/sk.js +0 -5
  206. package/dist/translations/sl.d.ts +0 -8
  207. package/dist/translations/sl.js +0 -5
  208. package/dist/translations/sq.d.ts +0 -8
  209. package/dist/translations/sq.js +0 -5
  210. package/dist/translations/sr-latn.d.ts +0 -8
  211. package/dist/translations/sr-latn.js +0 -5
  212. package/dist/translations/sr.d.ts +0 -8
  213. package/dist/translations/sr.js +0 -5
  214. package/dist/translations/sv.d.ts +0 -8
  215. package/dist/translations/sv.js +0 -5
  216. package/dist/translations/th.d.ts +0 -8
  217. package/dist/translations/th.js +0 -5
  218. package/dist/translations/tk.d.ts +0 -8
  219. package/dist/translations/tk.js +0 -5
  220. package/dist/translations/tr.d.ts +0 -8
  221. package/dist/translations/tr.js +0 -5
  222. package/dist/translations/tt.d.ts +0 -8
  223. package/dist/translations/tt.js +0 -5
  224. package/dist/translations/ug.d.ts +0 -8
  225. package/dist/translations/ug.js +0 -5
  226. package/dist/translations/uk.d.ts +0 -8
  227. package/dist/translations/uk.js +0 -5
  228. package/dist/translations/ur.d.ts +0 -8
  229. package/dist/translations/ur.js +0 -5
  230. package/dist/translations/uz.d.ts +0 -8
  231. package/dist/translations/uz.js +0 -5
  232. package/dist/translations/vi.d.ts +0 -8
  233. package/dist/translations/vi.js +0 -5
  234. package/dist/translations/zh-cn.d.ts +0 -8
  235. package/dist/translations/zh-cn.js +0 -5
  236. package/dist/translations/zh.d.ts +0 -8
  237. package/dist/translations/zh.js +0 -5
  238. package/dist/types/arialiveannouncer.d.ts +0 -102
  239. package/dist/types/augmentation.d.ts +0 -92
  240. package/dist/types/autocomplete/autocompleteview.d.ts +0 -85
  241. package/dist/types/bindings/addkeyboardhandlingforgrid.d.ts +0 -31
  242. package/dist/types/bindings/clickoutsidehandler.d.ts +0 -32
  243. package/dist/types/bindings/csstransitiondisablermixin.d.ts +0 -44
  244. package/dist/types/bindings/draggableviewmixin.d.ts +0 -50
  245. package/dist/types/bindings/injectcsstransitiondisabler.d.ts +0 -63
  246. package/dist/types/bindings/preventdefault.d.ts +0 -37
  247. package/dist/types/bindings/submithandler.d.ts +0 -61
  248. package/dist/types/button/button.d.ts +0 -179
  249. package/dist/types/button/buttonlabel.d.ts +0 -38
  250. package/dist/types/button/buttonlabelview.d.ts +0 -35
  251. package/dist/types/button/buttonview.d.ts +0 -185
  252. package/dist/types/button/switchbuttonview.d.ts +0 -49
  253. package/dist/types/collapsible/collapsibleview.d.ts +0 -74
  254. package/dist/types/colorgrid/colorgridview.d.ts +0 -136
  255. package/dist/types/colorgrid/colortileview.d.ts +0 -32
  256. package/dist/types/colorgrid/utils.d.ts +0 -51
  257. package/dist/types/colorpicker/colorpickerview.d.ts +0 -146
  258. package/dist/types/colorpicker/utils.d.ts +0 -52
  259. package/dist/types/colorselector/colorgridsfragmentview.d.ts +0 -199
  260. package/dist/types/colorselector/colorpickerfragmentview.d.ts +0 -133
  261. package/dist/types/colorselector/colorselectorview.d.ts +0 -246
  262. package/dist/types/colorselector/documentcolorcollection.d.ts +0 -74
  263. package/dist/types/componentfactory.d.ts +0 -85
  264. package/dist/types/dialog/dialog.d.ts +0 -277
  265. package/dist/types/dialog/dialogactionsview.d.ts +0 -73
  266. package/dist/types/dialog/dialogcontentview.d.ts +0 -31
  267. package/dist/types/dialog/dialogview.d.ts +0 -260
  268. package/dist/types/dropdown/button/dropdownbutton.d.ts +0 -29
  269. package/dist/types/dropdown/button/dropdownbuttonview.d.ts +0 -52
  270. package/dist/types/dropdown/button/splitbuttonview.d.ts +0 -166
  271. package/dist/types/dropdown/dropdownpanelfocusable.d.ts +0 -25
  272. package/dist/types/dropdown/dropdownpanelview.d.ts +0 -66
  273. package/dist/types/dropdown/dropdownview.d.ts +0 -319
  274. package/dist/types/dropdown/utils.d.ts +0 -239
  275. package/dist/types/editableui/editableuiview.d.ts +0 -76
  276. package/dist/types/editableui/inline/inlineeditableuiview.d.ts +0 -44
  277. package/dist/types/editorui/accessibilityhelp/accessibilityhelp.d.ts +0 -51
  278. package/dist/types/editorui/accessibilityhelp/accessibilityhelpcontentview.d.ts +0 -39
  279. package/dist/types/editorui/bodycollection.d.ts +0 -59
  280. package/dist/types/editorui/boxed/boxededitoruiview.d.ts +0 -44
  281. package/dist/types/editorui/editorui.d.ts +0 -292
  282. package/dist/types/editorui/editoruiview.d.ts +0 -43
  283. package/dist/types/editorui/poweredby.d.ts +0 -75
  284. package/dist/types/focuscycler.d.ts +0 -249
  285. package/dist/types/formheader/formheaderview.d.ts +0 -63
  286. package/dist/types/highlightedtext/highlightedtextview.d.ts +0 -42
  287. package/dist/types/icon/iconview.d.ts +0 -92
  288. package/dist/types/iframe/iframeview.d.ts +0 -54
  289. package/dist/types/index.d.ts +0 -78
  290. package/dist/types/input/inputbase.d.ts +0 -123
  291. package/dist/types/input/inputview.d.ts +0 -40
  292. package/dist/types/inputnumber/inputnumberview.d.ts +0 -53
  293. package/dist/types/inputtext/inputtextview.d.ts +0 -22
  294. package/dist/types/label/labelview.d.ts +0 -40
  295. package/dist/types/labeledfield/labeledfieldview.d.ts +0 -191
  296. package/dist/types/labeledfield/utils.d.ts +0 -127
  297. package/dist/types/labeledinput/labeledinputview.d.ts +0 -129
  298. package/dist/types/list/listitemgroupview.d.ts +0 -63
  299. package/dist/types/list/listitemview.d.ts +0 -40
  300. package/dist/types/list/listseparatorview.d.ts +0 -22
  301. package/dist/types/list/listview.d.ts +0 -128
  302. package/dist/types/model.d.ts +0 -26
  303. package/dist/types/notification/notification.d.ts +0 -215
  304. package/dist/types/panel/balloon/balloonpanelview.d.ts +0 -689
  305. package/dist/types/panel/balloon/contextualballoon.d.ts +0 -303
  306. package/dist/types/panel/sticky/stickypanelview.d.ts +0 -160
  307. package/dist/types/search/filteredview.d.ts +0 -35
  308. package/dist/types/search/searchinfoview.d.ts +0 -49
  309. package/dist/types/search/searchresultsview.d.ts +0 -58
  310. package/dist/types/search/text/searchtextqueryview.d.ts +0 -80
  311. package/dist/types/search/text/searchtextview.d.ts +0 -223
  312. package/dist/types/spinner/spinnerview.d.ts +0 -29
  313. package/dist/types/template.d.ts +0 -946
  314. package/dist/types/textarea/textareaview.d.ts +0 -108
  315. package/dist/types/toolbar/balloon/balloontoolbar.d.ts +0 -121
  316. package/dist/types/toolbar/block/blockbuttonview.d.ts +0 -39
  317. package/dist/types/toolbar/block/blocktoolbar.d.ts +0 -157
  318. package/dist/types/toolbar/normalizetoolbarconfig.d.ts +0 -44
  319. package/dist/types/toolbar/toolbarlinebreakview.d.ts +0 -22
  320. package/dist/types/toolbar/toolbarseparatorview.d.ts +0 -22
  321. package/dist/types/toolbar/toolbarview.d.ts +0 -271
  322. package/dist/types/tooltipmanager.d.ts +0 -188
  323. package/dist/types/view.d.ts +0 -426
  324. package/dist/types/viewcollection.d.ts +0 -143
@@ -0,0 +1,109 @@
1
+ /**
2
+ * @license 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
+ * @module ui/menubar/menubarmenuview
7
+ */
8
+ import { FocusTracker, KeystrokeHandler, type Locale, type PositioningFunction } from '@ckeditor/ckeditor5-utils';
9
+ import MenuBarMenuButtonView from './menubarmenubuttonview.js';
10
+ import type { FocusableView } from '../focuscycler.js';
11
+ import View from '../view.js';
12
+ import { default as MenuBarMenuPanelView, type MenuBarMenuPanelPosition } from './menubarmenupanelview.js';
13
+ import '../../theme/components/menubar/menubarmenu.css';
14
+ /**
15
+ * A menu view for the {@link module:ui/menubar/menubarview~MenuBarView}. Menus are building blocks of the menu bar,
16
+ * they host other sub-menus and menu items (buttons) that users can interact with.
17
+ */
18
+ export default class MenuBarMenuView extends View implements FocusableView {
19
+ /**
20
+ * Button of the menu view.
21
+ */
22
+ readonly buttonView: MenuBarMenuButtonView;
23
+ /**
24
+ * Panel of the menu. It hosts children of the menu.
25
+ */
26
+ readonly panelView: MenuBarMenuPanelView;
27
+ /**
28
+ * Tracks information about the DOM focus in the menu.
29
+ */
30
+ readonly focusTracker: FocusTracker;
31
+ /**
32
+ * Instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}. It manages
33
+ * keystrokes of the menu.
34
+ */
35
+ readonly keystrokes: KeystrokeHandler;
36
+ /**
37
+ * Controls whether the menu is open, i.e. shows or hides the {@link #panelView panel}.
38
+ *
39
+ * @observable
40
+ */
41
+ isOpen: boolean;
42
+ /**
43
+ * Controls whether the menu is enabled, i.e. its {@link #buttonView} can be clicked.
44
+ *
45
+ * @observable
46
+ */
47
+ isEnabled: boolean;
48
+ /**
49
+ * (Optional) The additional CSS class set on the menu {@link #element}.
50
+ *
51
+ * @observable
52
+ */
53
+ class: string | undefined;
54
+ /**
55
+ * The name of the position of the {@link #panelView}, relative to the menu.
56
+ *
57
+ * **Note**: The value is updated each time the panel gets {@link #isOpen open}.
58
+ *
59
+ * @observable
60
+ * @default 'w'
61
+ */
62
+ panelPosition: MenuBarMenuPanelPosition;
63
+ /**
64
+ * The parent menu view of the menu. It is `null` for top-level menus.
65
+ *
66
+ * See {@link module:ui/menubar/menubarview~MenuBarView#registerMenu}.
67
+ */
68
+ parentMenuView: MenuBarMenuView | null;
69
+ /**
70
+ * Creates an instance of the menu view.
71
+ *
72
+ * @param locale The localization services instance.
73
+ */
74
+ constructor(locale: Locale);
75
+ /**
76
+ * @inheritDoc
77
+ */
78
+ render(): void;
79
+ /**
80
+ * Attach all keyboard behaviors for the menu bar view.
81
+ *
82
+ * @internal
83
+ */
84
+ _attachBehaviors(): void;
85
+ /**
86
+ * Fires `arrowright` and `arrowleft` events when the user pressed corresponding arrow keys.
87
+ */
88
+ private _propagateArrowKeystrokeEvents;
89
+ /**
90
+ * Sets the position of the panel when the menu opens. The panel is positioned
91
+ * so that it optimally uses the available space in the viewport.
92
+ */
93
+ private _repositionPanelOnOpen;
94
+ /**
95
+ * @inheritDoc
96
+ */
97
+ focus(): void;
98
+ /**
99
+ * Positioning functions for the {@link #panelView} . They change depending on the role of the menu (top-level vs sub-menu) in
100
+ * the {@link module:ui/menubar/menubarview~MenuBarView menu bar} and the UI language direction.
101
+ */
102
+ get _panelPositions(): Array<PositioningFunction>;
103
+ /**
104
+ * A function used to calculate the optimal position for the dropdown panel.
105
+ *
106
+ * Referenced for unit testing purposes.
107
+ */
108
+ private static _getOptimalPosition;
109
+ }
@@ -0,0 +1,159 @@
1
+ /**
2
+ * @license 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
+ * @module ui/menubar/menubarmenuview
7
+ */
8
+ import { FocusTracker, KeystrokeHandler, getOptimalPosition } from '@ckeditor/ckeditor5-utils';
9
+ import MenuBarMenuButtonView from './menubarmenubuttonview.js';
10
+ import { MenuBarMenuBehaviors, MenuBarMenuViewPanelPositioningFunctions } from './utils.js';
11
+ import View from '../view.js';
12
+ import { default as MenuBarMenuPanelView } from './menubarmenupanelview.js';
13
+ import '../../theme/components/menubar/menubarmenu.css';
14
+ /**
15
+ * A menu view for the {@link module:ui/menubar/menubarview~MenuBarView}. Menus are building blocks of the menu bar,
16
+ * they host other sub-menus and menu items (buttons) that users can interact with.
17
+ */
18
+ class MenuBarMenuView extends View {
19
+ /**
20
+ * Creates an instance of the menu view.
21
+ *
22
+ * @param locale The localization services instance.
23
+ */
24
+ constructor(locale) {
25
+ super(locale);
26
+ const bind = this.bindTemplate;
27
+ this.buttonView = new MenuBarMenuButtonView(locale);
28
+ this.buttonView.delegate('mouseenter').to(this);
29
+ this.buttonView.bind('isOn', 'isEnabled').to(this, 'isOpen', 'isEnabled');
30
+ this.panelView = new MenuBarMenuPanelView(locale);
31
+ this.panelView.bind('isVisible').to(this, 'isOpen');
32
+ this.keystrokes = new KeystrokeHandler();
33
+ this.focusTracker = new FocusTracker();
34
+ this.set('isOpen', false);
35
+ this.set('isEnabled', true);
36
+ this.set('panelPosition', 'w');
37
+ this.set('class', undefined);
38
+ this.set('parentMenuView', null);
39
+ this.setTemplate({
40
+ tag: 'div',
41
+ attributes: {
42
+ class: [
43
+ 'ck',
44
+ 'ck-menu-bar__menu',
45
+ bind.to('class'),
46
+ bind.if('isEnabled', 'ck-disabled', value => !value),
47
+ bind.if('parentMenuView', 'ck-menu-bar__menu_top-level', value => !value)
48
+ ]
49
+ },
50
+ children: [
51
+ this.buttonView,
52
+ this.panelView
53
+ ]
54
+ });
55
+ }
56
+ /**
57
+ * @inheritDoc
58
+ */
59
+ render() {
60
+ super.render();
61
+ this.focusTracker.add(this.buttonView.element);
62
+ this.focusTracker.add(this.panelView.element);
63
+ // Listen for keystrokes coming from within #element.
64
+ this.keystrokes.listenTo(this.element);
65
+ MenuBarMenuBehaviors.closeOnEscKey(this);
66
+ this._repositionPanelOnOpen();
67
+ }
68
+ // For now, this method cannot be called in the render process because the `parentMenuView` may be assigned
69
+ // after the rendering process.
70
+ //
71
+ // TODO: We should reconsider the way we handle this logic.
72
+ /**
73
+ * Attach all keyboard behaviors for the menu bar view.
74
+ *
75
+ * @internal
76
+ */
77
+ _attachBehaviors() {
78
+ // Top-level menus.
79
+ if (!this.parentMenuView) {
80
+ this._propagateArrowKeystrokeEvents();
81
+ MenuBarMenuBehaviors.openAndFocusPanelOnArrowDownKey(this);
82
+ MenuBarMenuBehaviors.toggleOnButtonClick(this);
83
+ }
84
+ else {
85
+ MenuBarMenuBehaviors.openOnButtonClick(this);
86
+ MenuBarMenuBehaviors.openOnArrowRightKey(this);
87
+ MenuBarMenuBehaviors.closeOnArrowLeftKey(this);
88
+ MenuBarMenuBehaviors.closeOnParentClose(this);
89
+ }
90
+ }
91
+ /**
92
+ * Fires `arrowright` and `arrowleft` events when the user pressed corresponding arrow keys.
93
+ */
94
+ _propagateArrowKeystrokeEvents() {
95
+ this.keystrokes.set('arrowright', (data, cancel) => {
96
+ this.fire('arrowright');
97
+ cancel();
98
+ });
99
+ this.keystrokes.set('arrowleft', (data, cancel) => {
100
+ this.fire('arrowleft');
101
+ cancel();
102
+ });
103
+ }
104
+ /**
105
+ * Sets the position of the panel when the menu opens. The panel is positioned
106
+ * so that it optimally uses the available space in the viewport.
107
+ */
108
+ _repositionPanelOnOpen() {
109
+ // Let the menu control the position of the panel. The position must be updated every time the menu is open.
110
+ this.on('change:isOpen', (evt, name, isOpen) => {
111
+ if (!isOpen) {
112
+ return;
113
+ }
114
+ const optimalPanelPosition = MenuBarMenuView._getOptimalPosition({
115
+ element: this.panelView.element,
116
+ target: this.buttonView.element,
117
+ fitInViewport: true,
118
+ positions: this._panelPositions
119
+ });
120
+ this.panelView.position = (optimalPanelPosition ? optimalPanelPosition.name : this._panelPositions[0].name);
121
+ });
122
+ }
123
+ /**
124
+ * @inheritDoc
125
+ */
126
+ focus() {
127
+ this.buttonView.focus();
128
+ }
129
+ /**
130
+ * Positioning functions for the {@link #panelView} . They change depending on the role of the menu (top-level vs sub-menu) in
131
+ * the {@link module:ui/menubar/menubarview~MenuBarView menu bar} and the UI language direction.
132
+ */
133
+ get _panelPositions() {
134
+ const { southEast, southWest, northEast, northWest, westSouth, eastSouth, westNorth, eastNorth } = MenuBarMenuViewPanelPositioningFunctions;
135
+ if (this.locale.uiLanguageDirection === 'ltr') {
136
+ if (this.parentMenuView) {
137
+ return [eastSouth, eastNorth, westSouth, westNorth];
138
+ }
139
+ else {
140
+ return [southEast, southWest, northEast, northWest];
141
+ }
142
+ }
143
+ else {
144
+ if (this.parentMenuView) {
145
+ return [westSouth, westNorth, eastSouth, eastNorth];
146
+ }
147
+ else {
148
+ return [southWest, southEast, northWest, northEast];
149
+ }
150
+ }
151
+ }
152
+ }
153
+ /**
154
+ * A function used to calculate the optimal position for the dropdown panel.
155
+ *
156
+ * Referenced for unit testing purposes.
157
+ */
158
+ MenuBarMenuView._getOptimalPosition = getOptimalPosition;
159
+ export default MenuBarMenuView;
@@ -0,0 +1,164 @@
1
+ /**
2
+ * @license 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
+ * @module ui/menubar/menubarview
7
+ */
8
+ import { type BaseEvent, type Locale } from '@ckeditor/ckeditor5-utils';
9
+ import { type FocusableView } from '../focuscycler.js';
10
+ import View from '../view.js';
11
+ import type ViewCollection from '../viewcollection.js';
12
+ import type ComponentFactory from '../componentfactory.js';
13
+ import MenuBarMenuView from './menubarmenuview.js';
14
+ import '../../theme/components/menubar/menubar.css';
15
+ /**
16
+ * The application menu bar component. It brings a set of top-level menus (and sub-menus) that can be used
17
+ * to organize and access a large number of buttons.
18
+ */
19
+ export default class MenuBarView extends View implements FocusableView {
20
+ /**
21
+ * Collection of the child views inside the {@link #element}.
22
+ */
23
+ children: ViewCollection<MenuBarMenuView>;
24
+ /**
25
+ * Indicates whether any of top-level menus are open in the menu bar. To close
26
+ * the menu bar use the {@link #close} method.
27
+ *
28
+ * @observable
29
+ */
30
+ isOpen: boolean;
31
+ /**
32
+ * A list of {@link module:ui/menubar/menubarmenuview~MenuBarMenuView} instances registered in the menu bar.
33
+ *
34
+ * @observable
35
+ */
36
+ menus: Array<MenuBarMenuView>;
37
+ /**
38
+ * Creates an instance of the menu bar view.
39
+ *
40
+ * @param locale The localization services instance.
41
+ */
42
+ constructor(locale: Locale);
43
+ /**
44
+ * A utility that expands a plain menu bar configuration into a structure of menus (also: sub-menus)
45
+ * and items using a given {@link module:ui/componentfactory~ComponentFactory component factory}.
46
+ *
47
+ * See the {@link module:core/editor/editorconfig~EditorConfig#menuBar menu bar} in the editor
48
+ * configuration reference to learn how to configure the menu bar.
49
+ */
50
+ fillFromConfig(config: NormalizedMenuBarConfigObject, componentFactory: ComponentFactory): void;
51
+ /**
52
+ * @inheritDoc
53
+ */
54
+ render(): void;
55
+ /**
56
+ * Focuses the menu bar.
57
+ */
58
+ focus(): void;
59
+ /**
60
+ * Closes all menus in the bar.
61
+ */
62
+ close(): void;
63
+ /**
64
+ * Registers a menu view in the menu bar. Every {@link module:ui/menubar/menubarmenuview~MenuBarMenuView} instance must be registered
65
+ * in the menu bar to be properly managed.
66
+ */
67
+ registerMenu(menuView: MenuBarMenuView, parentMenuView?: MenuBarMenuView | null): void;
68
+ /**
69
+ * Creates a {@link module:ui/menubar/menubarmenuview~MenuBarMenuView} based on the given definition.
70
+ */
71
+ private _createMenu;
72
+ /**
73
+ * Creates a {@link module:ui/menubar/menubarmenuview~MenuBarMenuView} items based on the given definition.
74
+ */
75
+ private _createMenuItems;
76
+ /**
77
+ * Uses the component factory to create a content of the menu item (a button or a sub-menu).
78
+ */
79
+ private _createMenuItemContentFromFactory;
80
+ /**
81
+ * Checks component and its children recursively and calls {@link #registerMenu}
82
+ * for each item that is {@link module:ui/menubar/menubarmenuview~MenuBarMenuView}.
83
+ *
84
+ * @internal
85
+ */
86
+ private _registerMenuTree;
87
+ /**
88
+ * Manages the state of the {@link #isOpen} property of the menu bar. Because the state is a sum of individual
89
+ * top-level menus' states, it's necessary to listen to their changes and update the state accordingly.
90
+ *
91
+ * Additionally, it prevents from unnecessary changes of `isOpen` when one top-level menu opens and another closes
92
+ * (regardless of in which order), maintaining a stable `isOpen === true` in that situation.
93
+ */
94
+ private _setupIsOpenUpdater;
95
+ }
96
+ export type MenuBarConfig = MenuBarConfigObject;
97
+ export type MenuBarConfigObject = {
98
+ items?: Array<MenuBarMenuDefinition>;
99
+ removeItems?: Array<string>;
100
+ addItems?: Array<MenuBarConfigAddedItem | MenuBarConfigAddedGroup | MenuBarConfigAddedMenu>;
101
+ isVisible?: boolean;
102
+ };
103
+ export type NormalizedMenuBarConfigObject = Required<MenuBarConfigObject> & {
104
+ isUsingDefaultConfig: boolean;
105
+ };
106
+ export type MenuBarMenuGroupDefinition = {
107
+ groupId: string;
108
+ items: Array<MenuBarMenuDefinition | string>;
109
+ };
110
+ export type MenuBarMenuDefinition = {
111
+ menuId: string;
112
+ label: string;
113
+ groups: Array<MenuBarMenuGroupDefinition>;
114
+ };
115
+ export type MenuBarConfigAddedPosition = `start:${string}` | `end:${string}` | 'start' | 'end' | `after:${string}` | `before:${string}`;
116
+ export type MenuBarConfigAddedItem = {
117
+ item: string;
118
+ position: MenuBarConfigAddedPosition;
119
+ };
120
+ export type MenuBarConfigAddedGroup = {
121
+ group: MenuBarMenuGroupDefinition;
122
+ position: MenuBarConfigAddedPosition;
123
+ };
124
+ export type MenuBarConfigAddedMenu = {
125
+ menu: MenuBarMenuDefinition;
126
+ position: MenuBarConfigAddedPosition;
127
+ };
128
+ /**
129
+ * Any namespaced event fired by menu a {@link module:ui/menubar/menubarview~MenuBarView#menus menu view instance} of the
130
+ * {@link module:ui/menubar/menubarview~MenuBarView menu bar}.
131
+ */
132
+ interface MenuBarMenuEvent extends BaseEvent {
133
+ name: `menu:${string}` | `menu:change:${string}`;
134
+ }
135
+ /**
136
+ * A `mouseenter` event originating from a {@link module:ui/menubar/menubarview~MenuBarView#menus menu view instance} of the
137
+ * {@link module:ui/menubar/menubarview~MenuBarView menu bar}.
138
+ */
139
+ export interface MenuBarMenuMouseEnterEvent extends MenuBarMenuEvent {
140
+ name: 'menu:mouseenter';
141
+ }
142
+ /**
143
+ * An `arrowleft` event originating from a {@link module:ui/menubar/menubarview~MenuBarView#menus menu view instance} of the
144
+ * {@link module:ui/menubar/menubarview~MenuBarView menu bar}.
145
+ */
146
+ export interface MenuBarMenuArrowLeftEvent extends MenuBarMenuEvent {
147
+ name: 'menu:arrowleft';
148
+ }
149
+ /**
150
+ * An `arrowright` event originating from a {@link module:ui/menubar/menubarview~MenuBarView#menus menu view instance} of the
151
+ * {@link module:ui/menubar/menubarview~MenuBarView menu bar}.
152
+ */
153
+ export interface MenuBarMenuArrowRightEvent extends MenuBarMenuEvent {
154
+ name: 'menu:arrowright';
155
+ }
156
+ /**
157
+ * A `change:isOpen` event originating from a {@link module:ui/menubar/menubarview~MenuBarView#menus menu view instance} of the
158
+ * {@link module:ui/menubar/menubarview~MenuBarView menu bar}.
159
+ */
160
+ export interface MenuBarMenuChangeIsOpenEvent extends MenuBarMenuEvent {
161
+ name: 'menu:change:isOpen';
162
+ args: [name: string, value: boolean, oldValue: boolean];
163
+ }
164
+ export {};
@@ -0,0 +1,254 @@
1
+ /**
2
+ * @license 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
+ * @module ui/menubar/menubarview
7
+ */
8
+ import { logWarning } from '@ckeditor/ckeditor5-utils';
9
+ import View from '../view.js';
10
+ import { isObject } from 'lodash-es';
11
+ import ListItemView from '../list/listitemview.js';
12
+ import ListSeparatorView from '../list/listseparatorview.js';
13
+ import MenuBarMenuView from './menubarmenuview.js';
14
+ import MenuBarMenuListView from './menubarmenulistview.js';
15
+ import MenuBarMenuListItemView from './menubarmenulistitemview.js';
16
+ import MenuBarMenuListItemButtonView from './menubarmenulistitembuttonview.js';
17
+ import MenuBarMenuListItemFileDialogButtonView from './menubarmenulistitemfiledialogbuttonview.js';
18
+ import { MenuBarBehaviors, processMenuBarConfig } from './utils.js';
19
+ const EVENT_NAME_DELEGATES = ['mouseenter', 'arrowleft', 'arrowright', 'change:isOpen'];
20
+ import '../../theme/components/menubar/menubar.css';
21
+ /**
22
+ * The application menu bar component. It brings a set of top-level menus (and sub-menus) that can be used
23
+ * to organize and access a large number of buttons.
24
+ */
25
+ export default class MenuBarView extends View {
26
+ /**
27
+ * Creates an instance of the menu bar view.
28
+ *
29
+ * @param locale The localization services instance.
30
+ */
31
+ constructor(locale) {
32
+ super(locale);
33
+ /**
34
+ * A list of {@link module:ui/menubar/menubarmenuview~MenuBarMenuView} instances registered in the menu bar.
35
+ *
36
+ * @observable
37
+ */
38
+ this.menus = [];
39
+ const t = locale.t;
40
+ this.set('isOpen', false);
41
+ this._setupIsOpenUpdater();
42
+ this.children = this.createCollection();
43
+ // @if CK_DEBUG_MENU_BAR // // Logs events in the main event bus of the component.
44
+ // @if CK_DEBUG_MENU_BAR // this.on( 'menu', ( evt, data ) => {
45
+ // @if CK_DEBUG_MENU_BAR // console.log( `MenuBarView:${ evt.name }`, evt.path.map( view => view.element ) );
46
+ // @if CK_DEBUG_MENU_BAR // } );
47
+ this.setTemplate({
48
+ tag: 'div',
49
+ attributes: {
50
+ class: [
51
+ 'ck',
52
+ 'ck-menu-bar'
53
+ ],
54
+ 'aria-label': t('Editor menu bar'),
55
+ role: 'menubar'
56
+ },
57
+ children: this.children
58
+ });
59
+ }
60
+ /**
61
+ * A utility that expands a plain menu bar configuration into a structure of menus (also: sub-menus)
62
+ * and items using a given {@link module:ui/componentfactory~ComponentFactory component factory}.
63
+ *
64
+ * See the {@link module:core/editor/editorconfig~EditorConfig#menuBar menu bar} in the editor
65
+ * configuration reference to learn how to configure the menu bar.
66
+ */
67
+ fillFromConfig(config, componentFactory) {
68
+ const locale = this.locale;
69
+ const processedConfig = processMenuBarConfig({
70
+ normalizedConfig: config,
71
+ locale,
72
+ componentFactory
73
+ });
74
+ const topLevelCategoryMenuViews = processedConfig.items.map(menuDefinition => this._createMenu({
75
+ componentFactory,
76
+ menuDefinition
77
+ }));
78
+ this.children.addMany(topLevelCategoryMenuViews);
79
+ }
80
+ /**
81
+ * @inheritDoc
82
+ */
83
+ render() {
84
+ super.render();
85
+ MenuBarBehaviors.toggleMenusAndFocusItemsOnHover(this);
86
+ MenuBarBehaviors.closeMenusWhenTheBarCloses(this);
87
+ MenuBarBehaviors.closeMenuWhenAnotherOnTheSameLevelOpens(this);
88
+ MenuBarBehaviors.focusCycleMenusOnArrows(this);
89
+ MenuBarBehaviors.closeOnClickOutside(this);
90
+ }
91
+ /**
92
+ * Focuses the menu bar.
93
+ */
94
+ focus() {
95
+ if (this.children.first) {
96
+ this.children.first.focus();
97
+ }
98
+ }
99
+ /**
100
+ * Closes all menus in the bar.
101
+ */
102
+ close() {
103
+ for (const topLevelCategoryMenuView of this.children) {
104
+ topLevelCategoryMenuView.isOpen = false;
105
+ }
106
+ }
107
+ /**
108
+ * Registers a menu view in the menu bar. Every {@link module:ui/menubar/menubarmenuview~MenuBarMenuView} instance must be registered
109
+ * in the menu bar to be properly managed.
110
+ */
111
+ registerMenu(menuView, parentMenuView = null) {
112
+ if (parentMenuView) {
113
+ menuView.delegate(...EVENT_NAME_DELEGATES).to(parentMenuView);
114
+ menuView.parentMenuView = parentMenuView;
115
+ }
116
+ else {
117
+ menuView.delegate(...EVENT_NAME_DELEGATES).to(this, name => 'menu:' + name);
118
+ }
119
+ menuView._attachBehaviors();
120
+ this.menus.push(menuView);
121
+ }
122
+ /**
123
+ * Creates a {@link module:ui/menubar/menubarmenuview~MenuBarMenuView} based on the given definition.
124
+ */
125
+ _createMenu({ componentFactory, menuDefinition, parentMenuView }) {
126
+ const locale = this.locale;
127
+ const menuView = new MenuBarMenuView(locale);
128
+ this.registerMenu(menuView, parentMenuView);
129
+ menuView.buttonView.set({
130
+ label: menuDefinition.label
131
+ });
132
+ // Defer the creation of the menu structure until it gets open. This is a performance optimization
133
+ // that shortens the time needed to create the editor.
134
+ menuView.once('change:isOpen', () => {
135
+ const listView = new MenuBarMenuListView(locale);
136
+ listView.ariaLabel = menuDefinition.label;
137
+ menuView.panelView.children.add(listView);
138
+ listView.items.addMany(this._createMenuItems({ menuDefinition, parentMenuView: menuView, componentFactory }));
139
+ });
140
+ return menuView;
141
+ }
142
+ /**
143
+ * Creates a {@link module:ui/menubar/menubarmenuview~MenuBarMenuView} items based on the given definition.
144
+ */
145
+ _createMenuItems({ menuDefinition, parentMenuView, componentFactory }) {
146
+ const locale = this.locale;
147
+ const items = [];
148
+ for (const menuGroupDefinition of menuDefinition.groups) {
149
+ for (const itemDefinition of menuGroupDefinition.items) {
150
+ const menuItemView = new MenuBarMenuListItemView(locale, parentMenuView);
151
+ if (isObject(itemDefinition)) {
152
+ menuItemView.children.add(this._createMenu({
153
+ componentFactory,
154
+ menuDefinition: itemDefinition,
155
+ parentMenuView
156
+ }));
157
+ }
158
+ else {
159
+ const componentView = this._createMenuItemContentFromFactory({
160
+ componentName: itemDefinition,
161
+ componentFactory,
162
+ parentMenuView
163
+ });
164
+ if (!componentView) {
165
+ continue;
166
+ }
167
+ menuItemView.children.add(componentView);
168
+ }
169
+ items.push(menuItemView);
170
+ }
171
+ // Separate groups with a separator.
172
+ if (menuGroupDefinition !== menuDefinition.groups[menuDefinition.groups.length - 1]) {
173
+ items.push(new ListSeparatorView(locale));
174
+ }
175
+ }
176
+ return items;
177
+ }
178
+ /**
179
+ * Uses the component factory to create a content of the menu item (a button or a sub-menu).
180
+ */
181
+ _createMenuItemContentFromFactory({ componentName, parentMenuView, componentFactory }) {
182
+ const componentView = componentFactory.create(componentName);
183
+ if (!(componentView instanceof MenuBarMenuView ||
184
+ componentView instanceof MenuBarMenuListItemButtonView ||
185
+ componentView instanceof MenuBarMenuListItemFileDialogButtonView)) {
186
+ /**
187
+ * Adding unsupported components to the {@link module:ui/menubar/menubarview~MenuBarView} is not possible.
188
+ *
189
+ * A component should be either a {@link module:ui/menubar/menubarmenuview~MenuBarMenuView} (sub-menu) or a
190
+ * {@link module:ui/menubar/menubarmenulistitembuttonview~MenuBarMenuListItemButtonView} (button).
191
+ *
192
+ * @error menu-bar-component-unsupported
193
+ * @param componentName A name of the unsupported component used in the configuration.
194
+ * @param componentView An unsupported component view.
195
+ */
196
+ logWarning('menu-bar-component-unsupported', {
197
+ componentName,
198
+ componentView
199
+ });
200
+ return null;
201
+ }
202
+ this._registerMenuTree(componentView, parentMenuView);
203
+ // Close the whole menu bar when a component is executed.
204
+ componentView.on('execute', () => {
205
+ this.close();
206
+ });
207
+ return componentView;
208
+ }
209
+ /**
210
+ * Checks component and its children recursively and calls {@link #registerMenu}
211
+ * for each item that is {@link module:ui/menubar/menubarmenuview~MenuBarMenuView}.
212
+ *
213
+ * @internal
214
+ */
215
+ _registerMenuTree(componentView, parentMenuView) {
216
+ if (!(componentView instanceof MenuBarMenuView)) {
217
+ componentView.delegate('mouseenter').to(parentMenuView);
218
+ return;
219
+ }
220
+ this.registerMenu(componentView, parentMenuView);
221
+ const menuBarItemsList = componentView.panelView.children
222
+ .filter(child => child instanceof MenuBarMenuListView)[0];
223
+ if (!menuBarItemsList) {
224
+ componentView.delegate('mouseenter').to(parentMenuView);
225
+ return;
226
+ }
227
+ const nonSeparatorItems = menuBarItemsList.items.filter(item => item instanceof ListItemView);
228
+ for (const item of nonSeparatorItems) {
229
+ this._registerMenuTree(item.children.get(0), componentView);
230
+ }
231
+ }
232
+ /**
233
+ * Manages the state of the {@link #isOpen} property of the menu bar. Because the state is a sum of individual
234
+ * top-level menus' states, it's necessary to listen to their changes and update the state accordingly.
235
+ *
236
+ * Additionally, it prevents from unnecessary changes of `isOpen` when one top-level menu opens and another closes
237
+ * (regardless of in which order), maintaining a stable `isOpen === true` in that situation.
238
+ */
239
+ _setupIsOpenUpdater() {
240
+ let closeTimeout;
241
+ // TODO: This is not the prettiest approach but at least it's simple.
242
+ this.on('menu:change:isOpen', (evt, name, isOpen) => {
243
+ clearTimeout(closeTimeout);
244
+ if (isOpen) {
245
+ this.isOpen = true;
246
+ }
247
+ else {
248
+ closeTimeout = setTimeout(() => {
249
+ this.isOpen = Array.from(this.children).some(menuView => menuView.isOpen);
250
+ }, 0);
251
+ }
252
+ });
253
+ }
254
+ }