UNPKG

8.63 kBJavaScriptView Raw
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 link/ui/linkformview
7 */
8import { ButtonView, FocusCycler, LabeledFieldView, SwitchButtonView, View, ViewCollection, createLabeledInputText, submitHandler } from 'ckeditor5/src/ui';
9import { FocusTracker, KeystrokeHandler } from 'ckeditor5/src/utils';
10import { icons } from 'ckeditor5/src/core';
11// See: #8833.
12// eslint-disable-next-line ckeditor5-rules/ckeditor-imports
13import '@ckeditor/ckeditor5-ui/theme/components/responsive-form/responsiveform.css';
14import '../../theme/linkform.css';
15/**
16 * The link form view controller class.
17 *
18 * See {@link module:link/ui/linkformview~LinkFormView}.
19 */
20export default class LinkFormView extends View {
21 /**
22 * Creates an instance of the {@link module:link/ui/linkformview~LinkFormView} class.
23 *
24 * Also see {@link #render}.
25 *
26 * @param locale The localization services instance.
27 * @param linkCommand Reference to {@link module:link/linkcommand~LinkCommand}.
28 */
29 constructor(locale, linkCommand) {
30 super(locale);
31 /**
32 * Tracks information about DOM focus in the form.
33 */
34 this.focusTracker = new FocusTracker();
35 /**
36 * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
37 */
38 this.keystrokes = new KeystrokeHandler();
39 /**
40 * A collection of views that can be focused in the form.
41 */
42 this._focusables = new ViewCollection();
43 const t = locale.t;
44 this.urlInputView = this._createUrlInput();
45 this.saveButtonView = this._createButton(t('Save'), icons.check, 'ck-button-save');
46 this.saveButtonView.type = 'submit';
47 this.cancelButtonView = this._createButton(t('Cancel'), icons.cancel, 'ck-button-cancel', 'cancel');
48 this._manualDecoratorSwitches = this._createManualDecoratorSwitches(linkCommand);
49 this.children = this._createFormChildren(linkCommand.manualDecorators);
50 this._focusCycler = new FocusCycler({
51 focusables: this._focusables,
52 focusTracker: this.focusTracker,
53 keystrokeHandler: this.keystrokes,
54 actions: {
55 // Navigate form fields backwards using the Shift + Tab keystroke.
56 focusPrevious: 'shift + tab',
57 // Navigate form fields forwards using the Tab key.
58 focusNext: 'tab'
59 }
60 });
61 const classList = ['ck', 'ck-link-form', 'ck-responsive-form'];
62 if (linkCommand.manualDecorators.length) {
63 classList.push('ck-link-form_layout-vertical', 'ck-vertical-form');
64 }
65 this.setTemplate({
66 tag: 'form',
67 attributes: {
68 class: classList,
69 // https://github.com/ckeditor/ckeditor5-link/issues/90
70 tabindex: '-1'
71 },
72 children: this.children
73 });
74 }
75 /**
76 * Obtains the state of the {@link module:ui/button/switchbuttonview~SwitchButtonView switch buttons} representing
77 * {@link module:link/linkcommand~LinkCommand#manualDecorators manual link decorators}
78 * in the {@link module:link/ui/linkformview~LinkFormView}.
79 *
80 * @returns Key-value pairs, where the key is the name of the decorator and the value is its state.
81 */
82 getDecoratorSwitchesState() {
83 return Array
84 .from(this._manualDecoratorSwitches)
85 .reduce((accumulator, switchButton) => {
86 accumulator[switchButton.name] = switchButton.isOn;
87 return accumulator;
88 }, {});
89 }
90 /**
91 * @inheritDoc
92 */
93 render() {
94 super.render();
95 submitHandler({
96 view: this
97 });
98 const childViews = [
99 this.urlInputView,
100 ...this._manualDecoratorSwitches,
101 this.saveButtonView,
102 this.cancelButtonView
103 ];
104 childViews.forEach(v => {
105 // Register the view as focusable.
106 this._focusables.add(v);
107 // Register the view in the focus tracker.
108 this.focusTracker.add(v.element);
109 });
110 // Start listening for the keystrokes coming from #element.
111 this.keystrokes.listenTo(this.element);
112 }
113 /**
114 * @inheritDoc
115 */
116 destroy() {
117 super.destroy();
118 this.focusTracker.destroy();
119 this.keystrokes.destroy();
120 }
121 /**
122 * Focuses the fist {@link #_focusables} in the form.
123 */
124 focus() {
125 this._focusCycler.focusFirst();
126 }
127 /**
128 * Creates a labeled input view.
129 *
130 * @returns Labeled field view instance.
131 */
132 _createUrlInput() {
133 const t = this.locale.t;
134 const labeledInput = new LabeledFieldView(this.locale, createLabeledInputText);
135 labeledInput.label = t('Link URL');
136 return labeledInput;
137 }
138 /**
139 * Creates a button view.
140 *
141 * @param label The button label.
142 * @param icon The button icon.
143 * @param className The additional button CSS class name.
144 * @param eventName An event name that the `ButtonView#execute` event will be delegated to.
145 * @returns The button view instance.
146 */
147 _createButton(label, icon, className, eventName) {
148 const button = new ButtonView(this.locale);
149 button.set({
150 label,
151 icon,
152 tooltip: true
153 });
154 button.extendTemplate({
155 attributes: {
156 class: className
157 }
158 });
159 if (eventName) {
160 button.delegate('execute').to(this, eventName);
161 }
162 return button;
163 }
164 /**
165 * Populates {@link module:ui/viewcollection~ViewCollection} of {@link module:ui/button/switchbuttonview~SwitchButtonView}
166 * made based on {@link module:link/linkcommand~LinkCommand#manualDecorators}.
167 *
168 * @param linkCommand A reference to the link command.
169 * @returns ViewCollection of switch buttons.
170 */
171 _createManualDecoratorSwitches(linkCommand) {
172 const switches = this.createCollection();
173 for (const manualDecorator of linkCommand.manualDecorators) {
174 const switchButton = new SwitchButtonView(this.locale);
175 switchButton.set({
176 name: manualDecorator.id,
177 label: manualDecorator.label,
178 withText: true
179 });
180 switchButton.bind('isOn').toMany([manualDecorator, linkCommand], 'value', (decoratorValue, commandValue) => {
181 return commandValue === undefined && decoratorValue === undefined ? !!manualDecorator.defaultValue : !!decoratorValue;
182 });
183 switchButton.on('execute', () => {
184 manualDecorator.set('value', !switchButton.isOn);
185 });
186 switches.add(switchButton);
187 }
188 return switches;
189 }
190 /**
191 * Populates the {@link #children} collection of the form.
192 *
193 * If {@link module:link/linkcommand~LinkCommand#manualDecorators manual decorators} are configured in the editor, it creates an
194 * additional `View` wrapping all {@link #_manualDecoratorSwitches} switch buttons corresponding
195 * to these decorators.
196 *
197 * @param manualDecorators A reference to
198 * the collection of manual decorators stored in the link command.
199 * @returns The children of link form view.
200 */
201 _createFormChildren(manualDecorators) {
202 const children = this.createCollection();
203 children.add(this.urlInputView);
204 if (manualDecorators.length) {
205 const additionalButtonsView = new View();
206 additionalButtonsView.setTemplate({
207 tag: 'ul',
208 children: this._manualDecoratorSwitches.map(switchButton => ({
209 tag: 'li',
210 children: [switchButton],
211 attributes: {
212 class: [
213 'ck',
214 'ck-list__item'
215 ]
216 }
217 })),
218 attributes: {
219 class: [
220 'ck',
221 'ck-reset',
222 'ck-list'
223 ]
224 }
225 });
226 children.add(additionalButtonsView);
227 }
228 children.add(this.saveButtonView);
229 children.add(this.cancelButtonView);
230 return children;
231 }
232}