1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | import { Plugin } from 'ckeditor5/src/core';
|
9 | import { ButtonView, createDropdown, addToolbarToDropdown, SplitButtonView } from 'ckeditor5/src/ui';
|
10 | import { isObject, identity } from 'lodash-es';
|
11 | import ImageStyleEditing from './imagestyleediting';
|
12 | import utils from './utils';
|
13 | import '../../theme/imagestyle.css';
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 | export default class ImageStyleUI extends Plugin {
|
22 | |
23 |
|
24 |
|
25 | static get requires() {
|
26 | return [ImageStyleEditing];
|
27 | }
|
28 | |
29 |
|
30 |
|
31 | static get pluginName() {
|
32 | return 'ImageStyleUI';
|
33 | }
|
34 | |
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 | get localizedDefaultStylesTitles() {
|
50 | const t = this.editor.t;
|
51 | return {
|
52 | 'Wrap text': t('Wrap text'),
|
53 | 'Break text': t('Break text'),
|
54 | 'In line': t('In line'),
|
55 | 'Full size image': t('Full size image'),
|
56 | 'Side image': t('Side image'),
|
57 | 'Left aligned image': t('Left aligned image'),
|
58 | 'Centered image': t('Centered image'),
|
59 | 'Right aligned image': t('Right aligned image')
|
60 | };
|
61 | }
|
62 | |
63 |
|
64 |
|
65 | init() {
|
66 | const plugins = this.editor.plugins;
|
67 | const toolbarConfig = this.editor.config.get('image.toolbar') || [];
|
68 | const imageStyleEditing = plugins.get('ImageStyleEditing');
|
69 | const definedStyles = translateStyles(imageStyleEditing.normalizedStyles, this.localizedDefaultStylesTitles);
|
70 | for (const styleConfig of definedStyles) {
|
71 | this._createButton(styleConfig);
|
72 | }
|
73 | const definedDropdowns = translateStyles([
|
74 | ...toolbarConfig.filter(isObject),
|
75 | ...utils.getDefaultDropdownDefinitions(plugins)
|
76 | ], this.localizedDefaultStylesTitles);
|
77 | for (const dropdownConfig of definedDropdowns) {
|
78 | this._createDropdown(dropdownConfig, definedStyles);
|
79 | }
|
80 | }
|
81 | |
82 |
|
83 |
|
84 | _createDropdown(dropdownConfig, definedStyles) {
|
85 | const factory = this.editor.ui.componentFactory;
|
86 | factory.add(dropdownConfig.name, locale => {
|
87 | let defaultButton;
|
88 | const { defaultItem, items, title } = dropdownConfig;
|
89 | const buttonViews = items
|
90 | .filter(itemName => definedStyles.find(({ name }) => getUIComponentName(name) === itemName))
|
91 | .map(buttonName => {
|
92 | const button = factory.create(buttonName);
|
93 | if (buttonName === defaultItem) {
|
94 | defaultButton = button;
|
95 | }
|
96 | return button;
|
97 | });
|
98 | if (items.length !== buttonViews.length) {
|
99 | utils.warnInvalidStyle({ dropdown: dropdownConfig });
|
100 | }
|
101 | const dropdownView = createDropdown(locale, SplitButtonView);
|
102 | const splitButtonView = dropdownView.buttonView;
|
103 | const splitButtonViewArrow = splitButtonView.arrowView;
|
104 | addToolbarToDropdown(dropdownView, buttonViews, { enableActiveItemFocusOnDropdownOpen: true });
|
105 | splitButtonView.set({
|
106 | label: getDropdownButtonTitle(title, defaultButton.label),
|
107 | class: null,
|
108 | tooltip: true
|
109 | });
|
110 | splitButtonViewArrow.unbind('label');
|
111 | splitButtonViewArrow.set({
|
112 | label: title
|
113 | });
|
114 | splitButtonView.bind('icon').toMany(buttonViews, 'isOn', (...areOn) => {
|
115 | const index = areOn.findIndex(identity);
|
116 | return (index < 0) ? defaultButton.icon : buttonViews[index].icon;
|
117 | });
|
118 | splitButtonView.bind('label').toMany(buttonViews, 'isOn', (...areOn) => {
|
119 | const index = areOn.findIndex(identity);
|
120 | return getDropdownButtonTitle(title, (index < 0) ? defaultButton.label : buttonViews[index].label);
|
121 | });
|
122 | splitButtonView.bind('isOn').toMany(buttonViews, 'isOn', (...areOn) => areOn.some(identity));
|
123 | splitButtonView.bind('class')
|
124 | .toMany(buttonViews, 'isOn', (...areOn) => areOn.some(identity) ? 'ck-splitbutton_flatten' : undefined);
|
125 | splitButtonView.on('execute', () => {
|
126 | if (!buttonViews.some(({ isOn }) => isOn)) {
|
127 | defaultButton.fire('execute');
|
128 | }
|
129 | else {
|
130 | dropdownView.isOpen = !dropdownView.isOpen;
|
131 | }
|
132 | });
|
133 | dropdownView.bind('isEnabled')
|
134 | .toMany(buttonViews, 'isEnabled', (...areEnabled) => areEnabled.some(identity));
|
135 |
|
136 |
|
137 | this.listenTo(dropdownView, 'execute', () => {
|
138 | this.editor.editing.view.focus();
|
139 | });
|
140 | return dropdownView;
|
141 | });
|
142 | }
|
143 | |
144 |
|
145 |
|
146 | _createButton(buttonConfig) {
|
147 | const buttonName = buttonConfig.name;
|
148 | this.editor.ui.componentFactory.add(getUIComponentName(buttonName), locale => {
|
149 | const command = this.editor.commands.get('imageStyle');
|
150 | const view = new ButtonView(locale);
|
151 | view.set({
|
152 | label: buttonConfig.title,
|
153 | icon: buttonConfig.icon,
|
154 | tooltip: true,
|
155 | isToggleable: true
|
156 | });
|
157 | view.bind('isEnabled').to(command, 'isEnabled');
|
158 | view.bind('isOn').to(command, 'value', value => value === buttonName);
|
159 | view.on('execute', this._executeCommand.bind(this, buttonName));
|
160 | return view;
|
161 | });
|
162 | }
|
163 | _executeCommand(name) {
|
164 | this.editor.execute('imageStyle', { value: name });
|
165 | this.editor.editing.view.focus();
|
166 | }
|
167 | }
|
168 |
|
169 |
|
170 |
|
171 | function translateStyles(styles, titles) {
|
172 | for (const style of styles) {
|
173 |
|
174 |
|
175 | if (titles[style.title]) {
|
176 | style.title = titles[style.title];
|
177 | }
|
178 | }
|
179 | return styles;
|
180 | }
|
181 |
|
182 |
|
183 |
|
184 | function getUIComponentName(name) {
|
185 | return `imageStyle:${name}`;
|
186 | }
|
187 |
|
188 |
|
189 |
|
190 | function getDropdownButtonTitle(dropdownTitle, buttonTitle) {
|
191 | return (dropdownTitle ? dropdownTitle + ': ' : '') + buttonTitle;
|
192 | }
|