@ckeditor/ckeditor5-bookmark 0.0.0-nightly-20241025.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/LICENSE.md +17 -0
  3. package/README.md +26 -0
  4. package/build/bookmark.js +4 -0
  5. package/ckeditor5-metadata.json +24 -0
  6. package/dist/augmentation.d.ts +28 -0
  7. package/dist/bookmark.d.ts +34 -0
  8. package/dist/bookmarkconfig.d.ts +52 -0
  9. package/dist/bookmarkediting.d.ts +55 -0
  10. package/dist/bookmarkui.d.ts +170 -0
  11. package/dist/index-content.css +4 -0
  12. package/dist/index-editor.css +150 -0
  13. package/dist/index.css +195 -0
  14. package/dist/index.css.map +1 -0
  15. package/dist/index.d.ts +18 -0
  16. package/dist/index.js +1322 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/insertbookmarkcommand.d.ts +42 -0
  19. package/dist/ui/bookmarkactionsview.d.ts +106 -0
  20. package/dist/ui/bookmarkformview.d.ts +122 -0
  21. package/dist/updatebookmarkcommand.d.ts +46 -0
  22. package/dist/utils.d.ts +15 -0
  23. package/lang/contexts.json +13 -0
  24. package/package.json +43 -0
  25. package/src/augmentation.d.ts +24 -0
  26. package/src/augmentation.js +5 -0
  27. package/src/bookmark.d.ts +30 -0
  28. package/src/bookmark.js +36 -0
  29. package/src/bookmarkconfig.d.ts +48 -0
  30. package/src/bookmarkconfig.js +5 -0
  31. package/src/bookmarkediting.d.ts +51 -0
  32. package/src/bookmarkediting.js +212 -0
  33. package/src/bookmarkui.d.ts +166 -0
  34. package/src/bookmarkui.js +583 -0
  35. package/src/index.d.ts +14 -0
  36. package/src/index.js +13 -0
  37. package/src/insertbookmarkcommand.d.ts +38 -0
  38. package/src/insertbookmarkcommand.js +113 -0
  39. package/src/ui/bookmarkactionsview.d.ts +102 -0
  40. package/src/ui/bookmarkactionsview.js +154 -0
  41. package/src/ui/bookmarkformview.d.ts +118 -0
  42. package/src/ui/bookmarkformview.js +203 -0
  43. package/src/updatebookmarkcommand.d.ts +42 -0
  44. package/src/updatebookmarkcommand.js +75 -0
  45. package/src/utils.d.ts +11 -0
  46. package/src/utils.js +19 -0
  47. package/theme/bookmark.css +50 -0
  48. package/theme/bookmarkactions.css +44 -0
  49. package/theme/bookmarkform.css +42 -0
