-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
/
linkformview.js
362 lines (307 loc) · 9.24 KB
/
linkformview.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
/**
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @module link/ui/linkformview
*/
import {
ButtonView,
FocusCycler,
LabeledFieldView,
SwitchButtonView,
View,
ViewCollection,
createLabeledInputText,
injectCssTransitionDisabler,
submitHandler
} from 'ckeditor5/src/ui';
import { FocusTracker, KeystrokeHandler } from 'ckeditor5/src/utils';
import { icons } from 'ckeditor5/src/core';
// See: #8833.
// eslint-disable-next-line ckeditor5-rules/ckeditor-imports
import '@ckeditor/ckeditor5-ui/theme/components/responsive-form/responsiveform.css';
import '../../theme/linkform.css';
/**
* The link form view controller class.
*
* See {@link module:link/ui/linkformview~LinkFormView}.
*
* @extends module:ui/view~View
*/
export default class LinkFormView extends View {
/**
* Creates an instance of the {@link module:link/ui/linkformview~LinkFormView} class.
*
* Also see {@link #render}.
*
* @param {module:utils/locale~Locale} [locale] The localization services instance.
* @param {module:link/linkcommand~LinkCommand} linkCommand Reference to {@link module:link/linkcommand~LinkCommand}.
* @param {String} [protocol] A value of a protocol to be displayed in the input's placeholder.
*/
constructor( locale, linkCommand ) {
super( locale );
const t = locale.t;
/**
* Tracks information about DOM focus in the form.
*
* @readonly
* @member {module:utils/focustracker~FocusTracker}
*/
this.focusTracker = new FocusTracker();
/**
* An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
*
* @readonly
* @member {module:utils/keystrokehandler~KeystrokeHandler}
*/
this.keystrokes = new KeystrokeHandler();
/**
* The URL input view.
*
* @member {module:ui/labeledfield/labeledfieldview~LabeledFieldView}
*/
this.urlInputView = this._createUrlInput();
/**
* The Save button view.
*
* @member {module:ui/button/buttonview~ButtonView}
*/
this.saveButtonView = this._createButton( t( 'Save' ), icons.check, 'ck-button-save' );
this.saveButtonView.type = 'submit';
/**
* The Cancel button view.
*
* @member {module:ui/button/buttonview~ButtonView}
*/
this.cancelButtonView = this._createButton( t( 'Cancel' ), icons.cancel, 'ck-button-cancel', 'cancel' );
/**
* A collection of {@link module:ui/button/switchbuttonview~SwitchButtonView},
* which corresponds to {@link module:link/linkcommand~LinkCommand#manualDecorators manual decorators}
* configured in the editor.
*
* @private
* @readonly
* @type {module:ui/viewcollection~ViewCollection}
*/
this._manualDecoratorSwitches = this._createManualDecoratorSwitches( linkCommand );
/**
* A collection of child views in the form.
*
* @readonly
* @type {module:ui/viewcollection~ViewCollection}
*/
this.children = this._createFormChildren( linkCommand.manualDecorators );
/**
* A collection of views that can be focused in the form.
*
* @readonly
* @protected
* @member {module:ui/viewcollection~ViewCollection}
*/
this._focusables = new ViewCollection();
/**
* Helps cycling over {@link #_focusables} in the form.
*
* @readonly
* @protected
* @member {module:ui/focuscycler~FocusCycler}
*/
this._focusCycler = new FocusCycler( {
focusables: this._focusables,
focusTracker: this.focusTracker,
keystrokeHandler: this.keystrokes,
actions: {
// Navigate form fields backwards using the Shift + Tab keystroke.
focusPrevious: 'shift + tab',
// Navigate form fields forwards using the Tab key.
focusNext: 'tab'
}
} );
const classList = [ 'ck', 'ck-link-form', 'ck-responsive-form' ];
if ( linkCommand.manualDecorators.length ) {
classList.push( 'ck-link-form_layout-vertical', 'ck-vertical-form' );
}
this.setTemplate( {
tag: 'form',
attributes: {
class: classList,
// https://github.com/ckeditor/ckeditor5-link/issues/90
tabindex: '-1'
},
children: this.children
} );
injectCssTransitionDisabler( this );
}
/**
* Obtains the state of the {@link module:ui/button/switchbuttonview~SwitchButtonView switch buttons} representing
* {@link module:link/linkcommand~LinkCommand#manualDecorators manual link decorators}
* in the {@link module:link/ui/linkformview~LinkFormView}.
*
* @returns {Object.<String,Boolean>} Key-value pairs, where the key is the name of the decorator and the value is
* its state.
*/
getDecoratorSwitchesState() {
return Array.from( this._manualDecoratorSwitches ).reduce( ( accumulator, switchButton ) => {
accumulator[ switchButton.name ] = switchButton.isOn;
return accumulator;
}, {} );
}
/**
* @inheritDoc
*/
render() {
super.render();
submitHandler( {
view: this
} );
const childViews = [
this.urlInputView,
...this._manualDecoratorSwitches,
this.saveButtonView,
this.cancelButtonView
];
childViews.forEach( v => {
// Register the view as focusable.
this._focusables.add( v );
// Register the view in the focus tracker.
this.focusTracker.add( v.element );
} );
// Start listening for the keystrokes coming from #element.
this.keystrokes.listenTo( this.element );
}
/**
* @inheritDoc
*/
destroy() {
super.destroy();
this.focusTracker.destroy();
this.keystrokes.destroy();
}
/**
* Focuses the fist {@link #_focusables} in the form.
*/
focus() {
this._focusCycler.focusFirst();
}
/**
* Creates a labeled input view.
*
* @private
* @returns {module:ui/labeledfield/labeledfieldview~LabeledFieldView} Labeled field view instance.
*/
_createUrlInput() {
const t = this.locale.t;
const labeledInput = new LabeledFieldView( this.locale, createLabeledInputText );
labeledInput.label = t( 'Link URL' );
return labeledInput;
}
/**
* Creates a button view.
*
* @private
* @param {String} label The button label.
* @param {String} icon The button icon.
* @param {String} className The additional button CSS class name.
* @param {String} [eventName] An event name that the `ButtonView#execute` event will be delegated to.
* @returns {module:ui/button/buttonview~ButtonView} The button view instance.
*/
_createButton( label, icon, className, eventName ) {
const button = new ButtonView( this.locale );
button.set( {
label,
icon,
tooltip: true
} );
button.extendTemplate( {
attributes: {
class: className
}
} );
if ( eventName ) {
button.delegate( 'execute' ).to( this, eventName );
}
return button;
}
/**
* Populates {@link module:ui/viewcollection~ViewCollection} of {@link module:ui/button/switchbuttonview~SwitchButtonView}
* made based on {@link module:link/linkcommand~LinkCommand#manualDecorators}.
*
* @private
* @param {module:link/linkcommand~LinkCommand} linkCommand A reference to the link command.
* @returns {module:ui/viewcollection~ViewCollection} of switch buttons.
*/
_createManualDecoratorSwitches( linkCommand ) {
const switches = this.createCollection();
for ( const manualDecorator of linkCommand.manualDecorators ) {
const switchButton = new SwitchButtonView( this.locale );
switchButton.set( {
name: manualDecorator.id,
label: manualDecorator.label,
withText: true
} );
switchButton.bind( 'isOn' ).toMany( [ manualDecorator, linkCommand ], 'value', ( decoratorValue, commandValue ) => {
return commandValue === undefined && decoratorValue === undefined ? manualDecorator.defaultValue : decoratorValue;
} );
switchButton.on( 'execute', () => {
manualDecorator.set( 'value', !switchButton.isOn );
} );
switches.add( switchButton );
}
return switches;
}
/**
* Populates the {@link #children} collection of the form.
*
* If {@link module:link/linkcommand~LinkCommand#manualDecorators manual decorators} are configured in the editor, it creates an
* additional `View` wrapping all {@link #_manualDecoratorSwitches} switch buttons corresponding
* to these decorators.
*
* @private
* @param {module:utils/collection~Collection} manualDecorators A reference to
* the collection of manual decorators stored in the link command.
* @returns {module:ui/viewcollection~ViewCollection} The children of link form view.
*/
_createFormChildren( manualDecorators ) {
const children = this.createCollection();
children.add( this.urlInputView );
if ( manualDecorators.length ) {
const additionalButtonsView = new View();
additionalButtonsView.setTemplate( {
tag: 'ul',
children: this._manualDecoratorSwitches.map( switchButton => ( {
tag: 'li',
children: [ switchButton ],
attributes: {
class: [
'ck',
'ck-list__item'
]
}
} ) ),
attributes: {
class: [
'ck',
'ck-reset',
'ck-list'
]
}
} );
children.add( additionalButtonsView );
}
children.add( this.saveButtonView );
children.add( this.cancelButtonView );
return children;
}
}
/**
* Fired when the form view is submitted (when one of the children triggered the submit event),
* for example with a click on {@link #saveButtonView}.
*
* @event submit
*/
/**
* Fired when the form view is canceled, for example with a click on {@link #cancelButtonView}.
*
* @event cancel
*/