@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.
- package/CHANGELOG.md +4 -0
- package/LICENSE.md +17 -0
- package/README.md +26 -0
- package/build/bookmark.js +4 -0
- package/ckeditor5-metadata.json +24 -0
- package/dist/augmentation.d.ts +28 -0
- package/dist/bookmark.d.ts +34 -0
- package/dist/bookmarkconfig.d.ts +52 -0
- package/dist/bookmarkediting.d.ts +55 -0
- package/dist/bookmarkui.d.ts +170 -0
- package/dist/index-content.css +4 -0
- package/dist/index-editor.css +150 -0
- package/dist/index.css +195 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +1322 -0
- package/dist/index.js.map +1 -0
- package/dist/insertbookmarkcommand.d.ts +42 -0
- package/dist/ui/bookmarkactionsview.d.ts +106 -0
- package/dist/ui/bookmarkformview.d.ts +122 -0
- package/dist/updatebookmarkcommand.d.ts +46 -0
- package/dist/utils.d.ts +15 -0
- package/lang/contexts.json +13 -0
- package/package.json +43 -0
- package/src/augmentation.d.ts +24 -0
- package/src/augmentation.js +5 -0
- package/src/bookmark.d.ts +30 -0
- package/src/bookmark.js +36 -0
- package/src/bookmarkconfig.d.ts +48 -0
- package/src/bookmarkconfig.js +5 -0
- package/src/bookmarkediting.d.ts +51 -0
- package/src/bookmarkediting.js +212 -0
- package/src/bookmarkui.d.ts +166 -0
- package/src/bookmarkui.js +583 -0
- package/src/index.d.ts +14 -0
- package/src/index.js +13 -0
- package/src/insertbookmarkcommand.d.ts +38 -0
- package/src/insertbookmarkcommand.js +113 -0
- package/src/ui/bookmarkactionsview.d.ts +102 -0
- package/src/ui/bookmarkactionsview.js +154 -0
- package/src/ui/bookmarkformview.d.ts +118 -0
- package/src/ui/bookmarkformview.js +203 -0
- package/src/updatebookmarkcommand.d.ts +42 -0
- package/src/updatebookmarkcommand.js +75 -0
- package/src/utils.d.ts +11 -0
- package/src/utils.js +19 -0
- package/theme/bookmark.css +50 -0
- package/theme/bookmarkactions.css +44 -0
- 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
|
+
}
|