@@ -0,0 +1,113 @@
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
+ import { logWarning } from 'ckeditor5/src/utils.js';
6
+ import { Command } from 'ckeditor5/src/core.js';
7
+ import { isBookmarkIdValid } from './utils.js';
8
+ /**
9
+ * The insert bookmark command.
10
+ *
11
+ * The command is registered by {@link module:bookmark/bookmarkediting~BookmarkEditing} as `'insertBookmark'`.
12
+ *
13
+ * To insert a bookmark element at place where is the current collapsed selection or where is the beginning of document selection,
14
+ * execute the command passing the bookmark id as a parameter:
15
+ *
16
+ * ```ts
17
+ * editor.execute( 'insertBookmark', { bookmarkId: 'foo_bar' } );
18
+ * ```
19
+ */
20
+ export default class InsertBookmarkCommand extends Command {
21
+ /**
22
+ * @inheritDoc
23
+ */
24
+ refresh() {
25
+ const model = this.editor.model;
26
+ const selection = model.document.selection;
27
+ const position = this._getPositionToInsertBookmark(selection);
28
+ this.isEnabled = !!position;
29
+ }
30
+ /**
31
+ * Executes the command.
32
+ *
33
+ * @fires execute
34
+ * @param options Command options.
35
+ * @param options.bookmarkId The value of the `bookmarkId` attribute.
36
+ */
37
+ execute(options) {
38
+ if (!options) {
39
+ return;
40
+ }
41
+ const { bookmarkId } = options;
42
+ if (!isBookmarkIdValid(bookmarkId)) {
43
+ /**
44
+ * Insert bookmark command can be executed only with a valid name.
45
+ *
46
+ * A valid bookmark name must be a non-empty string and must not contain any spaces.
47
+ *
48
+ * @error insert-bookmark-command-executed-with-invalid-name
49
+ */
50
+ logWarning('insert-bookmark-command-executed-with-invalid-name');
51
+ return;
52
+ }
53
+ const editor = this.editor;
54
+ const model = editor.model;
55
+ const selection = model.document.selection;
56
+ model.change(writer => {
57
+ let position = this._getPositionToInsertBookmark(selection);
58
+ const isBookmarkAllowed = model.schema.checkChild(position, 'bookmark');
59
+ // If the position does not allow for `bookmark` but allows for a `paragraph`
60
+ // then insert a `paragraph` then we will insert a `bookmark` inside.
61
+ if (!isBookmarkAllowed) {
62
+ const newPosition = editor.execute('insertParagraph', { position });
63
+ if (!newPosition) {
64
+ return;
65
+ }
66
+ position = newPosition;
67
+ }
68
+ const bookmarkElement = writer.createElement('bookmark', {
69
+ ...Object.fromEntries(selection.getAttributes()),
70
+ bookmarkId
71
+ });
72
+ model.insertObject(bookmarkElement, position, null, { setSelection: 'on' });
73
+ });
74
+ }
75
+ /**
76
+ * Returns the position where the bookmark can be inserted. And if it is not possible to insert a bookmark,
77
+ * check if it is possible to insert a paragraph.
78
+ */
79
+ _getPositionToInsertBookmark(selection) {
80
+ const model = this.editor.model;
81
+ const schema = model.schema;
82
+ const firstRange = selection.getFirstRange();
83
+ const startPosition = firstRange.start;
84
+ // Return position if it is allowed to insert bookmark or if it is allowed to insert paragraph.
85
+ if (isBookmarkAllowed(startPosition, schema)) {
86
+ return startPosition;
87
+ }
88
+ for (const { previousPosition, item } of firstRange) {
89
+ // When the table cell is selected (from the outside) we look for the first paragraph-like element inside.
90
+ if (item.is('element') &&
91
+ schema.checkChild(item, '$text') &&
92
+ isBookmarkAllowed(item, schema)) {
93
+ return model.createPositionAt(item, 0);
94
+ }
95
+ if (isBookmarkAllowed(previousPosition, schema)) {
96
+ return previousPosition;
97
+ }
98
+ }
99
+ return null;
100
+ }
101
+ }
102
+ /**
103
+ * Verify if the given position allows for bookmark insertion. Verify if auto-paragraphing could help.
104
+ */
105
+ function isBookmarkAllowed(position, schema) {
106
+ if (schema.checkChild(position, 'bookmark')) {
107
+ return true;
108
+ }
109
+ if (!schema.checkChild(position, 'paragraph')) {
110
+ return false;
111
+ }
112
+ return schema.checkChild('paragraph', 'bookmark');
113
+ }
@@ -0,0 +1,102 @@
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 bookmark/ui/bookmarkactionsview
7
+ */
8
+ import { LabelView, ButtonView, View } from 'ckeditor5/src/ui.js';
9
+ import { FocusTracker, KeystrokeHandler, type LocaleTranslate, type Locale } from 'ckeditor5/src/utils.js';
10
+ import '@ckeditor/ckeditor5-ui/theme/components/responsive-form/responsiveform.css';
11
+ import '../../theme/bookmarkactions.css';
12
+ /**
13
+ * The bookmark actions view class. This view displays the bookmark preview, allows
14
+ * removing or editing the bookmark.
15
+ */
16
+ export default class BookmarkActionsView extends View {
17
+ /**
18
+ * Tracks information about DOM focus in the actions.
19
+ */
20
+ readonly focusTracker: FocusTracker;
21
+ /**
22
+ * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
23
+ */
24
+ readonly keystrokes: KeystrokeHandler;
25
+ /**
26
+ * The bookmark preview view.
27
+ */
28
+ bookmarkPreviewView: LabelView;
29
+ /**
30
+ * The remove button view.
31
+ */
32
+ removeButtonView: ButtonView;
33
+ /**
34
+ * The edit bookmark button view.
35
+ */
36
+ editButtonView: ButtonView;
37
+ /**
38
+ * The id preview view.
39
+ *
40
+ * @observable
41
+ */
42
+ id: string | undefined;
43
+ /**
44
+ * A collection of views that can be focused in the view.
45
+ */
46
+ private readonly _focusables;
47
+ /**
48
+ * Helps cycling over {@link #_focusables} in the view.
49
+ */
50
+ private readonly _focusCycler;
51
+ t: LocaleTranslate;
52
+ /**
53
+ * @inheritDoc
54
+ */
55
+ constructor(locale: Locale);
56
+ /**
57
+ * @inheritDoc
58
+ */
59
+ render(): void;
60
+ /**
61
+ * @inheritDoc
62
+ */
63
+ destroy(): void;
64
+ /**
65
+ * Focuses the fist {@link #_focusables} in the actions.
66
+ */
67
+ focus(): void;
68
+ /**
69
+ * Creates a button view.
70
+ *
71
+ * @param label The button label.
72
+ * @param icon The button icon.
73
+ * @param eventName An event name that the `ButtonView#execute` event will be delegated to.
74
+ * @param additionalLabel An additional label outside the button.
75
+ * @returns The button view instance.
76
+ */
77
+ private _createButton;
78
+ /**
79
+ * Creates a bookmark name preview label.
80
+ *
81
+ * @returns The label view instance.
82
+ */
83
+ private _createBookmarkPreviewView;
84
+ }
85
+ /**
86
+ * Fired when the {@link ~BookmarkActionsView#editButtonView} is clicked.
87
+ *
88
+ * @eventName ~BookmarkActionsView#edit
89
+ */
90
+ export type EditEvent = {
91
+ name: 'edit';
92
+ args: [];
93
+ };
94
+ /**
95
+ * Fired when the {@link ~BookmarkActionsView#removeButtonView} is clicked.
96
+ *
97
+ * @eventName ~BookmarkActionsView#remove
98
+ */
99
+ export type RemoveEvent = {
100
+ name: 'remove';
101
+ args: [];
102
+ };
@@ -0,0 +1,154 @@
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 bookmark/ui/bookmarkactionsview
7
+ */
8
+ import { LabelView, ButtonView, View, ViewCollection, FocusCycler } from 'ckeditor5/src/ui.js';
9
+ import { FocusTracker, KeystrokeHandler } from 'ckeditor5/src/utils.js';
10
+ import { icons } from 'ckeditor5/src/core.js';
11
+ // eslint-disable-next-line ckeditor5-rules/ckeditor-imports
12
+ import '@ckeditor/ckeditor5-ui/theme/components/responsive-form/responsiveform.css';
13
+ import '../../theme/bookmarkactions.css';
14
+ /**
15
+ * The bookmark actions view class. This view displays the bookmark preview, allows
16
+ * removing or editing the bookmark.
17
+ */
18
+ export default class BookmarkActionsView extends View {
19
+ /**
20
+ * @inheritDoc
21
+ */
22
+ constructor(locale) {
23
+ super(locale);
24
+ /**
25
+ * Tracks information about DOM focus in the actions.
26
+ */
27
+ this.focusTracker = new FocusTracker();
28
+ /**
29
+ * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
30
+ */
31
+ this.keystrokes = new KeystrokeHandler();
32
+ /**
33
+ * A collection of views that can be focused in the view.
34
+ */
35
+ this._focusables = new ViewCollection();
36
+ const t = locale.t;
37
+ this.bookmarkPreviewView = this._createBookmarkPreviewView();
38
+ this.removeButtonView = this._createButton(t('Remove bookmark'), icons.remove, 'remove', this.bookmarkPreviewView);
39
+ this.editButtonView = this._createButton(t('Edit bookmark'), icons.pencil, 'edit', this.bookmarkPreviewView);
40
+ this.set('id', undefined);
41
+ this._focusCycler = new FocusCycler({
42
+ focusables: this._focusables,
43
+ focusTracker: this.focusTracker,
44
+ keystrokeHandler: this.keystrokes,
45
+ actions: {
46
+ // Navigate fields backwards using the Shift + Tab keystroke.
47
+ focusPrevious: 'shift + tab',
48
+ // Navigate fields forwards using the Tab key.
49
+ focusNext: 'tab'
50
+ }
51
+ });
52
+ this.setTemplate({
53
+ tag: 'div',
54
+ attributes: {
55
+ class: [
56
+ 'ck',
57
+ 'ck-bookmark-actions',
58
+ 'ck-responsive-form'
59
+ ],
60
+ // https://github.com/ckeditor/ckeditor5-link/issues/90
61
+ tabindex: '-1'
62
+ },
63
+ children: [
64
+ this.bookmarkPreviewView,
65
+ this.editButtonView,
66
+ this.removeButtonView
67
+ ]
68
+ });
69
+ }
70
+ /**
71
+ * @inheritDoc
72
+ */
73
+ render() {
74
+ super.render();
75
+ const childViews = [
76
+ this.editButtonView,
77
+ this.removeButtonView
78
+ ];
79
+ childViews.forEach(v => {
80
+ // Register the view as focusable.
81
+ this._focusables.add(v);
82
+ // Register the view in the focus tracker.
83
+ this.focusTracker.add(v.element);
84
+ });
85
+ // Start listening for the keystrokes coming from #element.
86
+ this.keystrokes.listenTo(this.element);
87
+ }
88
+ /**
89
+ * @inheritDoc
90
+ */
91
+ destroy() {
92
+ super.destroy();
93
+ this.focusTracker.destroy();
94
+ this.keystrokes.destroy();
95
+ }
96
+ /**
97
+ * Focuses the fist {@link #_focusables} in the actions.
98
+ */
99
+ focus() {
100
+ this._focusCycler.focusFirst();
101
+ }
102
+ /**
103
+ * Creates a button view.
104
+ *
105
+ * @param label The button label.
106
+ * @param icon The button icon.
107
+ * @param eventName An event name that the `ButtonView#execute` event will be delegated to.
108
+ * @param additionalLabel An additional label outside the button.
109
+ * @returns The button view instance.
110
+ */
111
+ _createButton(label, icon, eventName, additionalLabel) {
112
+ const button = new ButtonView(this.locale);
113
+ button.set({
114
+ label,
115
+ icon,
116
+ tooltip: true
117
+ });
118
+ button.delegate('execute').to(this, eventName);
119
+ // Since button label `id` is bound to the `ariaLabelledBy` property
120
+ // we need to modify this binding to include only the first ID token
121
+ // as this button will be labeled by multiple labels.
122
+ button.labelView.unbind('id');
123
+ button.labelView.bind('id').to(button, 'ariaLabelledBy', ariaLabelledBy => {
124
+ return getFirstToken(ariaLabelledBy);
125
+ });
126
+ button.ariaLabelledBy = `${button.ariaLabelledBy} ${additionalLabel.id}`;
127
+ return button;
128
+ }
129
+ /**
130
+ * Creates a bookmark name preview label.
131
+ *
132
+ * @returns The label view instance.
133
+ */
134
+ _createBookmarkPreviewView() {
135
+ const label = new LabelView(this.locale);
136
+ label.extendTemplate({
137
+ attributes: {
138
+ class: [
139
+ 'ck',
140
+ 'ck-bookmark-actions__preview'
141
+ ]
142
+ }
143
+ });
144
+ // Bind label text with the bookmark ID.
145
+ label.bind('text').to(this, 'id');
146
+ return label;
147
+ }
148
+ }
149
+ /**
150
+ * Returns the first token from space separated token list.
151
+ */
152
+ function getFirstToken(tokenList) {
153
+ return tokenList.split(' ')[0];
154
+ }
@@ -0,0 +1,118 @@
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 bookmark/ui/bookmarkformview
7
+ */
8
+ import { ButtonView, LabeledFieldView, View, ViewCollection, type InputTextView } from 'ckeditor5/src/ui.js';
9
+ import { FocusTracker, KeystrokeHandler, type Locale } from 'ckeditor5/src/utils.js';
10
+ import '@ckeditor/ckeditor5-ui/theme/components/responsive-form/responsiveform.css';
11
+ import '../../theme/bookmarkform.css';
12
+ /**
13
+ * The bookmark form view controller class.
14
+ *
15
+ * See {@link module:bookmark/ui/bookmarkformview~BookmarkFormView}.
16
+ */
17
+ export default class BookmarkFormView extends View {
18
+ /**
19
+ * Tracks information about DOM focus in the form.
20
+ */
21
+ readonly focusTracker: FocusTracker;
22
+ /**
23
+ * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
24
+ */
25
+ readonly keystrokes: KeystrokeHandler;
26
+ /**
27
+ * The ID input view.
28
+ */
29
+ idInputView: LabeledFieldView<InputTextView>;
30
+ /**
31
+ * The Submit button view.
32
+ */
33
+ buttonView: ButtonView;
34
+ /**
35
+ * A collection of form child views in the form.
36
+ */
37
+ readonly children: ViewCollection;
38
+ /**
39
+ * An array of form validators used by {@link #isValid}.
40
+ */
41
+ private readonly _validators;
42
+ /**
43
+ * A collection of views that can be focused in the form.
44
+ */
45
+ private readonly _focusables;
46
+ /**
47
+ * Helps cycling over {@link #_focusables} in the form.
48
+ */
49
+ private readonly _focusCycler;
50
+ /**
51
+ * Creates an instance of the {@link module:bookmark/ui/bookmarkformview~BookmarkFormView} class.
52
+ *
53
+ * Also see {@link #render}.
54
+ *
55
+ * @param locale The localization services instance.
56
+ * @param validators Form validators used by {@link #isValid}.
57
+ */
58
+ constructor(locale: Locale, validators: Array<BookmarkFormValidatorCallback>);
59
+ /**
60
+ * @inheritDoc
61
+ */
62
+ render(): void;
63
+ /**
64
+ * @inheritDoc
65
+ */
66
+ destroy(): void;
67
+ /**
68
+ * Focuses the fist {@link #_focusables} in the form.
69
+ */
70
+ focus(): void;
71
+ /**
72
+ * Validates the form and returns `false` when some fields are invalid.
73
+ */
74
+ isValid(): boolean;
75
+ /**
76
+ * Cleans up the supplementary error and information text of the {@link #idInputView}
77
+ * bringing them back to the state when the form has been displayed for the first time.
78
+ *
79
+ * See {@link #isValid}.
80
+ */
81
+ resetFormStatus(): void;
82
+ /**
83
+ * Creates header and form view.
84
+ */
85
+ private _createViewChildren;
86
+ /**
87
+ * Creates form content view with input and button.
88
+ */
89
+ private _createFormContentView;
90
+ /**
91
+ * Creates a labeled input view.
92
+ *
93
+ * @returns Labeled field view instance.
94
+ */
95
+ private _createIdInput;
96
+ /**
97
+ * Creates a button view.
98
+ *
99
+ * @param label The button label.
100
+ * @param className The additional button CSS class name.
101
+ * @returns The button view instance.
102
+ */
103
+ private _createButton;
104
+ /**
105
+ * The native DOM `value` of the {@link #idInputView} element.
106
+ *
107
+ * **Note**: Do not confuse it with the {@link module:ui/inputtext/inputtextview~InputTextView#value}
108
+ * which works one way only and may not represent the actual state of the component in the DOM.
109
+ */
110
+ get id(): string | null;
111
+ }
112
+ /**
113
+ * Callback used by {@link ~BookmarkFormView} to check if passed form value is valid.
114
+ *
115
+ * If `undefined` is returned, it is assumed that the form value is correct and there is no error.
116
+ * If string is returned, it is assumed that the form value is incorrect and the returned string is displayed in the error label
117
+ */
118
+ export type BookmarkFormValidatorCallback = (form: BookmarkFormView) => string | undefined;
@@ -0,0 +1,203 @@
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 bookmark/ui/bookmarkformview
7
+ */
8
+ import { ButtonView, FocusCycler, LabeledFieldView, View, ViewCollection, createLabeledInputText, submitHandler, FormHeaderView } from 'ckeditor5/src/ui.js';
9
+ import { FocusTracker, KeystrokeHandler } from 'ckeditor5/src/utils.js';
10
+ // See: #8833.
11
+ // eslint-disable-next-line ckeditor5-rules/ckeditor-imports
12
+ import '@ckeditor/ckeditor5-ui/theme/components/responsive-form/responsiveform.css';
13
+ import '../../theme/bookmarkform.css';
14
+ /**
15
+ * The bookmark form view controller class.
16
+ *
17
+ * See {@link module:bookmark/ui/bookmarkformview~BookmarkFormView}.
18
+ */
19
+ export default class BookmarkFormView extends View {
20
+ /**
21
+ * Creates an instance of the {@link module:bookmark/ui/bookmarkformview~BookmarkFormView} class.
22
+ *
23
+ * Also see {@link #render}.
24
+ *
25
+ * @param locale The localization services instance.
26
+ * @param validators Form validators used by {@link #isValid}.
27
+ */
28
+ constructor(locale, validators) {
29
+ super(locale);
30
+ /**
31
+ * Tracks information about DOM focus in the form.
32
+ */
33
+ this.focusTracker = new FocusTracker();
34
+ /**
35
+ * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
36
+ */
37
+ this.keystrokes = new KeystrokeHandler();
38
+ /**
39
+ * A collection of views that can be focused in the form.
40
+ */
41
+ this._focusables = new ViewCollection();
42
+ const t = locale.t;
43
+ this._validators = validators;
44
+ this.idInputView = this._createIdInput();
45
+ this.buttonView = this._createButton(t('Insert'), 'ck-button-action ck-button-bold');
46
+ this.buttonView.type = 'submit';
47
+ this.children = this._createViewChildren();
48
+ this._focusCycler = new FocusCycler({
49
+ focusables: this._focusables,
50
+ focusTracker: this.focusTracker,
51
+ keystrokeHandler: this.keystrokes,
52
+ actions: {
53
+ // Navigate form fields backwards using the Shift + Tab keystroke.
54
+ focusPrevious: 'shift + tab',
55
+ // Navigate form fields forwards using the Tab key.
56
+ focusNext: 'tab'
57
+ }
58
+ });
59
+ const classList = ['ck', 'ck-bookmark-view'];
60
+ this.setTemplate({
61
+ tag: 'form',
62
+ attributes: {
63
+ class: classList,
64
+ // https://github.com/ckeditor/ckeditor5-link/issues/90
65
+ tabindex: '-1'
66
+ },
67
+ children: this.children
68
+ });
69
+ }
70
+ /**
71
+ * @inheritDoc
72
+ */
73
+ render() {
74
+ super.render();
75
+ submitHandler({
76
+ view: this
77
+ });
78
+ const childViews = [
79
+ this.idInputView,
80
+ this.buttonView
81
+ ];
82
+ childViews.forEach(v => {
83
+ // Register the view as focusable.
84
+ this._focusables.add(v);
85
+ // Register the view in the focus tracker.
86
+ this.focusTracker.add(v.element);
87
+ });
88
+ // Start listening for the keystrokes coming from #element.
89
+ this.keystrokes.listenTo(this.element);
90
+ }
91
+ /**
92
+ * @inheritDoc
93
+ */
94
+ destroy() {
95
+ super.destroy();
96
+ this.focusTracker.destroy();
97
+ this.keystrokes.destroy();
98
+ }
99
+ /**
100
+ * Focuses the fist {@link #_focusables} in the form.
101
+ */
102
+ focus() {
103
+ this._focusCycler.focusFirst();
104
+ }
105
+ /**
106
+ * Validates the form and returns `false` when some fields are invalid.
107
+ */
108
+ isValid() {
109
+ this.resetFormStatus();
110
+ for (const validator of this._validators) {
111
+ const errorText = validator(this);
112
+ // One error per field is enough.
113
+ if (errorText) {
114
+ // Apply updated error.
115
+ this.idInputView.errorText = errorText;
116
+ return false;
117
+ }
118
+ }
119
+ return true;
120
+ }
121
+ /**
122
+ * Cleans up the supplementary error and information text of the {@link #idInputView}
123
+ * bringing them back to the state when the form has been displayed for the first time.
124
+ *
125
+ * See {@link #isValid}.
126
+ */
127
+ resetFormStatus() {
128
+ this.idInputView.errorText = null;
129
+ }
130
+ /**
131
+ * Creates header and form view.
132
+ */
133
+ _createViewChildren() {
134
+ const children = this.createCollection();
135
+ const t = this.t;
136
+ children.add(new FormHeaderView(this.locale, { label: t('Bookmark') }));
137
+ children.add(this._createFormContentView());
138
+ return children;
139
+ }
140
+ /**
141
+ * Creates form content view with input and button.
142
+ */
143
+ _createFormContentView() {
144
+ const view = new View(this.locale);
145
+ const children = this.createCollection();
146
+ const classList = ['ck', 'ck-bookmark-form', 'ck-responsive-form'];
147
+ children.add(this.idInputView);
148
+ children.add(this.buttonView);
149
+ view.setTemplate({
150
+ tag: 'div',
151
+ attributes: {
152
+ class: classList
153
+ },
154
+ children
155
+ });
156
+ return view;
157
+ }
158
+ /**
159
+ * Creates a labeled input view.
160
+ *
161
+ * @returns Labeled field view instance.
162
+ */
163
+ _createIdInput() {
164
+ const t = this.locale.t;
165
+ const labeledInput = new LabeledFieldView(this.locale, createLabeledInputText);
166
+ labeledInput.label = t('Bookmark name');
167
+ labeledInput.infoText = t('Enter the bookmark name without spaces.');
168
+ return labeledInput;
169
+ }
170
+ /**
171
+ * Creates a button view.
172
+ *
173
+ * @param label The button label.
174
+ * @param className The additional button CSS class name.
175
+ * @returns The button view instance.
176
+ */
177
+ _createButton(label, className) {
178
+ const button = new ButtonView(this.locale);
179
+ button.set({
180
+ label,
181
+ withText: true
182
+ });
183
+ button.extendTemplate({
184
+ attributes: {
185
+ class: className
186
+ }
187
+ });
188
+ return button;
189
+ }
190
+ /**
191
+ * The native DOM `value` of the {@link #idInputView} element.
192
+ *
193
+ * **Note**: Do not confuse it with the {@link module:ui/inputtext/inputtextview~InputTextView#value}
194
+ * which works one way only and may not represent the actual state of the component in the DOM.
195
+ */
196
+ get id() {
197
+ const { element } = this.idInputView.fieldView;
198
+ if (!element) {
199
+ return null;
200
+ }
201
+ return element.value.trim();
202
+ }
203
+ }