@ckeditor/ckeditor5-style 34.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +17 -0
- package/README.md +21 -0
- package/build/style.js +5 -0
- package/build/style.js.map +1 -0
- package/ckeditor5-metadata.json +25 -0
- package/lang/contexts.json +6 -0
- package/lang/translations/en.po +33 -0
- package/package.json +76 -0
- package/src/index.js +12 -0
- package/src/style.js +119 -0
- package/src/stylecommand.js +267 -0
- package/src/styleediting.js +161 -0
- package/src/styleui.js +99 -0
- package/src/ui/stylegridbuttonview.js +118 -0
- package/src/ui/stylegridview.js +101 -0
- package/src/ui/stylegroupview.js +73 -0
- package/src/ui/stylepanelview.js +201 -0
- package/src/utils.js +50 -0
- package/theme/style.css +4 -0
- package/theme/stylegrid.css +28 -0
- package/theme/stylegroup.css +4 -0
- package/theme/stylepanel.css +5 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2022, 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
|
+
/**
|
|
7
|
+
* @module style/stylecommand
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Command } from 'ckeditor5/src/core';
|
|
11
|
+
import { logWarning, first } from 'ckeditor5/src/utils';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Style command.
|
|
15
|
+
*
|
|
16
|
+
* Applies and removes styles from selection and elements.
|
|
17
|
+
*
|
|
18
|
+
* @extends module:core/command~Command
|
|
19
|
+
*/
|
|
20
|
+
export default class StyleCommand extends Command {
|
|
21
|
+
constructor( editor, styles ) {
|
|
22
|
+
super( editor );
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Set of currently applied styles on current selection.
|
|
26
|
+
*
|
|
27
|
+
* Names of styles correspond to the `name` property of
|
|
28
|
+
* {@link module:style/style~StyleDefinition configured definitions}.
|
|
29
|
+
*
|
|
30
|
+
* @observable
|
|
31
|
+
* @readonly
|
|
32
|
+
* @member {Boolean|String} #value
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Styles object. Helps in getting styles definitions by
|
|
37
|
+
* class name, style name and model element name.
|
|
38
|
+
*
|
|
39
|
+
* @private
|
|
40
|
+
* @readonly
|
|
41
|
+
* @member {module:style/styleediting~Styles}
|
|
42
|
+
*/
|
|
43
|
+
this.styles = styles;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Names of enabled styles (styles that can be applied to the current selection).
|
|
47
|
+
*
|
|
48
|
+
* Names of enabled styles correspond to the `name` property of
|
|
49
|
+
* {@link module:style/style~StyleDefinition configured definitions}.
|
|
50
|
+
*
|
|
51
|
+
* @readonly
|
|
52
|
+
* @observable
|
|
53
|
+
* @member {Array.<String>} #enabledStyles
|
|
54
|
+
*/
|
|
55
|
+
this.set( 'enabledStyles', [] );
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Refresh state.
|
|
59
|
+
*/
|
|
60
|
+
this.refresh();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @inheritDoc
|
|
65
|
+
*/
|
|
66
|
+
refresh() {
|
|
67
|
+
let value = [];
|
|
68
|
+
const editor = this.editor;
|
|
69
|
+
const selection = editor.model.document.selection;
|
|
70
|
+
const block = first( selection.getSelectedBlocks() );
|
|
71
|
+
|
|
72
|
+
this.enabledStyles = [];
|
|
73
|
+
|
|
74
|
+
if ( !block || !editor.model.schema.isObject( block ) ) {
|
|
75
|
+
value = this._prepareNewInlineElementValue( value, selection );
|
|
76
|
+
this.enabledStyles = this.styles.getInlineElementsNames();
|
|
77
|
+
|
|
78
|
+
if ( block ) {
|
|
79
|
+
value = this._prepareNewBlockElementValue( value, block );
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this.isEnabled = this.enabledStyles.length > 0;
|
|
84
|
+
this.value = this.isEnabled ? value : [];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Executes the command — applies the style classes to the selection or removes it from the selection.
|
|
89
|
+
*
|
|
90
|
+
* If the command value already contains the requested style, it will remove the style classes. Otherwise, it will set it.
|
|
91
|
+
*
|
|
92
|
+
* The execution result differs, depending on the {@link module:engine/model/document~Document#selection} and the
|
|
93
|
+
* style type (inline or block):
|
|
94
|
+
*
|
|
95
|
+
* * When applying inline styles:
|
|
96
|
+
* * If the selection is on a range, the command applies the style classes to all nodes in that range.
|
|
97
|
+
* * If the selection is collapsed in a non-empty node, the command applies the style classes to the
|
|
98
|
+
* {@link module:engine/model/document~Document#selection} itself (note that typed characters copy style classes from the selection).
|
|
99
|
+
*
|
|
100
|
+
* * When applying block styles:
|
|
101
|
+
* * If the selection is on a range, the command applies the style classes to the nearest block parent element.
|
|
102
|
+
*
|
|
103
|
+
* * When selection is set on a widget object:
|
|
104
|
+
* * Do nothing. Widgets are not yet supported by the style command.
|
|
105
|
+
*
|
|
106
|
+
* @fires execute
|
|
107
|
+
* @param {String} styleName Style name matching the one defined in the config.
|
|
108
|
+
*/
|
|
109
|
+
execute( styleName ) {
|
|
110
|
+
if ( !this.enabledStyles.includes( styleName ) ) {
|
|
111
|
+
/**
|
|
112
|
+
* Style command can be executed only on a correct style name.
|
|
113
|
+
* This warning may be caused by passing name that it not specified in any of the
|
|
114
|
+
* definitions in the styles config, when trying to apply style that is not allowed
|
|
115
|
+
* on given element or passing class name instead of the style name.
|
|
116
|
+
*
|
|
117
|
+
* @error style-command-executed-with-incorrect-style-name
|
|
118
|
+
*/
|
|
119
|
+
logWarning( 'style-command-executed-with-incorrect-style-name' );
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const editor = this.editor;
|
|
124
|
+
const model = editor.model;
|
|
125
|
+
const doc = model.document;
|
|
126
|
+
const selection = doc.selection;
|
|
127
|
+
|
|
128
|
+
const selectedBlockElement = first( selection.getSelectedBlocks() );
|
|
129
|
+
const definition = this.styles.getDefinitionsByName( styleName );
|
|
130
|
+
|
|
131
|
+
if ( selectedBlockElement && definition.isBlock ) {
|
|
132
|
+
this._handleStyleUpdate( definition, selectedBlockElement );
|
|
133
|
+
} else {
|
|
134
|
+
this._handleStyleUpdate( definition, selection );
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Adds or removes classes to element, range or selection.
|
|
140
|
+
*
|
|
141
|
+
* @private
|
|
142
|
+
* @param {Object} definition Style definition object.
|
|
143
|
+
* @param {module:engine/model/selection~Selectable} selectable Selection, range or element to update the style on.
|
|
144
|
+
*/
|
|
145
|
+
_handleStyleUpdate( definition, selectable ) {
|
|
146
|
+
const { name, element, classes } = definition;
|
|
147
|
+
const htmlSupport = this.editor.plugins.get( 'GeneralHtmlSupport' );
|
|
148
|
+
|
|
149
|
+
if ( this.value.includes( name ) ) {
|
|
150
|
+
htmlSupport.removeModelHtmlClass( element, classes, selectable );
|
|
151
|
+
} else {
|
|
152
|
+
htmlSupport.addModelHtmlClass( element, classes, selectable );
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Returns inline element value.
|
|
158
|
+
*
|
|
159
|
+
* @private
|
|
160
|
+
* @param {Array} value
|
|
161
|
+
* @param {module:engine/model/selection~Selection} selection
|
|
162
|
+
*/
|
|
163
|
+
_prepareNewInlineElementValue( value, selection ) {
|
|
164
|
+
let newValue = [ ...value ];
|
|
165
|
+
|
|
166
|
+
const attributes = selection.getAttributes();
|
|
167
|
+
|
|
168
|
+
for ( const [ attribute ] of attributes ) {
|
|
169
|
+
newValue = [ ...newValue, ...this._getAttributeValue( attribute ) ];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return newValue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Returns element value and sets enabled styles.
|
|
177
|
+
*
|
|
178
|
+
* @private
|
|
179
|
+
* @param {Array} value
|
|
180
|
+
* @param {Object|null} element
|
|
181
|
+
* @return {Array} Current block element styles value.
|
|
182
|
+
*/
|
|
183
|
+
_prepareNewBlockElementValue( value, element ) {
|
|
184
|
+
const availableDefinitions = this.styles.getDefinitionsByElementName( element.name );
|
|
185
|
+
|
|
186
|
+
if ( availableDefinitions ) {
|
|
187
|
+
const blockStyleNames = availableDefinitions.map( ( { name } ) => name );
|
|
188
|
+
this.enabledStyles = [ ...this.enabledStyles, ...blockStyleNames ];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return [ ...value, ...this._getAttributeValue( 'htmlAttributes' ) ];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Get classes attribute value.
|
|
196
|
+
*
|
|
197
|
+
* @private
|
|
198
|
+
* @param {String} attribute
|
|
199
|
+
*/
|
|
200
|
+
_getAttributeValue( attribute ) {
|
|
201
|
+
const value = [];
|
|
202
|
+
const classes = attribute === 'htmlAttributes' ?
|
|
203
|
+
this._getValueFromBlockElement() :
|
|
204
|
+
this._getValueFromFirstAllowedNode( attribute );
|
|
205
|
+
|
|
206
|
+
for ( const htmlClass of classes ) {
|
|
207
|
+
const { name } = this.styles.getDefinitionsByClassName( htmlClass ) || {};
|
|
208
|
+
|
|
209
|
+
value.push( name );
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return value;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Gets classes from currently selected block element.
|
|
217
|
+
*
|
|
218
|
+
* @private
|
|
219
|
+
*/
|
|
220
|
+
_getValueFromBlockElement() {
|
|
221
|
+
const selection = this.editor.model.document.selection;
|
|
222
|
+
const block = first( selection.getSelectedBlocks() );
|
|
223
|
+
const attributes = block.getAttribute( 'htmlAttributes' );
|
|
224
|
+
|
|
225
|
+
if ( attributes ) {
|
|
226
|
+
return attributes.classes;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return [];
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Gets classes from currently selected text element.
|
|
234
|
+
*
|
|
235
|
+
* @private
|
|
236
|
+
* @param {String} attributeName Text attribute name.
|
|
237
|
+
*/
|
|
238
|
+
_getValueFromFirstAllowedNode( attributeName ) {
|
|
239
|
+
const model = this.editor.model;
|
|
240
|
+
const schema = model.schema;
|
|
241
|
+
const selection = model.document.selection;
|
|
242
|
+
|
|
243
|
+
if ( selection.isCollapsed ) {
|
|
244
|
+
/* istanbul ignore next */
|
|
245
|
+
const { classes } = selection.getAttribute( attributeName ) || {};
|
|
246
|
+
|
|
247
|
+
/* istanbul ignore next */
|
|
248
|
+
return classes || [];
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
for ( const range of selection.getRanges() ) {
|
|
252
|
+
for ( const item of range.getItems() ) {
|
|
253
|
+
/* istanbul ignore else */
|
|
254
|
+
if ( schema.checkAttribute( item, attributeName ) ) {
|
|
255
|
+
/* istanbul ignore next */
|
|
256
|
+
const { classes } = item.getAttribute( attributeName ) || {};
|
|
257
|
+
|
|
258
|
+
/* istanbul ignore next */
|
|
259
|
+
return classes || [];
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/* istanbul ignore next */
|
|
265
|
+
return [];
|
|
266
|
+
}
|
|
267
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2022, 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
|
+
/**
|
|
7
|
+
* @module style/styleediting
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Plugin } from 'ckeditor5/src/core';
|
|
11
|
+
import { normalizeConfig } from './utils';
|
|
12
|
+
|
|
13
|
+
import StyleCommand from './stylecommand';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The style engine feature.
|
|
17
|
+
*
|
|
18
|
+
* It configures the {@glink features/general-html-support General HTML Support feature} based on
|
|
19
|
+
* {@link module:style/style~StyleConfig#definitions configured style definitions} and introduces the
|
|
20
|
+
* {@link module:style/stylecommand~StyleCommand style command} that applies styles to the content of the document.
|
|
21
|
+
*
|
|
22
|
+
* @extends module:core/plugin~Plugin
|
|
23
|
+
*/
|
|
24
|
+
export default class StyleEditing extends Plugin {
|
|
25
|
+
/**
|
|
26
|
+
* @inheritDoc
|
|
27
|
+
*/
|
|
28
|
+
static get pluginName() {
|
|
29
|
+
return 'StyleEditing';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @inheritDoc
|
|
34
|
+
*/
|
|
35
|
+
static get requires() {
|
|
36
|
+
return [ 'GeneralHtmlSupport' ];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @inheritDoc
|
|
41
|
+
*/
|
|
42
|
+
init() {
|
|
43
|
+
const editor = this.editor;
|
|
44
|
+
const dataSchema = editor.plugins.get( 'DataSchema' );
|
|
45
|
+
const normalizedStyleDefinitions = normalizeConfig( dataSchema, editor.config.get( 'style.definitions' ) );
|
|
46
|
+
const styles = new Styles( normalizedStyleDefinitions );
|
|
47
|
+
|
|
48
|
+
editor.commands.add( 'style', new StyleCommand( editor, styles ) );
|
|
49
|
+
|
|
50
|
+
this._configureGHSDataFilter( normalizedStyleDefinitions );
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* This is where the styles feature configures the GHS feature. This method translates normalized
|
|
55
|
+
* {@link module:style/style~StyleDefinition style definitions} to {@link module:engine/view/matcher~MatcherPattern matcher patterns}
|
|
56
|
+
* and feeds them to the GHS {@link module:html-support/datafilter~DataFilter} plugin.
|
|
57
|
+
*
|
|
58
|
+
* @private
|
|
59
|
+
* @param {Object} normalizedStyleDefinitions
|
|
60
|
+
*/
|
|
61
|
+
_configureGHSDataFilter( { block: blockDefinitions, inline: inlineDefinitions } ) {
|
|
62
|
+
const ghsDataFilter = this.editor.plugins.get( 'DataFilter' );
|
|
63
|
+
|
|
64
|
+
ghsDataFilter.loadAllowedConfig( blockDefinitions.map( normalizedStyleDefinitionToMatcherPattern ) );
|
|
65
|
+
ghsDataFilter.loadAllowedConfig( inlineDefinitions.map( normalizedStyleDefinitionToMatcherPattern ) );
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The helper class storing various mappings based on
|
|
71
|
+
* {@link module:style/style~StyleConfig#definitions configured style definitions}. Used internally by
|
|
72
|
+
* {@link module:style/stylecommand~StyleCommand}.
|
|
73
|
+
*
|
|
74
|
+
* @private
|
|
75
|
+
*/
|
|
76
|
+
class Styles {
|
|
77
|
+
/**
|
|
78
|
+
* @param {Object} An object with normalized style definitions grouped into `block` and `inline` categories (arrays).
|
|
79
|
+
*/
|
|
80
|
+
constructor( styleDefinitions ) {
|
|
81
|
+
this.styleTypes = [ 'inline', 'block' ];
|
|
82
|
+
this.styleDefinitions = styleDefinitions;
|
|
83
|
+
this.elementToDefinition = new Map();
|
|
84
|
+
this.classToDefinition = new Map();
|
|
85
|
+
this.nameToDefinition = new Map();
|
|
86
|
+
|
|
87
|
+
this._prepareDefinitionsMapping();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Populates various maps to simplify getting config definitions
|
|
92
|
+
* by model name,class name and style name.
|
|
93
|
+
*
|
|
94
|
+
* @private
|
|
95
|
+
*/
|
|
96
|
+
_prepareDefinitionsMapping() {
|
|
97
|
+
for ( const type of this.styleTypes ) {
|
|
98
|
+
for ( const { modelElements, name, element, classes, isBlock } of this.styleDefinitions[ type ] ) {
|
|
99
|
+
for ( const modelElement of modelElements ) {
|
|
100
|
+
const currentValue = this.elementToDefinition.get( modelElement ) || [];
|
|
101
|
+
const newValue = [ ...currentValue, { name, element, classes } ];
|
|
102
|
+
this.elementToDefinition.set( modelElement, newValue );
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
this.classToDefinition.set( classes.join( ' ' ), { name, element, classes } );
|
|
106
|
+
this.nameToDefinition.set( name, { name, element, classes, isBlock } );
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Returns all inline definitions elements names.
|
|
113
|
+
*
|
|
114
|
+
* @protected
|
|
115
|
+
* @return {Array.<String>} Inline elements names.
|
|
116
|
+
*/
|
|
117
|
+
getInlineElementsNames() {
|
|
118
|
+
return this.styleDefinitions.inline.map( ( { name } ) => name );
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Returns the style config definitions by the model element name.
|
|
123
|
+
*
|
|
124
|
+
* @protected
|
|
125
|
+
* @return {Object} Style config definition.
|
|
126
|
+
*/
|
|
127
|
+
getDefinitionsByElementName( elementName ) {
|
|
128
|
+
return this.elementToDefinition.get( elementName );
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Returns the style config definitions by the style name.
|
|
133
|
+
*
|
|
134
|
+
* @protected
|
|
135
|
+
* @return {Object} Style config definition.
|
|
136
|
+
*/
|
|
137
|
+
getDefinitionsByName( name ) {
|
|
138
|
+
return this.nameToDefinition.get( name );
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Returns the style config definitions by the style name.
|
|
143
|
+
*
|
|
144
|
+
* @protected
|
|
145
|
+
* @return {Object} Style config definition.
|
|
146
|
+
*/
|
|
147
|
+
getDefinitionsByClassName( className ) {
|
|
148
|
+
return this.classToDefinition.get( className );
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Translates a normalized style definition to a view matcher pattern.
|
|
153
|
+
//
|
|
154
|
+
// @param {Object} definition A normalized style definition.
|
|
155
|
+
// @returns {module:engine/view/matcher~MatcherPattern}
|
|
156
|
+
function normalizedStyleDefinitionToMatcherPattern( { element, classes } ) {
|
|
157
|
+
return {
|
|
158
|
+
name: element,
|
|
159
|
+
classes
|
|
160
|
+
};
|
|
161
|
+
}
|
package/src/styleui.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2022, 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
|
+
/**
|
|
7
|
+
* @module style/styleui
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Plugin } from 'ckeditor5/src/core';
|
|
11
|
+
import { createDropdown } from 'ckeditor5/src/ui';
|
|
12
|
+
|
|
13
|
+
import StylePanelView from './ui/stylepanelview';
|
|
14
|
+
import { normalizeConfig } from './utils';
|
|
15
|
+
|
|
16
|
+
import '../theme/style.css';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The UI plugin of the style feature .
|
|
20
|
+
*
|
|
21
|
+
* It registers the `'style'` UI dropdown in the editor's {@link module:ui/componentfactory~ComponentFactory component factory}
|
|
22
|
+
* that displays a grid of styles and allows changing styles of the content.
|
|
23
|
+
*
|
|
24
|
+
* @extends module:core/plugin~Plugin
|
|
25
|
+
*/
|
|
26
|
+
export default class StyleUI extends Plugin {
|
|
27
|
+
/**
|
|
28
|
+
* @inheritDoc
|
|
29
|
+
*/
|
|
30
|
+
static get pluginName() {
|
|
31
|
+
return 'StyleUI';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @inheritDoc
|
|
36
|
+
*/
|
|
37
|
+
init() {
|
|
38
|
+
const editor = this.editor;
|
|
39
|
+
const dataSchema = editor.plugins.get( 'DataSchema' );
|
|
40
|
+
const normalizedStyleDefinitions = normalizeConfig( dataSchema, editor.config.get( 'style.definitions' ) );
|
|
41
|
+
|
|
42
|
+
// Add the dropdown fo the component factory.
|
|
43
|
+
editor.ui.componentFactory.add( 'style', locale => {
|
|
44
|
+
const t = locale.t;
|
|
45
|
+
const dropdown = createDropdown( locale );
|
|
46
|
+
const panelView = new StylePanelView( locale, normalizedStyleDefinitions );
|
|
47
|
+
const styleCommand = editor.commands.get( 'style' );
|
|
48
|
+
|
|
49
|
+
// The entire dropdown will be disabled together with the command (e.g. when the editor goes read-only).
|
|
50
|
+
dropdown.bind( 'isEnabled' ).to( styleCommand );
|
|
51
|
+
|
|
52
|
+
// Put the styles panel is the dropdown.
|
|
53
|
+
dropdown.panelView.children.add( panelView );
|
|
54
|
+
|
|
55
|
+
// This dropdown has no icon. It displays text label depending on the selection.
|
|
56
|
+
dropdown.buttonView.withText = true;
|
|
57
|
+
|
|
58
|
+
// The label of the dropdown is dynamic and depends on how many styles are active at a time.
|
|
59
|
+
dropdown.buttonView.bind( 'label' ).to( styleCommand, 'value', value => {
|
|
60
|
+
if ( value.length > 1 ) {
|
|
61
|
+
return t( 'Multiple styles' );
|
|
62
|
+
} else if ( value.length === 1 ) {
|
|
63
|
+
return value[ 0 ];
|
|
64
|
+
} else {
|
|
65
|
+
return t( 'Styles' );
|
|
66
|
+
}
|
|
67
|
+
} );
|
|
68
|
+
|
|
69
|
+
// The dropdown has a static CSS class for easy customization. There's another CSS class
|
|
70
|
+
// that gets displayed when multiple styles are active at a time allowing visual customization of
|
|
71
|
+
// the label.
|
|
72
|
+
dropdown.bind( 'class' ).to( styleCommand, 'value', value => {
|
|
73
|
+
const classes = [
|
|
74
|
+
'ck-style-dropdown'
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
if ( value.length > 1 ) {
|
|
78
|
+
classes.push( 'ck-style-dropdown_multiple-active' );
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return classes.join( ' ' );
|
|
82
|
+
} );
|
|
83
|
+
|
|
84
|
+
// Close the dropdown when a style is selected in the styles panel.
|
|
85
|
+
panelView.delegate( 'execute' ).to( dropdown );
|
|
86
|
+
|
|
87
|
+
// Execute the command when a style is selected in the styles panel.
|
|
88
|
+
panelView.on( 'execute', evt => {
|
|
89
|
+
editor.execute( 'style', evt.source.styleDefinition.name );
|
|
90
|
+
} );
|
|
91
|
+
|
|
92
|
+
// Bind the state of the styles panel to the command.
|
|
93
|
+
panelView.bind( 'activeStyles' ).to( styleCommand, 'value' );
|
|
94
|
+
panelView.bind( 'enabledStyles' ).to( styleCommand, 'enabledStyles' );
|
|
95
|
+
|
|
96
|
+
return dropdown;
|
|
97
|
+
} );
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2022, 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
|
+
/**
|
|
7
|
+
* @module style/ui/stylegridbuttonview
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
ButtonView,
|
|
12
|
+
View
|
|
13
|
+
} from 'ckeditor5/src/ui';
|
|
14
|
+
|
|
15
|
+
// These are intermediate element names that can't be rendered as style preview because they don't make sense standalone.
|
|
16
|
+
const NON_PREVIEWABLE_ELEMENT_NAMES = [
|
|
17
|
+
'caption', 'colgroup', 'dd', 'dt', 'figcaption', 'legend', 'li', 'optgroup', 'option', 'rp',
|
|
18
|
+
'rt', 'summary', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr'
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A class representing an individual button (style) in the grid. Renders a rich preview of the style.
|
|
23
|
+
*
|
|
24
|
+
* @protected
|
|
25
|
+
* @extends {module:ui/button/buttonview~ButtonView}
|
|
26
|
+
*/
|
|
27
|
+
export default class StyleGridButtonView extends ButtonView {
|
|
28
|
+
/**
|
|
29
|
+
* Creates an instance of the {@link module:style/ui/stylegridbuttonview~StyleGridButtonView} class.
|
|
30
|
+
*
|
|
31
|
+
* @param {module:utils/locale~Locale} locale The localization services instance.
|
|
32
|
+
* @param {module:style/style~StyleDefinition} styleDefinition Definition of the style.
|
|
33
|
+
*/
|
|
34
|
+
constructor( locale, styleDefinition ) {
|
|
35
|
+
super( locale );
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Definition of the style the button will apply when executed.
|
|
39
|
+
*
|
|
40
|
+
* @readonly
|
|
41
|
+
* @member {module:style/style~StyleDefinition} #styleDefinition
|
|
42
|
+
*/
|
|
43
|
+
this.styleDefinition = styleDefinition;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The view rendering the preview of the style.
|
|
47
|
+
*
|
|
48
|
+
* @protected
|
|
49
|
+
* @readonly
|
|
50
|
+
* @member {module:ui/view~View} #previewView
|
|
51
|
+
*/
|
|
52
|
+
this.previewView = this._createPreview();
|
|
53
|
+
|
|
54
|
+
this.set( {
|
|
55
|
+
label: styleDefinition.name,
|
|
56
|
+
class: 'ck-style-grid__button',
|
|
57
|
+
withText: true
|
|
58
|
+
} );
|
|
59
|
+
|
|
60
|
+
this.extendTemplate( {
|
|
61
|
+
attributes: {
|
|
62
|
+
role: 'option'
|
|
63
|
+
}
|
|
64
|
+
} );
|
|
65
|
+
|
|
66
|
+
this.children.add( this.previewView, 0 );
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Creates the view representing the preview of the style.
|
|
71
|
+
*
|
|
72
|
+
* @private
|
|
73
|
+
* @returns {module:ui/view~View}
|
|
74
|
+
*/
|
|
75
|
+
_createPreview() {
|
|
76
|
+
const { element, classes } = this.styleDefinition;
|
|
77
|
+
const previewView = new View( this.locale );
|
|
78
|
+
|
|
79
|
+
previewView.setTemplate( {
|
|
80
|
+
tag: 'div',
|
|
81
|
+
|
|
82
|
+
attributes: {
|
|
83
|
+
class: [
|
|
84
|
+
'ck',
|
|
85
|
+
'ck-reset_all-excluded',
|
|
86
|
+
'ck-style-grid__button__preview',
|
|
87
|
+
'ck-content'
|
|
88
|
+
]
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
children: [
|
|
92
|
+
{
|
|
93
|
+
tag: this._isPreviewable( element ) ? element : 'div',
|
|
94
|
+
attributes: {
|
|
95
|
+
class: classes
|
|
96
|
+
},
|
|
97
|
+
children: [
|
|
98
|
+
{ text: 'AaBbCcDdEeFfGgHhIiJj' }
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
} );
|
|
103
|
+
|
|
104
|
+
return previewView;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Decides whether an element should be created in the preview or a substitute `<div>` should
|
|
109
|
+
* be used instead. This avoids previewing a standalone `<td>`, `<li>`, etc. without a parent.
|
|
110
|
+
*
|
|
111
|
+
* @private
|
|
112
|
+
* @param {module:style/style~StyleDefinition} styleDefinition
|
|
113
|
+
* @returns {Boolean} `true` when the element can be rendered. `false` otherwise.
|
|
114
|
+
*/
|
|
115
|
+
_isPreviewable( elementName ) {
|
|
116
|
+
return !NON_PREVIEWABLE_ELEMENT_NAMES.includes( elementName );
|
|
117
|
+
}
|
|
118
|
+
}
|