1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | import { View, InputTextView, ButtonView, createDropdown, ColorGridView, FocusCycler, ViewCollection } from 'ckeditor5/src/ui';
|
9 | import { icons } from 'ckeditor5/src/core';
|
10 | import { FocusTracker, KeystrokeHandler } from 'ckeditor5/src/utils';
|
11 | import '../../theme/colorinput.css';
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | export default class ColorInputView extends View {
|
19 | |
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 | constructor(locale, options) {
|
30 | super(locale);
|
31 | this.set('value', '');
|
32 | this.set('isReadOnly', false);
|
33 | this.set('isFocused', false);
|
34 | this.set('isEmpty', true);
|
35 | this.options = options;
|
36 | this.focusTracker = new FocusTracker();
|
37 | this._focusables = new ViewCollection();
|
38 | this.dropdownView = this._createDropdownView();
|
39 | this.inputView = this._createInputTextView();
|
40 | this.keystrokes = new KeystrokeHandler();
|
41 | this._stillTyping = false;
|
42 | this._focusCycler = new FocusCycler({
|
43 | focusables: this._focusables,
|
44 | focusTracker: this.focusTracker,
|
45 | keystrokeHandler: this.keystrokes,
|
46 | actions: {
|
47 |
|
48 | focusPrevious: 'shift + tab',
|
49 |
|
50 | focusNext: 'tab'
|
51 | }
|
52 | });
|
53 | this.setTemplate({
|
54 | tag: 'div',
|
55 | attributes: {
|
56 | class: [
|
57 | 'ck',
|
58 | 'ck-input-color'
|
59 | ]
|
60 | },
|
61 | children: [
|
62 | this.dropdownView,
|
63 | this.inputView
|
64 | ]
|
65 | });
|
66 | this.on('change:value', (evt, name, inputValue) => this._setInputValue(inputValue));
|
67 | }
|
68 | |
69 |
|
70 |
|
71 | render() {
|
72 | super.render();
|
73 |
|
74 | this.keystrokes.listenTo(this.dropdownView.panelView.element);
|
75 | }
|
76 | |
77 |
|
78 |
|
79 | focus() {
|
80 | this.inputView.focus();
|
81 | }
|
82 | |
83 |
|
84 |
|
85 | destroy() {
|
86 | super.destroy();
|
87 | this.focusTracker.destroy();
|
88 | this.keystrokes.destroy();
|
89 | }
|
90 | |
91 |
|
92 |
|
93 | _createDropdownView() {
|
94 | const locale = this.locale;
|
95 | const t = locale.t;
|
96 | const bind = this.bindTemplate;
|
97 | const colorGrid = this._createColorGrid(locale);
|
98 | const dropdown = createDropdown(locale);
|
99 | const colorPreview = new View();
|
100 | const removeColorButton = this._createRemoveColorButton();
|
101 | colorPreview.setTemplate({
|
102 | tag: 'span',
|
103 | attributes: {
|
104 | class: [
|
105 | 'ck',
|
106 | 'ck-input-color__button__preview'
|
107 | ],
|
108 | style: {
|
109 | backgroundColor: bind.to('value')
|
110 | }
|
111 | },
|
112 | children: [{
|
113 | tag: 'span',
|
114 | attributes: {
|
115 | class: [
|
116 | 'ck',
|
117 | 'ck-input-color__button__preview__no-color-indicator',
|
118 | bind.if('value', 'ck-hidden', value => value != '')
|
119 | ]
|
120 | }
|
121 | }]
|
122 | });
|
123 | dropdown.buttonView.extendTemplate({
|
124 | attributes: {
|
125 | class: 'ck-input-color__button'
|
126 | }
|
127 | });
|
128 | dropdown.buttonView.children.add(colorPreview);
|
129 | dropdown.buttonView.label = t('Color picker');
|
130 | dropdown.buttonView.tooltip = true;
|
131 | dropdown.panelPosition = locale.uiLanguageDirection === 'rtl' ? 'se' : 'sw';
|
132 | dropdown.panelView.children.add(removeColorButton);
|
133 | dropdown.panelView.children.add(colorGrid);
|
134 | dropdown.bind('isEnabled').to(this, 'isReadOnly', value => !value);
|
135 | this._focusables.add(removeColorButton);
|
136 | this._focusables.add(colorGrid);
|
137 | this.focusTracker.add(removeColorButton.element);
|
138 | this.focusTracker.add(colorGrid.element);
|
139 | return dropdown;
|
140 | }
|
141 | |
142 |
|
143 |
|
144 |
|
145 |
|
146 | _createInputTextView() {
|
147 | const locale = this.locale;
|
148 | const inputView = new InputTextView(locale);
|
149 | inputView.extendTemplate({
|
150 | on: {
|
151 | blur: inputView.bindTemplate.to('blur')
|
152 | }
|
153 | });
|
154 | inputView.value = this.value;
|
155 | inputView.bind('isReadOnly', 'hasError').to(this);
|
156 | this.bind('isFocused', 'isEmpty').to(inputView);
|
157 | inputView.on('input', () => {
|
158 | const inputValue = inputView.element.value;
|
159 |
|
160 | const mappedColor = this.options.colorDefinitions.find(def => inputValue === def.label);
|
161 | this._stillTyping = true;
|
162 | this.value = mappedColor && mappedColor.color || inputValue;
|
163 | });
|
164 | inputView.on('blur', () => {
|
165 | this._stillTyping = false;
|
166 | this._setInputValue(inputView.element.value);
|
167 | });
|
168 | inputView.delegate('input').to(this);
|
169 | return inputView;
|
170 | }
|
171 | |
172 |
|
173 |
|
174 | _createRemoveColorButton() {
|
175 | const locale = this.locale;
|
176 | const t = locale.t;
|
177 | const removeColorButton = new ButtonView(locale);
|
178 | const defaultColor = this.options.defaultColorValue || '';
|
179 | const removeColorButtonLabel = defaultColor ? t('Restore default') : t('Remove color');
|
180 | removeColorButton.class = 'ck-input-color__remove-color';
|
181 | removeColorButton.withText = true;
|
182 | removeColorButton.icon = icons.eraser;
|
183 | removeColorButton.label = removeColorButtonLabel;
|
184 | removeColorButton.on('execute', () => {
|
185 | this.value = defaultColor;
|
186 | this.dropdownView.isOpen = false;
|
187 | this.fire('input');
|
188 | });
|
189 | return removeColorButton;
|
190 | }
|
191 | |
192 |
|
193 |
|
194 | _createColorGrid(locale) {
|
195 | const colorGrid = new ColorGridView(locale, {
|
196 | colorDefinitions: this.options.colorDefinitions,
|
197 | columns: this.options.columns
|
198 | });
|
199 | colorGrid.on('execute', (evtData, data) => {
|
200 | this.value = data.value;
|
201 | this.dropdownView.isOpen = false;
|
202 | this.fire('input');
|
203 | });
|
204 | colorGrid.bind('selectedColor').to(this, 'value');
|
205 | return colorGrid;
|
206 | }
|
207 | |
208 |
|
209 |
|
210 |
|
211 |
|
212 |
|
213 |
|
214 |
|
215 |
|
216 |
|
217 |
|
218 | _setInputValue(inputValue) {
|
219 | if (!this._stillTyping) {
|
220 | const normalizedInputValue = normalizeColor(inputValue);
|
221 |
|
222 | const mappedColor = this.options.colorDefinitions.find(def => normalizedInputValue === normalizeColor(def.color));
|
223 | if (mappedColor) {
|
224 | this.inputView.value = mappedColor.label;
|
225 | }
|
226 | else {
|
227 | this.inputView.value = inputValue || '';
|
228 | }
|
229 | }
|
230 | }
|
231 | }
|
232 |
|
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 |
|
239 |
|
240 | function normalizeColor(colorString) {
|
241 | return colorString
|
242 |
|
243 | .replace(/([(,])\s+/g, '$1')
|
244 |
|
245 | .replace(/^\s+|\s+(?=[),\s]|$)/g, '')
|
246 |
|
247 | .replace(/,|\s/g, ' ');
|
248 | }
|