@ckeditor/ckeditor5-ui 39.0.2 → 40.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +3 -3
- package/lang/contexts.json +5 -1
- package/lang/translations/ar.po +16 -0
- package/lang/translations/ast.po +16 -0
- package/lang/translations/az.po +16 -0
- package/lang/translations/bg.po +16 -0
- package/lang/translations/bn.po +16 -0
- package/lang/translations/ca.po +16 -0
- package/lang/translations/cs.po +16 -0
- package/lang/translations/da.po +16 -0
- package/lang/translations/de-ch.po +16 -0
- package/lang/translations/de.po +16 -0
- package/lang/translations/el.po +16 -0
- package/lang/translations/en-au.po +16 -0
- package/lang/translations/en-gb.po +16 -0
- package/lang/translations/en.po +16 -0
- package/lang/translations/eo.po +16 -0
- package/lang/translations/es.po +16 -0
- package/lang/translations/et.po +16 -0
- package/lang/translations/eu.po +16 -0
- package/lang/translations/fa.po +16 -0
- package/lang/translations/fi.po +16 -0
- package/lang/translations/fr.po +16 -0
- package/lang/translations/gl.po +16 -0
- package/lang/translations/he.po +16 -0
- package/lang/translations/hi.po +16 -0
- package/lang/translations/hr.po +16 -0
- package/lang/translations/hu.po +16 -0
- package/lang/translations/id.po +16 -0
- package/lang/translations/it.po +16 -0
- package/lang/translations/ja.po +16 -0
- package/lang/translations/km.po +16 -0
- package/lang/translations/kn.po +16 -0
- package/lang/translations/ko.po +16 -0
- package/lang/translations/ku.po +16 -0
- package/lang/translations/lt.po +16 -0
- package/lang/translations/lv.po +16 -0
- package/lang/translations/ms.po +16 -0
- package/lang/translations/nb.po +16 -0
- package/lang/translations/ne.po +16 -0
- package/lang/translations/nl.po +16 -0
- package/lang/translations/no.po +16 -0
- package/lang/translations/pl.po +16 -0
- package/lang/translations/pt-br.po +17 -1
- package/lang/translations/pt.po +16 -0
- package/lang/translations/ro.po +16 -0
- package/lang/translations/ru.po +16 -0
- package/lang/translations/sk.po +16 -0
- package/lang/translations/sl.po +16 -0
- package/lang/translations/sq.po +16 -0
- package/lang/translations/sr-latn.po +16 -0
- package/lang/translations/sr.po +16 -0
- package/lang/translations/sv.po +16 -0
- package/lang/translations/th.po +16 -0
- package/lang/translations/tk.po +16 -0
- package/lang/translations/tr.po +16 -0
- package/lang/translations/tt.po +16 -0
- package/lang/translations/ug.po +38 -22
- package/lang/translations/uk.po +16 -0
- package/lang/translations/ur.po +16 -0
- package/lang/translations/uz.po +16 -0
- package/lang/translations/vi.po +16 -0
- package/lang/translations/zh-cn.po +16 -0
- package/lang/translations/zh.po +16 -0
- package/package.json +3 -3
- package/src/arialiveannouncer.d.ts +94 -0
- package/src/arialiveannouncer.js +113 -0
- package/src/autocomplete/autocompleteview.d.ts +81 -0
- package/src/autocomplete/autocompleteview.js +153 -0
- package/src/button/button.d.ts +0 -6
- package/src/button/buttonlabel.d.ts +34 -0
- package/src/button/buttonlabel.js +5 -0
- package/src/button/buttonlabelview.d.ts +31 -0
- package/src/button/buttonlabelview.js +42 -0
- package/src/button/buttonview.d.ts +14 -10
- package/src/button/buttonview.js +11 -25
- package/src/dropdown/dropdownview.js +5 -4
- package/src/dropdown/utils.d.ts +15 -1
- package/src/dropdown/utils.js +47 -21
- package/src/editorui/editorui.d.ts +6 -0
- package/src/editorui/editorui.js +2 -0
- package/src/editorui/poweredby.js +14 -37
- package/src/focuscycler.d.ts +45 -2
- package/src/focuscycler.js +34 -9
- package/src/formheader/formheaderview.d.ts +6 -0
- package/src/formheader/formheaderview.js +6 -0
- package/src/highlightedtext/highlightedtextview.d.ts +38 -0
- package/src/highlightedtext/highlightedtextview.js +102 -0
- package/src/icon/iconview.d.ts +7 -0
- package/src/icon/iconview.js +2 -0
- package/src/index.d.ts +12 -2
- package/src/index.js +8 -0
- package/src/input/inputbase.d.ts +107 -0
- package/src/input/inputbase.js +110 -0
- package/src/input/inputview.d.ts +4 -89
- package/src/input/inputview.js +5 -87
- package/src/labeledfield/labeledfieldview.d.ts +7 -2
- package/src/labeledfield/labeledfieldview.js +2 -2
- package/src/labeledfield/utils.d.ts +34 -4
- package/src/labeledfield/utils.js +51 -6
- package/src/list/listitemgroupview.d.ts +59 -0
- package/src/list/listitemgroupview.js +63 -0
- package/src/list/listitemview.d.ts +2 -1
- package/src/list/listitemview.js +3 -1
- package/src/list/listview.d.ts +59 -2
- package/src/list/listview.js +105 -8
- package/src/panel/balloon/balloonpanelview.js +26 -4
- package/src/panel/sticky/stickypanelview.d.ts +1 -3
- package/src/panel/sticky/stickypanelview.js +53 -50
- package/src/search/filteredview.d.ts +31 -0
- package/src/search/filteredview.js +5 -0
- package/src/search/searchinfoview.d.ts +45 -0
- package/src/search/searchinfoview.js +59 -0
- package/src/search/searchresultsview.d.ts +54 -0
- package/src/search/searchresultsview.js +65 -0
- package/src/search/text/searchtextqueryview.d.ts +76 -0
- package/src/search/text/searchtextqueryview.js +75 -0
- package/src/search/text/searchtextview.d.ts +219 -0
- package/src/search/text/searchtextview.js +201 -0
- package/src/spinner/spinnerview.d.ts +25 -0
- package/src/spinner/spinnerview.js +38 -0
- package/src/textarea/textareaview.d.ts +88 -0
- package/src/textarea/textareaview.js +142 -0
- package/src/toolbar/block/blocktoolbar.js +30 -26
- package/src/toolbar/normalizetoolbarconfig.d.ts +1 -0
- package/src/toolbar/normalizetoolbarconfig.js +9 -8
- package/src/toolbar/toolbarview.d.ts +1 -0
- package/src/toolbar/toolbarview.js +4 -2
- package/theme/components/arialiveannouncer/arialiveannouncer.css +10 -0
- package/theme/components/autocomplete/autocomplete.css +22 -0
- package/theme/components/button/button.css +9 -1
- package/theme/components/formheader/formheader.css +4 -0
- package/theme/components/highlightedtext/highlightedtext.css +12 -0
- package/theme/components/search/search.css +43 -0
- package/theme/components/spinner/spinner.css +23 -0
- package/theme/components/textarea/textarea.css +10 -0
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import InputTextView from '../inputtext/inputtextview';
|
|
9
9
|
import InputNumberView from '../inputnumber/inputnumberview';
|
|
10
|
-
import
|
|
10
|
+
import TextareaView from '../textarea/textareaview';
|
|
11
11
|
import type DropdownView from '../dropdown/dropdownview';
|
|
12
|
+
import type { LabeledFieldViewCreator } from './labeledfieldview';
|
|
12
13
|
/**
|
|
13
14
|
* A helper for creating labeled inputs.
|
|
14
15
|
*
|
|
@@ -36,7 +37,7 @@ import type DropdownView from '../dropdown/dropdownview';
|
|
|
36
37
|
* {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView#statusView labeled view's status} and the input.
|
|
37
38
|
* @returns The input text view instance.
|
|
38
39
|
*/
|
|
39
|
-
|
|
40
|
+
declare const createLabeledInputText: LabeledFieldViewCreator<InputTextView>;
|
|
40
41
|
/**
|
|
41
42
|
* A helper for creating labeled number inputs.
|
|
42
43
|
*
|
|
@@ -64,7 +65,35 @@ export declare function createLabeledInputText(labeledFieldView: LabeledFieldVie
|
|
|
64
65
|
* {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView#statusView labeled view's status} and the input.
|
|
65
66
|
* @returns The input number view instance.
|
|
66
67
|
*/
|
|
67
|
-
|
|
68
|
+
declare const createLabeledInputNumber: LabeledFieldViewCreator<InputNumberView>;
|
|
69
|
+
/**
|
|
70
|
+
* A helper for creating labeled textarea.
|
|
71
|
+
*
|
|
72
|
+
* It creates an instance of a {@link module:ui/textarea/textareaview~TextareaView textarea} that is
|
|
73
|
+
* logically related to a {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView labeled view} in DOM.
|
|
74
|
+
*
|
|
75
|
+
* The helper does the following:
|
|
76
|
+
*
|
|
77
|
+
* * It sets textarea's `id` and `ariaDescribedById` attributes.
|
|
78
|
+
* * It binds textarea's `isReadOnly` to the labeled view.
|
|
79
|
+
* * It binds textarea's `hasError` to the labeled view.
|
|
80
|
+
* * It enables a logic that cleans up the error when user starts typing in the textarea.
|
|
81
|
+
*
|
|
82
|
+
* Usage:
|
|
83
|
+
*
|
|
84
|
+
* ```ts
|
|
85
|
+
* const labeledTextarea = new LabeledFieldView( locale, createLabeledTextarea );
|
|
86
|
+
* console.log( labeledTextarea.fieldView ); // A textarea instance.
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* @param labeledFieldView The instance of the labeled field view.
|
|
90
|
+
* @param viewUid An UID string that allows DOM logical connection between the
|
|
91
|
+
* {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView#labelView labeled view's label} and the textarea.
|
|
92
|
+
* @param statusUid An UID string that allows DOM logical connection between the
|
|
93
|
+
* {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView#statusView labeled view's status} and the textarea.
|
|
94
|
+
* @returns The textarea view instance.
|
|
95
|
+
*/
|
|
96
|
+
declare const createLabeledTextarea: LabeledFieldViewCreator<TextareaView>;
|
|
68
97
|
/**
|
|
69
98
|
* A helper for creating labeled dropdowns.
|
|
70
99
|
*
|
|
@@ -90,4 +119,5 @@ export declare function createLabeledInputNumber(labeledFieldView: LabeledFieldV
|
|
|
90
119
|
* {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView#statusView labeled view status} and the dropdown.
|
|
91
120
|
* @returns The dropdown view instance.
|
|
92
121
|
*/
|
|
93
|
-
|
|
122
|
+
declare const createLabeledDropdown: LabeledFieldViewCreator<DropdownView>;
|
|
123
|
+
export { createLabeledInputNumber, createLabeledInputText, createLabeledTextarea, createLabeledDropdown };
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import InputTextView from '../inputtext/inputtextview';
|
|
9
9
|
import InputNumberView from '../inputnumber/inputnumberview';
|
|
10
|
+
import TextareaView from '../textarea/textareaview';
|
|
10
11
|
import { createDropdown } from '../dropdown/utils';
|
|
11
12
|
/**
|
|
12
13
|
* A helper for creating labeled inputs.
|
|
@@ -35,7 +36,7 @@ import { createDropdown } from '../dropdown/utils';
|
|
|
35
36
|
* {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView#statusView labeled view's status} and the input.
|
|
36
37
|
* @returns The input text view instance.
|
|
37
38
|
*/
|
|
38
|
-
|
|
39
|
+
const createLabeledInputText = (labeledFieldView, viewUid, statusUid) => {
|
|
39
40
|
const inputView = new InputTextView(labeledFieldView.locale);
|
|
40
41
|
inputView.set({
|
|
41
42
|
id: viewUid,
|
|
@@ -50,7 +51,7 @@ export function createLabeledInputText(labeledFieldView, viewUid, statusUid) {
|
|
|
50
51
|
});
|
|
51
52
|
labeledFieldView.bind('isEmpty', 'isFocused', 'placeholder').to(inputView);
|
|
52
53
|
return inputView;
|
|
53
|
-
}
|
|
54
|
+
};
|
|
54
55
|
/**
|
|
55
56
|
* A helper for creating labeled number inputs.
|
|
56
57
|
*
|
|
@@ -78,7 +79,7 @@ export function createLabeledInputText(labeledFieldView, viewUid, statusUid) {
|
|
|
78
79
|
* {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView#statusView labeled view's status} and the input.
|
|
79
80
|
* @returns The input number view instance.
|
|
80
81
|
*/
|
|
81
|
-
|
|
82
|
+
const createLabeledInputNumber = (labeledFieldView, viewUid, statusUid) => {
|
|
82
83
|
const inputView = new InputNumberView(labeledFieldView.locale);
|
|
83
84
|
inputView.set({
|
|
84
85
|
id: viewUid,
|
|
@@ -94,7 +95,50 @@ export function createLabeledInputNumber(labeledFieldView, viewUid, statusUid) {
|
|
|
94
95
|
});
|
|
95
96
|
labeledFieldView.bind('isEmpty', 'isFocused', 'placeholder').to(inputView);
|
|
96
97
|
return inputView;
|
|
97
|
-
}
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* A helper for creating labeled textarea.
|
|
101
|
+
*
|
|
102
|
+
* It creates an instance of a {@link module:ui/textarea/textareaview~TextareaView textarea} that is
|
|
103
|
+
* logically related to a {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView labeled view} in DOM.
|
|
104
|
+
*
|
|
105
|
+
* The helper does the following:
|
|
106
|
+
*
|
|
107
|
+
* * It sets textarea's `id` and `ariaDescribedById` attributes.
|
|
108
|
+
* * It binds textarea's `isReadOnly` to the labeled view.
|
|
109
|
+
* * It binds textarea's `hasError` to the labeled view.
|
|
110
|
+
* * It enables a logic that cleans up the error when user starts typing in the textarea.
|
|
111
|
+
*
|
|
112
|
+
* Usage:
|
|
113
|
+
*
|
|
114
|
+
* ```ts
|
|
115
|
+
* const labeledTextarea = new LabeledFieldView( locale, createLabeledTextarea );
|
|
116
|
+
* console.log( labeledTextarea.fieldView ); // A textarea instance.
|
|
117
|
+
* ```
|
|
118
|
+
*
|
|
119
|
+
* @param labeledFieldView The instance of the labeled field view.
|
|
120
|
+
* @param viewUid An UID string that allows DOM logical connection between the
|
|
121
|
+
* {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView#labelView labeled view's label} and the textarea.
|
|
122
|
+
* @param statusUid An UID string that allows DOM logical connection between the
|
|
123
|
+
* {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView#statusView labeled view's status} and the textarea.
|
|
124
|
+
* @returns The textarea view instance.
|
|
125
|
+
*/
|
|
126
|
+
const createLabeledTextarea = (labeledFieldView, viewUid, statusUid) => {
|
|
127
|
+
const textareaView = new TextareaView(labeledFieldView.locale);
|
|
128
|
+
textareaView.set({
|
|
129
|
+
id: viewUid,
|
|
130
|
+
ariaDescribedById: statusUid
|
|
131
|
+
});
|
|
132
|
+
textareaView.bind('isReadOnly').to(labeledFieldView, 'isEnabled', value => !value);
|
|
133
|
+
textareaView.bind('hasError').to(labeledFieldView, 'errorText', value => !!value);
|
|
134
|
+
textareaView.on('input', () => {
|
|
135
|
+
// UX: Make the error text disappear and disable the error indicator as the user
|
|
136
|
+
// starts fixing the errors.
|
|
137
|
+
labeledFieldView.errorText = null;
|
|
138
|
+
});
|
|
139
|
+
labeledFieldView.bind('isEmpty', 'isFocused', 'placeholder').to(textareaView);
|
|
140
|
+
return textareaView;
|
|
141
|
+
};
|
|
98
142
|
/**
|
|
99
143
|
* A helper for creating labeled dropdowns.
|
|
100
144
|
*
|
|
@@ -120,7 +164,7 @@ export function createLabeledInputNumber(labeledFieldView, viewUid, statusUid) {
|
|
|
120
164
|
* {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView#statusView labeled view status} and the dropdown.
|
|
121
165
|
* @returns The dropdown view instance.
|
|
122
166
|
*/
|
|
123
|
-
|
|
167
|
+
const createLabeledDropdown = (labeledFieldView, viewUid, statusUid) => {
|
|
124
168
|
const dropdownView = createDropdown(labeledFieldView.locale);
|
|
125
169
|
dropdownView.set({
|
|
126
170
|
id: viewUid,
|
|
@@ -128,4 +172,5 @@ export function createLabeledDropdown(labeledFieldView, viewUid, statusUid) {
|
|
|
128
172
|
});
|
|
129
173
|
dropdownView.bind('isEnabled').to(labeledFieldView);
|
|
130
174
|
return dropdownView;
|
|
131
|
-
}
|
|
175
|
+
};
|
|
176
|
+
export { createLabeledInputNumber, createLabeledInputText, createLabeledTextarea, createLabeledDropdown };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2023, 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/list/listitemgroupview
|
|
7
|
+
*/
|
|
8
|
+
import View from '../view';
|
|
9
|
+
import type ViewCollection from '../viewcollection';
|
|
10
|
+
import ListView from './listview';
|
|
11
|
+
import LabelView from '../label/labelview';
|
|
12
|
+
import { type Locale } from '@ckeditor/ckeditor5-utils';
|
|
13
|
+
/**
|
|
14
|
+
* The list item group view class.
|
|
15
|
+
*/
|
|
16
|
+
export default class ListItemGroupView extends View {
|
|
17
|
+
/**
|
|
18
|
+
* The visible label of the group.
|
|
19
|
+
*
|
|
20
|
+
* @observable
|
|
21
|
+
* @default ''
|
|
22
|
+
*/
|
|
23
|
+
label: string;
|
|
24
|
+
/**
|
|
25
|
+
* Label of the group view. Its text is configurable using the {@link #label label attribute}.
|
|
26
|
+
*
|
|
27
|
+
* If a custom label view is not passed in `ListItemGroupView` constructor, the label is an instance
|
|
28
|
+
* of {@link module:ui/label/labelview~LabelView}.
|
|
29
|
+
*/
|
|
30
|
+
readonly labelView: LabelView;
|
|
31
|
+
/**
|
|
32
|
+
* Collection of the child list items inside this group.
|
|
33
|
+
*/
|
|
34
|
+
readonly items: ListView['items'];
|
|
35
|
+
/**
|
|
36
|
+
* Collection of the child elements of the group.
|
|
37
|
+
*/
|
|
38
|
+
readonly children: ViewCollection;
|
|
39
|
+
/**
|
|
40
|
+
* Controls whether the item view is visible. Visible by default, list items are hidden
|
|
41
|
+
* using a CSS class.
|
|
42
|
+
*
|
|
43
|
+
* @observable
|
|
44
|
+
* @default true
|
|
45
|
+
*/
|
|
46
|
+
isVisible: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Creates an instance of the list item group view class.
|
|
49
|
+
*
|
|
50
|
+
* @param locale The {@link module:core/editor/editor~Editor#locale} instance.
|
|
51
|
+
* @param labelView The instance of the group's label. If not provided, an instance of
|
|
52
|
+
* {@link module:ui/label/labelview~LabelView} is used.
|
|
53
|
+
*/
|
|
54
|
+
constructor(locale?: Locale, labelView?: LabelView);
|
|
55
|
+
/**
|
|
56
|
+
* Focuses the list item.
|
|
57
|
+
*/
|
|
58
|
+
focus(): void;
|
|
59
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2023, 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/list/listitemgroupview
|
|
7
|
+
*/
|
|
8
|
+
import View from '../view';
|
|
9
|
+
import ListView from './listview';
|
|
10
|
+
import LabelView from '../label/labelview';
|
|
11
|
+
/**
|
|
12
|
+
* The list item group view class.
|
|
13
|
+
*/
|
|
14
|
+
export default class ListItemGroupView extends View {
|
|
15
|
+
/**
|
|
16
|
+
* Creates an instance of the list item group view class.
|
|
17
|
+
*
|
|
18
|
+
* @param locale The {@link module:core/editor/editor~Editor#locale} instance.
|
|
19
|
+
* @param labelView The instance of the group's label. If not provided, an instance of
|
|
20
|
+
* {@link module:ui/label/labelview~LabelView} is used.
|
|
21
|
+
*/
|
|
22
|
+
constructor(locale, labelView = new LabelView()) {
|
|
23
|
+
super(locale);
|
|
24
|
+
const bind = this.bindTemplate;
|
|
25
|
+
const nestedList = new ListView(locale);
|
|
26
|
+
this.set({
|
|
27
|
+
label: '',
|
|
28
|
+
isVisible: true
|
|
29
|
+
});
|
|
30
|
+
this.labelView = labelView;
|
|
31
|
+
this.labelView.bind('text').to(this, 'label');
|
|
32
|
+
this.children = this.createCollection();
|
|
33
|
+
this.children.addMany([this.labelView, nestedList]);
|
|
34
|
+
nestedList.set({
|
|
35
|
+
role: 'group',
|
|
36
|
+
ariaLabelledBy: labelView.id
|
|
37
|
+
});
|
|
38
|
+
// Disable focus tracking and accessible navigation in the child list.
|
|
39
|
+
nestedList.focusTracker.destroy();
|
|
40
|
+
nestedList.keystrokes.destroy();
|
|
41
|
+
this.items = nestedList.items;
|
|
42
|
+
this.setTemplate({
|
|
43
|
+
tag: 'li',
|
|
44
|
+
attributes: {
|
|
45
|
+
role: 'presentation',
|
|
46
|
+
class: [
|
|
47
|
+
'ck',
|
|
48
|
+
'ck-list__group',
|
|
49
|
+
bind.if('isVisible', 'ck-hidden', value => !value)
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
children: this.children
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Focuses the list item.
|
|
57
|
+
*/
|
|
58
|
+
focus() {
|
|
59
|
+
if (this.items.first) {
|
|
60
|
+
this.items.first.focus();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* @module ui/list/listitemview
|
|
7
7
|
*/
|
|
8
8
|
import View from '../view';
|
|
9
|
+
import type { FocusableView } from '../focuscycler';
|
|
9
10
|
import type ViewCollection from '../viewcollection';
|
|
10
11
|
import type { Locale } from '@ckeditor/ckeditor5-utils';
|
|
11
12
|
/**
|
|
@@ -15,7 +16,7 @@ export default class ListItemView extends View {
|
|
|
15
16
|
/**
|
|
16
17
|
* Collection of the child views inside of the list item {@link #element}.
|
|
17
18
|
*/
|
|
18
|
-
readonly children: ViewCollection
|
|
19
|
+
readonly children: ViewCollection<FocusableView>;
|
|
19
20
|
/**
|
|
20
21
|
* Controls whether the item view is visible. Visible by default, list items are hidden
|
|
21
22
|
* using a CSS class.
|
package/src/list/listitemview.js
CHANGED
package/src/list/listview.d.ts
CHANGED
|
@@ -6,18 +6,26 @@
|
|
|
6
6
|
* @module ui/list/listview
|
|
7
7
|
*/
|
|
8
8
|
import View from '../view';
|
|
9
|
+
import type ListItemView from './listitemview';
|
|
10
|
+
import ListItemGroupView from './listitemgroupview';
|
|
9
11
|
import type DropdownPanelFocusable from '../dropdown/dropdownpanelfocusable';
|
|
10
|
-
import
|
|
12
|
+
import ViewCollection from '../viewcollection';
|
|
11
13
|
import { FocusTracker, KeystrokeHandler, type Locale } from '@ckeditor/ckeditor5-utils';
|
|
12
14
|
import '../../theme/components/list/list.css';
|
|
13
15
|
/**
|
|
14
16
|
* The list view class.
|
|
15
17
|
*/
|
|
16
18
|
export default class ListView extends View<HTMLUListElement> implements DropdownPanelFocusable {
|
|
19
|
+
/**
|
|
20
|
+
* The collection of focusable views in the list. It is used to determine accessible navigation
|
|
21
|
+
* between the {@link module:ui/list/listitemview~ListItemView list items} and
|
|
22
|
+
* {@link module:ui/list/listitemgroupview~ListItemGroupView list groups}.
|
|
23
|
+
*/
|
|
24
|
+
readonly focusables: ViewCollection;
|
|
17
25
|
/**
|
|
18
26
|
* Collection of the child list views.
|
|
19
27
|
*/
|
|
20
|
-
readonly items: ViewCollection
|
|
28
|
+
readonly items: ViewCollection<ListItemView | ListItemGroupView>;
|
|
21
29
|
/**
|
|
22
30
|
* Tracks information about DOM focus in the list.
|
|
23
31
|
*/
|
|
@@ -32,6 +40,12 @@ export default class ListView extends View<HTMLUListElement> implements Dropdown
|
|
|
32
40
|
* @observable
|
|
33
41
|
*/
|
|
34
42
|
ariaLabel: string | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* (Optional) The ARIA property reflected by the `aria-ariaLabelledBy` DOM attribute used by assistive technologies.
|
|
45
|
+
*
|
|
46
|
+
* @observable
|
|
47
|
+
*/
|
|
48
|
+
ariaLabelledBy?: string | undefined;
|
|
35
49
|
/**
|
|
36
50
|
* The property reflected by the `role` DOM attribute to be used by assistive technologies.
|
|
37
51
|
*
|
|
@@ -42,6 +56,11 @@ export default class ListView extends View<HTMLUListElement> implements Dropdown
|
|
|
42
56
|
* Helps cycling over focusable {@link #items} in the list.
|
|
43
57
|
*/
|
|
44
58
|
private readonly _focusCycler;
|
|
59
|
+
/**
|
|
60
|
+
* A cached map of {@link module:ui/list/listitemgroupview~ListItemGroupView} to `change` event listeners for their `items`.
|
|
61
|
+
* Used for accessibility and keyboard navigation purposes.
|
|
62
|
+
*/
|
|
63
|
+
private readonly _listItemGroupToChangeListeners;
|
|
45
64
|
/**
|
|
46
65
|
* @inheritDoc
|
|
47
66
|
*/
|
|
@@ -58,8 +77,46 @@ export default class ListView extends View<HTMLUListElement> implements Dropdown
|
|
|
58
77
|
* Focuses the first focusable in {@link #items}.
|
|
59
78
|
*/
|
|
60
79
|
focus(): void;
|
|
80
|
+
/**
|
|
81
|
+
* Focuses the first focusable in {@link #items}.
|
|
82
|
+
*/
|
|
83
|
+
focusFirst(): void;
|
|
61
84
|
/**
|
|
62
85
|
* Focuses the last focusable in {@link #items}.
|
|
63
86
|
*/
|
|
64
87
|
focusLast(): void;
|
|
88
|
+
/**
|
|
89
|
+
* Registers a list item view in the focus tracker.
|
|
90
|
+
*
|
|
91
|
+
* @param item The list item view to be registered.
|
|
92
|
+
* @param index Index of the list item view in the {@link #items} collection. If not specified, the item will be added at the end.
|
|
93
|
+
*/
|
|
94
|
+
private _registerFocusableListItem;
|
|
95
|
+
/**
|
|
96
|
+
* Removes a list item view from the focus tracker.
|
|
97
|
+
*
|
|
98
|
+
* @param item The list item view to be removed.
|
|
99
|
+
*/
|
|
100
|
+
private _deregisterFocusableListItem;
|
|
101
|
+
/**
|
|
102
|
+
* Gets a callback that will be called when the `items` collection of a {@link module:ui/list/listitemgroupview~ListItemGroupView}
|
|
103
|
+
* change.
|
|
104
|
+
*
|
|
105
|
+
* @param groupView The group view for which the callback will be created.
|
|
106
|
+
* @returns The callback function to be used for the items `change` event listener in a group.
|
|
107
|
+
*/
|
|
108
|
+
private _getOnGroupItemsChangeCallback;
|
|
109
|
+
/**
|
|
110
|
+
* Registers a list item group view (and its children) in the focus tracker.
|
|
111
|
+
*
|
|
112
|
+
* @param groupView A group view to be registered.
|
|
113
|
+
* @param groupIndex Index of the group view in the {@link #items} collection. If not specified, the group will be added at the end.
|
|
114
|
+
*/
|
|
115
|
+
private _registerFocusableItemsGroup;
|
|
116
|
+
/**
|
|
117
|
+
* Removes a list item group view (and its children) from the focus tracker.
|
|
118
|
+
*
|
|
119
|
+
* @param groupView The group view to be removed.
|
|
120
|
+
*/
|
|
121
|
+
private _deregisterFocusableItemsGroup;
|
|
65
122
|
}
|
package/src/list/listview.js
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import View from '../view';
|
|
9
9
|
import FocusCycler from '../focuscycler';
|
|
10
|
+
import ListItemGroupView from './listitemgroupview';
|
|
11
|
+
import ViewCollection from '../viewcollection';
|
|
10
12
|
import { FocusTracker, KeystrokeHandler } from '@ckeditor/ckeditor5-utils';
|
|
11
13
|
import '../../theme/components/list/list.css';
|
|
12
14
|
/**
|
|
@@ -18,12 +20,18 @@ export default class ListView extends View {
|
|
|
18
20
|
*/
|
|
19
21
|
constructor(locale) {
|
|
20
22
|
super(locale);
|
|
23
|
+
/**
|
|
24
|
+
* A cached map of {@link module:ui/list/listitemgroupview~ListItemGroupView} to `change` event listeners for their `items`.
|
|
25
|
+
* Used for accessibility and keyboard navigation purposes.
|
|
26
|
+
*/
|
|
27
|
+
this._listItemGroupToChangeListeners = new WeakMap();
|
|
21
28
|
const bind = this.bindTemplate;
|
|
29
|
+
this.focusables = new ViewCollection();
|
|
22
30
|
this.items = this.createCollection();
|
|
23
31
|
this.focusTracker = new FocusTracker();
|
|
24
32
|
this.keystrokes = new KeystrokeHandler();
|
|
25
33
|
this._focusCycler = new FocusCycler({
|
|
26
|
-
focusables: this.
|
|
34
|
+
focusables: this.focusables,
|
|
27
35
|
focusTracker: this.focusTracker,
|
|
28
36
|
keystrokeHandler: this.keystrokes,
|
|
29
37
|
actions: {
|
|
@@ -34,6 +42,7 @@ export default class ListView extends View {
|
|
|
34
42
|
}
|
|
35
43
|
});
|
|
36
44
|
this.set('ariaLabel', undefined);
|
|
45
|
+
this.set('ariaLabelledBy', undefined);
|
|
37
46
|
this.set('role', undefined);
|
|
38
47
|
this.setTemplate({
|
|
39
48
|
tag: 'ul',
|
|
@@ -44,7 +53,8 @@ export default class ListView extends View {
|
|
|
44
53
|
'ck-list'
|
|
45
54
|
],
|
|
46
55
|
role: bind.to('role'),
|
|
47
|
-
'aria-label': bind.to('ariaLabel')
|
|
56
|
+
'aria-label': bind.to('ariaLabel'),
|
|
57
|
+
'aria-labelledby': bind.to('ariaLabelledBy')
|
|
48
58
|
},
|
|
49
59
|
children: this.items
|
|
50
60
|
});
|
|
@@ -56,13 +66,30 @@ export default class ListView extends View {
|
|
|
56
66
|
super.render();
|
|
57
67
|
// Items added before rendering should be known to the #focusTracker.
|
|
58
68
|
for (const item of this.items) {
|
|
59
|
-
|
|
69
|
+
if (item instanceof ListItemGroupView) {
|
|
70
|
+
this._registerFocusableItemsGroup(item);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
this._registerFocusableListItem(item);
|
|
74
|
+
}
|
|
60
75
|
}
|
|
61
|
-
this.items.on('
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
76
|
+
this.items.on('change', (evt, data) => {
|
|
77
|
+
for (const removed of data.removed) {
|
|
78
|
+
if (removed instanceof ListItemGroupView) {
|
|
79
|
+
this._deregisterFocusableItemsGroup(removed);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
this._deregisterFocusableListItem(removed);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
for (const added of Array.from(data.added).reverse()) {
|
|
86
|
+
if (added instanceof ListItemGroupView) {
|
|
87
|
+
this._registerFocusableItemsGroup(added, data.index);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
this._registerFocusableListItem(added, data.index);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
66
93
|
});
|
|
67
94
|
// Start listening for the keystrokes coming from #element.
|
|
68
95
|
this.keystrokes.listenTo(this.element);
|
|
@@ -81,10 +108,80 @@ export default class ListView extends View {
|
|
|
81
108
|
focus() {
|
|
82
109
|
this._focusCycler.focusFirst();
|
|
83
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Focuses the first focusable in {@link #items}.
|
|
113
|
+
*/
|
|
114
|
+
focusFirst() {
|
|
115
|
+
this._focusCycler.focusFirst();
|
|
116
|
+
}
|
|
84
117
|
/**
|
|
85
118
|
* Focuses the last focusable in {@link #items}.
|
|
86
119
|
*/
|
|
87
120
|
focusLast() {
|
|
88
121
|
this._focusCycler.focusLast();
|
|
89
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Registers a list item view in the focus tracker.
|
|
125
|
+
*
|
|
126
|
+
* @param item The list item view to be registered.
|
|
127
|
+
* @param index Index of the list item view in the {@link #items} collection. If not specified, the item will be added at the end.
|
|
128
|
+
*/
|
|
129
|
+
_registerFocusableListItem(item, index) {
|
|
130
|
+
this.focusTracker.add(item.element);
|
|
131
|
+
this.focusables.add(item, index);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Removes a list item view from the focus tracker.
|
|
135
|
+
*
|
|
136
|
+
* @param item The list item view to be removed.
|
|
137
|
+
*/
|
|
138
|
+
_deregisterFocusableListItem(item) {
|
|
139
|
+
this.focusTracker.remove(item.element);
|
|
140
|
+
this.focusables.remove(item);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Gets a callback that will be called when the `items` collection of a {@link module:ui/list/listitemgroupview~ListItemGroupView}
|
|
144
|
+
* change.
|
|
145
|
+
*
|
|
146
|
+
* @param groupView The group view for which the callback will be created.
|
|
147
|
+
* @returns The callback function to be used for the items `change` event listener in a group.
|
|
148
|
+
*/
|
|
149
|
+
_getOnGroupItemsChangeCallback(groupView) {
|
|
150
|
+
return (evt, data) => {
|
|
151
|
+
for (const removed of data.removed) {
|
|
152
|
+
this._deregisterFocusableListItem(removed);
|
|
153
|
+
}
|
|
154
|
+
for (const added of Array.from(data.added).reverse()) {
|
|
155
|
+
this._registerFocusableListItem(added, this.items.getIndex(groupView) + data.index);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Registers a list item group view (and its children) in the focus tracker.
|
|
161
|
+
*
|
|
162
|
+
* @param groupView A group view to be registered.
|
|
163
|
+
* @param groupIndex Index of the group view in the {@link #items} collection. If not specified, the group will be added at the end.
|
|
164
|
+
*/
|
|
165
|
+
_registerFocusableItemsGroup(groupView, groupIndex) {
|
|
166
|
+
Array.from(groupView.items).forEach((child, childIndex) => {
|
|
167
|
+
const registeredChildIndex = typeof groupIndex !== 'undefined' ? groupIndex + childIndex : undefined;
|
|
168
|
+
this._registerFocusableListItem(child, registeredChildIndex);
|
|
169
|
+
});
|
|
170
|
+
const groupItemsChangeCallback = this._getOnGroupItemsChangeCallback(groupView);
|
|
171
|
+
// Cache the reference to the callback in case the group is removed (see _deregisterFocusableItemsGroup()).
|
|
172
|
+
this._listItemGroupToChangeListeners.set(groupView, groupItemsChangeCallback);
|
|
173
|
+
groupView.items.on('change', groupItemsChangeCallback);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Removes a list item group view (and its children) from the focus tracker.
|
|
177
|
+
*
|
|
178
|
+
* @param groupView The group view to be removed.
|
|
179
|
+
*/
|
|
180
|
+
_deregisterFocusableItemsGroup(groupView) {
|
|
181
|
+
for (const child of groupView.items) {
|
|
182
|
+
this._deregisterFocusableListItem(child);
|
|
183
|
+
}
|
|
184
|
+
groupView.items.off('change', this._listItemGroupToChangeListeners.get(groupView));
|
|
185
|
+
this._listItemGroupToChangeListeners.delete(groupView);
|
|
186
|
+
}
|
|
90
187
|
}
|
|
@@ -11,6 +11,22 @@ import { isElement } from 'lodash-es';
|
|
|
11
11
|
import '../../../theme/components/panel/balloonpanel.css';
|
|
12
12
|
const toPx = toUnit('px');
|
|
13
13
|
const defaultLimiterElement = global.document.body;
|
|
14
|
+
// A static balloon panel positioning function that moves the balloon far off the viewport.
|
|
15
|
+
// It is used as a fallback when there is no way to position the balloon using provided
|
|
16
|
+
// positioning functions (see: `getOptimalPosition()`), for instance, when the target the
|
|
17
|
+
// balloon should be attached to gets obscured by scrollable containers or the viewport.
|
|
18
|
+
//
|
|
19
|
+
// It prevents the balloon from being attached to the void and possible degradation of the UX.
|
|
20
|
+
// At the same time, it keeps the balloon physically visible in the DOM so the focus remains
|
|
21
|
+
// uninterrupted.
|
|
22
|
+
const POSITION_OFF_SCREEN = {
|
|
23
|
+
top: -99999,
|
|
24
|
+
left: -99999,
|
|
25
|
+
name: 'arrowless',
|
|
26
|
+
config: {
|
|
27
|
+
withArrow: false
|
|
28
|
+
}
|
|
29
|
+
};
|
|
14
30
|
/**
|
|
15
31
|
* The balloon panel view class.
|
|
16
32
|
*
|
|
@@ -153,7 +169,7 @@ export default class BalloonPanelView extends View {
|
|
|
153
169
|
limiter: defaultLimiterElement,
|
|
154
170
|
fitInViewport: true
|
|
155
171
|
}, options);
|
|
156
|
-
const optimalPosition = BalloonPanelView._getOptimalPosition(positionOptions);
|
|
172
|
+
const optimalPosition = BalloonPanelView._getOptimalPosition(positionOptions) || POSITION_OFF_SCREEN;
|
|
157
173
|
// Usually browsers make some problems with super accurate values like 104.345px
|
|
158
174
|
// so it is better to use int values.
|
|
159
175
|
const left = parseInt(optimalPosition.left);
|
|
@@ -953,12 +969,18 @@ export function generatePositions(options = {}) {
|
|
|
953
969
|
...(config && { config })
|
|
954
970
|
}),
|
|
955
971
|
// ------- Sticky
|
|
956
|
-
viewportStickyNorth: (targetRect, balloonRect, viewportRect) => {
|
|
957
|
-
|
|
972
|
+
viewportStickyNorth: (targetRect, balloonRect, viewportRect, limiterRect) => {
|
|
973
|
+
const boundaryRect = limiterRect || viewportRect;
|
|
974
|
+
if (!targetRect.getIntersection(boundaryRect)) {
|
|
975
|
+
return null;
|
|
976
|
+
}
|
|
977
|
+
// Engage when the target top and bottom edges are close or off the boundary.
|
|
978
|
+
// By close, it means there's not enough space for the balloon arrow (offset).
|
|
979
|
+
if (boundaryRect.height - targetRect.height > stickyVerticalOffset) {
|
|
958
980
|
return null;
|
|
959
981
|
}
|
|
960
982
|
return {
|
|
961
|
-
top:
|
|
983
|
+
top: boundaryRect.top + stickyVerticalOffset,
|
|
962
984
|
left: targetRect.left + targetRect.width / 2 - balloonRect.width / 2,
|
|
963
985
|
name: 'arrowless',
|
|
964
986
|
config: {
|
|
@@ -125,10 +125,8 @@ export default class StickyPanelView extends View {
|
|
|
125
125
|
/**
|
|
126
126
|
* Analyzes the environment to decide whether the panel should be sticky or not.
|
|
127
127
|
* Then handles the positioning of the panel.
|
|
128
|
-
*
|
|
129
|
-
* @param [scrollTarget] The element which is being scrolled.
|
|
130
128
|
*/
|
|
131
|
-
checkIfShouldBeSticky(
|
|
129
|
+
checkIfShouldBeSticky(): void;
|
|
132
130
|
/**
|
|
133
131
|
* Sticks the panel at the given CSS `top` offset.
|
|
134
132
|
*
|