draftail-text-utils 0.1.7__py3-none-any.whl
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.
- draftail_text_utils/__init__.py +4 -0
- draftail_text_utils/apps.py +7 -0
- draftail_text_utils/conf.py +482 -0
- draftail_text_utils/migrations/__init__.py +0 -0
- draftail_text_utils/rich_text/__init__.py +0 -0
- draftail_text_utils/rich_text/base.py +267 -0
- draftail_text_utils/rich_text/font_family.py +61 -0
- draftail_text_utils/rich_text/font_size.py +94 -0
- draftail_text_utils/rich_text/highlight_color.py +99 -0
- draftail_text_utils/rich_text/text_alignment.py +70 -0
- draftail_text_utils/rich_text/text_color.py +95 -0
- draftail_text_utils/static/draftail_text_utils/css/draftail_text_utils.css +5 -0
- draftail_text_utils/static/draftail_text_utils/css/font_family.css +57 -0
- draftail_text_utils/static/draftail_text_utils/css/font_size.css +102 -0
- draftail_text_utils/static/draftail_text_utils/css/highlight_color.css +117 -0
- draftail_text_utils/static/draftail_text_utils/css/text_alignment.css +72 -0
- draftail_text_utils/static/draftail_text_utils/css/text_color.css +117 -0
- draftail_text_utils/static/draftail_text_utils/js/font_family.js +149 -0
- draftail_text_utils/static/draftail_text_utils/js/font_size.js +408 -0
- draftail_text_utils/static/draftail_text_utils/js/font_size_entity.js +39 -0
- draftail_text_utils/static/draftail_text_utils/js/highlight_color.js +350 -0
- draftail_text_utils/static/draftail_text_utils/js/highlight_color_entity.js +43 -0
- draftail_text_utils/static/draftail_text_utils/js/text_alignment.js +139 -0
- draftail_text_utils/static/draftail_text_utils/js/text_color.js +343 -0
- draftail_text_utils/static/draftail_text_utils/js/text_color_entity.js +42 -0
- draftail_text_utils/templates/draftail_text_utils/icons/align-center.svg +1 -0
- draftail_text_utils/templates/draftail_text_utils/icons/align-justify.svg +1 -0
- draftail_text_utils/templates/draftail_text_utils/icons/align-left.svg +1 -0
- draftail_text_utils/templates/draftail_text_utils/icons/align-right.svg +1 -0
- draftail_text_utils/templates/draftail_text_utils/icons/font.svg +1 -0
- draftail_text_utils/templates/draftail_text_utils/icons/highlighter.svg +1 -0
- draftail_text_utils/templates/draftail_text_utils/icons/palette.svg +1 -0
- draftail_text_utils/templates/draftail_text_utils/icons/text-height.svg +1 -0
- draftail_text_utils/templatetags/__init__.py +0 -0
- draftail_text_utils/templatetags/draftail_text_utils_tags.py +29 -0
- draftail_text_utils/wagtail_hooks.py +266 -0
- draftail_text_utils-0.1.7.dist-info/METADATA +63 -0
- draftail_text_utils-0.1.7.dist-info/RECORD +39 -0
- draftail_text_utils-0.1.7.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
// https://github.com/wagtail/draftail/blob/main/src/components/Toolbar/ToolbarButton.tsx
|
|
2
|
+
// export interface ToolbarButtonProps {
|
|
3
|
+
// name?: string;
|
|
4
|
+
// active?: boolean;
|
|
5
|
+
// label?: string | null;
|
|
6
|
+
// title?: string | null;
|
|
7
|
+
// icon?: IconProp | null;
|
|
8
|
+
// className?: string | null;
|
|
9
|
+
// tooltipDirection?: "up" | "down";
|
|
10
|
+
// onClick?: ((name: string) => void) | null;
|
|
11
|
+
// }
|
|
12
|
+
|
|
13
|
+
(function () {
|
|
14
|
+
var React = window.React;
|
|
15
|
+
var RichUtils = window.DraftJS.RichUtils;
|
|
16
|
+
var Modifier = window.DraftJS.Modifier;
|
|
17
|
+
var EditorState = window.DraftJS.EditorState;
|
|
18
|
+
var ToolbarButton = window.Draftail.ToolbarButton;
|
|
19
|
+
var data = window.draftailTextUtils || {
|
|
20
|
+
customFontSizes: {
|
|
21
|
+
PRESETS: [
|
|
22
|
+
8,
|
|
23
|
+
9,
|
|
24
|
+
10,
|
|
25
|
+
11,
|
|
26
|
+
12,
|
|
27
|
+
14,
|
|
28
|
+
this.state.inputValue || '',
|
|
29
|
+
18,
|
|
30
|
+
24,
|
|
31
|
+
30,
|
|
32
|
+
36,
|
|
33
|
+
48,
|
|
34
|
+
60,
|
|
35
|
+
72,
|
|
36
|
+
96,
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Build options from config
|
|
42
|
+
var options = data.customFontSizes.PRESETS.map(function (size) {
|
|
43
|
+
return {
|
|
44
|
+
label: String(size),
|
|
45
|
+
type: 'FONT_SIZE_' + size,
|
|
46
|
+
style: { fontSize: size + 'px' },
|
|
47
|
+
size: size,
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
var optMap = new Map(
|
|
52
|
+
options.map(function (opt) {
|
|
53
|
+
return [opt.type, opt];
|
|
54
|
+
}),
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
var FONT_SIZE_STEP = data.customFontSizes.STEP || 1;
|
|
58
|
+
var MIN_FONT_SIZE = data.customFontSizes.MIN || options[0].size;
|
|
59
|
+
var MAX_FONT_SIZE =
|
|
60
|
+
data.customFontSizes.MAX || options[options.length - 1].size;
|
|
61
|
+
|
|
62
|
+
var savedSelection = null;
|
|
63
|
+
|
|
64
|
+
const entityType = 'FONT_SIZE';
|
|
65
|
+
const controlType = 'font-size';
|
|
66
|
+
const control = JSON.parse(
|
|
67
|
+
document.getElementById(`draftail-plugin-control-${controlType}`)
|
|
68
|
+
.textContent,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
function removeFontSizeEntity(editorState) {
|
|
72
|
+
var selection = editorState.getSelection();
|
|
73
|
+
if (!selection.getHasFocus()) return editorState;
|
|
74
|
+
|
|
75
|
+
var contentWithoutEntity = Modifier.applyEntity(
|
|
76
|
+
editorState.getCurrentContent(),
|
|
77
|
+
selection,
|
|
78
|
+
null, // null removes entity
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
return EditorState.push(editorState, contentWithoutEntity, 'apply-entity');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function saveSelection(getEditorState) {
|
|
85
|
+
const selection = getEditorState().getSelection();
|
|
86
|
+
if (selection && !selection.isCollapsed()) {
|
|
87
|
+
savedSelection = selection;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function restoreSelection(editorState) {
|
|
92
|
+
if (savedSelection && !savedSelection.isCollapsed()) {
|
|
93
|
+
return EditorState.forceSelection(editorState, savedSelection);
|
|
94
|
+
}
|
|
95
|
+
return editorState;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Helper to find active size
|
|
99
|
+
function getActiveSize(editorState) {
|
|
100
|
+
var selection = editorState.getSelection();
|
|
101
|
+
if (!selection.getHasFocus()) return null;
|
|
102
|
+
|
|
103
|
+
var contentState = editorState.getCurrentContent();
|
|
104
|
+
var startKey = selection.getStartKey();
|
|
105
|
+
var startOffset = selection.getStartOffset();
|
|
106
|
+
var blockWithEntityAt = contentState.getBlockForKey(startKey);
|
|
107
|
+
|
|
108
|
+
// Check entity at cursor position
|
|
109
|
+
var entityKey = blockWithEntityAt.getEntityAt(startOffset);
|
|
110
|
+
if (entityKey) {
|
|
111
|
+
var entity = contentState.getEntity(entityKey);
|
|
112
|
+
if (entity.getType() === entityType) {
|
|
113
|
+
return entity.getData().size;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// If selection is collapsed, also check character before (so styling after typing works)
|
|
118
|
+
if (selection.isCollapsed() && startOffset > 0) {
|
|
119
|
+
entityKey = blockWithEntityAt.getEntityAt(startOffset - 1);
|
|
120
|
+
if (entityKey) {
|
|
121
|
+
var entity = contentState.getEntity(entityKey);
|
|
122
|
+
if (entity.getType() === entityType) {
|
|
123
|
+
return entity.getData().size;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// The control component
|
|
132
|
+
var FontSizeControl = class FontSizeControl extends React.Component {
|
|
133
|
+
constructor(props) {
|
|
134
|
+
super(props); // getEditorState, onChange
|
|
135
|
+
this.state = {
|
|
136
|
+
isDropdownOpen: false,
|
|
137
|
+
inputValue: '',
|
|
138
|
+
isInputFocused: false,
|
|
139
|
+
};
|
|
140
|
+
this.controlRef = React.createRef();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// lifecycle
|
|
144
|
+
componentDidMount() {
|
|
145
|
+
this.syncInputValue();
|
|
146
|
+
document.addEventListener('mousedown', this.handleClickOutside);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
componentWillUnmount() {
|
|
150
|
+
document.removeEventListener('mousedown', this.handleClickOutside);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
componentDidUpdate() {
|
|
154
|
+
this.syncInputValue(); // always runs, but guarded internally
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// helpers
|
|
158
|
+
getActiveSize() {
|
|
159
|
+
return getActiveSize(this.props.getEditorState());
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
syncInputValue() {
|
|
163
|
+
// While the user is editing, never overwrite their input
|
|
164
|
+
if (this.state.isInputFocused || this.state.isDropdownOpen) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
var activeSize = this.getActiveSize();
|
|
169
|
+
var newValue = activeSize || '';
|
|
170
|
+
|
|
171
|
+
if (this.state.inputValue !== newValue) {
|
|
172
|
+
this.setState({ inputValue: newValue });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
toggleDropdown(force) {
|
|
177
|
+
const willBeOpen =
|
|
178
|
+
typeof force === 'boolean' ? force : !this.state.isDropdownOpen;
|
|
179
|
+
if (willBeOpen && !this.state.isDropdownOpen) {
|
|
180
|
+
saveSelection(this.props.getEditorState);
|
|
181
|
+
}
|
|
182
|
+
this.setState({ isDropdownOpen: willBeOpen });
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
applyFontSize(size) {
|
|
186
|
+
console.log(size);
|
|
187
|
+
var editorState = restoreSelection(this.props.getEditorState());
|
|
188
|
+
var currentSize = this.getActiveSize(editorState);
|
|
189
|
+
editorState = removeFontSizeEntity(editorState);
|
|
190
|
+
|
|
191
|
+
if (size === currentSize) {
|
|
192
|
+
// Re-sync the input value
|
|
193
|
+
this.props.onChange(editorState);
|
|
194
|
+
this.setState({ isDropdownOpen: false, inputValue: '' });
|
|
195
|
+
this.syncInputValue();
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
var selection = editorState.getSelection();
|
|
200
|
+
|
|
201
|
+
// Apply the entity to the selection
|
|
202
|
+
var contentWithEntity = editorState
|
|
203
|
+
.getCurrentContent()
|
|
204
|
+
.createEntity(entityType, 'MUTABLE', {
|
|
205
|
+
size,
|
|
206
|
+
});
|
|
207
|
+
var contentStateWithEntity = Modifier.applyEntity(
|
|
208
|
+
contentWithEntity,
|
|
209
|
+
selection,
|
|
210
|
+
contentWithEntity.getLastCreatedEntityKey(),
|
|
211
|
+
);
|
|
212
|
+
var newContent = EditorState.push(
|
|
213
|
+
editorState,
|
|
214
|
+
contentStateWithEntity,
|
|
215
|
+
'apply-entity',
|
|
216
|
+
);
|
|
217
|
+
this.props.onChange(newContent);
|
|
218
|
+
|
|
219
|
+
// Re-sync the input value
|
|
220
|
+
this.setState({ isDropdownOpen: false, inputValue: String(size) });
|
|
221
|
+
this.syncInputValue();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
increment() {
|
|
225
|
+
var activeSize = this.getActiveSize();
|
|
226
|
+
var currentSize = isNaN(activeSize || this.state.inputValue || 'NaN')
|
|
227
|
+
? 16
|
|
228
|
+
: parseInt(activeSize || this.state.inputValue);
|
|
229
|
+
this.applyFontSize(Math.min(MAX_FONT_SIZE, currentSize + 1));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
decrement() {
|
|
233
|
+
var activeSize = this.getActiveSize();
|
|
234
|
+
var currentSize = isNaN(activeSize || this.state.inputValue || 'NaN')
|
|
235
|
+
? 16
|
|
236
|
+
: parseInt(activeSize || this.state.inputValue);
|
|
237
|
+
this.applyFontSize(Math.max(MIN_FONT_SIZE, currentSize - 1));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// event handlers
|
|
241
|
+
handleInputMouseDown = (e) => {
|
|
242
|
+
this.toggleDropdown();
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
handleInputFocus = () => {
|
|
246
|
+
saveSelection(this.props.getEditorState);
|
|
247
|
+
this.setState({ isInputFocused: true });
|
|
248
|
+
if (!this.state.isDropdownOpen) {
|
|
249
|
+
this.setState({ isDropdownOpen: true });
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
handleInputBlur = () => {
|
|
254
|
+
// When leaving the field, clear the focus flag and re‑sync (will clear if no style)
|
|
255
|
+
this.setState({ isInputFocused: false }, () => {
|
|
256
|
+
this.syncInputValue();
|
|
257
|
+
});
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
handleInputChange = (e) => {
|
|
261
|
+
this.setState({ inputValue: e.target.value.replace(/[^0-9]/g, '') });
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
handleInputKeyDown = (e) => {
|
|
265
|
+
if (e.key === 'Escape') {
|
|
266
|
+
this.toggleDropdown(false);
|
|
267
|
+
this.syncInputValue();
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (['ArrowUp', 'ArrowDown', 'Enter'].includes(e.key)) {
|
|
272
|
+
e.preventDefault();
|
|
273
|
+
if (e.key === 'Enter') {
|
|
274
|
+
var size = parseInt(this.state.inputValue, 10);
|
|
275
|
+
if (size > 0 && size <= 400) {
|
|
276
|
+
this.applyFontSize(size);
|
|
277
|
+
} else {
|
|
278
|
+
this.syncInputValue();
|
|
279
|
+
}
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (e.key === 'ArrowUp') {
|
|
284
|
+
this.increment();
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
this.decrement();
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
handleClickOutside = (event) => {
|
|
294
|
+
if (
|
|
295
|
+
this.controlRef.current &&
|
|
296
|
+
!this.controlRef.current.contains(event.target)
|
|
297
|
+
) {
|
|
298
|
+
this.setState({ isDropdownOpen: false });
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
// render
|
|
303
|
+
render() {
|
|
304
|
+
var activeSize = this.getActiveSize();
|
|
305
|
+
var inputDisplay = this.state.inputValue;
|
|
306
|
+
var isDropdownExpanded = this.state.isDropdownOpen;
|
|
307
|
+
var dropdown = React.createElement(
|
|
308
|
+
'ul',
|
|
309
|
+
{
|
|
310
|
+
'id': 'Draftail--font-size-dropdown',
|
|
311
|
+
'className': 'Draftail--font-size-dropdown',
|
|
312
|
+
'aria-expanded': isDropdownExpanded,
|
|
313
|
+
},
|
|
314
|
+
options.map(
|
|
315
|
+
function (opt) {
|
|
316
|
+
return React.createElement(
|
|
317
|
+
'li',
|
|
318
|
+
{
|
|
319
|
+
'key': `FONT_SIZE_OPT_${opt.size}`,
|
|
320
|
+
'onMouseDown': (e) => {
|
|
321
|
+
e.preventDefault();
|
|
322
|
+
this.applyFontSize(opt.size);
|
|
323
|
+
},
|
|
324
|
+
'className': 'Draftail--font-size-option',
|
|
325
|
+
'aria-selected': inputDisplay === opt.size,
|
|
326
|
+
},
|
|
327
|
+
opt.label,
|
|
328
|
+
);
|
|
329
|
+
}.bind(this),
|
|
330
|
+
),
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
var decrementBtn = React.createElement(ToolbarButton, {
|
|
334
|
+
key: `FONT_SIZE_CTRL_DECREMENT`,
|
|
335
|
+
name: 'DECREMENT',
|
|
336
|
+
title: 'Decrease font size',
|
|
337
|
+
label: '-',
|
|
338
|
+
tooltipDirection: 'up',
|
|
339
|
+
className: 'Draftail--font-size-decrement',
|
|
340
|
+
onClick: this.decrement.bind(this),
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
var incrementBtn = React.createElement(ToolbarButton, {
|
|
344
|
+
key: `FONT_SIZE_CTRL_INCREMENT`,
|
|
345
|
+
name: 'INCREMENT',
|
|
346
|
+
title: 'Increase font size',
|
|
347
|
+
label: '+',
|
|
348
|
+
tooltipDirection: 'up',
|
|
349
|
+
className: 'Draftail--font-size-increment',
|
|
350
|
+
onClick: this.increment.bind(this),
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
var input = React.createElement(
|
|
354
|
+
'div',
|
|
355
|
+
{
|
|
356
|
+
'key': 'FONT_SIZE_CTRL_INPUT_WRAPPER',
|
|
357
|
+
'className': 'Draftail--font-size-input-wrapper',
|
|
358
|
+
'data-draftail-balloon': 'up',
|
|
359
|
+
'aria-label': control.label,
|
|
360
|
+
'aria-controls': 'Draftail--font-size-dropdown',
|
|
361
|
+
},
|
|
362
|
+
React.createElement('input', {
|
|
363
|
+
key: 'FONT_SIZE_CTRL_INPUT',
|
|
364
|
+
id: 'Draftail--font-size-input',
|
|
365
|
+
className: 'Draftail--font-size-input',
|
|
366
|
+
type: 'text',
|
|
367
|
+
defaultValue: activeSize || '',
|
|
368
|
+
value: inputDisplay,
|
|
369
|
+
placeholder: 'Font Size',
|
|
370
|
+
onMouseDown: this.handleInputMouseDown,
|
|
371
|
+
onFocus: this.handleInputFocus,
|
|
372
|
+
onBlur: this.handleInputBlur,
|
|
373
|
+
onChange: this.handleInputChange,
|
|
374
|
+
onKeyDown: this.handleInputKeyDown,
|
|
375
|
+
}),
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
var controlRow = React.createElement(
|
|
379
|
+
'div',
|
|
380
|
+
{
|
|
381
|
+
key: 'FONT_SIZE_CTRL_ROW',
|
|
382
|
+
className: 'Draftail--font-size-control-row',
|
|
383
|
+
},
|
|
384
|
+
decrementBtn,
|
|
385
|
+
input,
|
|
386
|
+
incrementBtn,
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
return React.createElement(
|
|
390
|
+
'div',
|
|
391
|
+
{
|
|
392
|
+
key: 'FONT_SIZE_CTRL',
|
|
393
|
+
className: 'Draftail--font-size-control',
|
|
394
|
+
ref: this.controlRef,
|
|
395
|
+
},
|
|
396
|
+
controlRow,
|
|
397
|
+
dropdown,
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
window.draftail.registerPlugin(
|
|
402
|
+
{
|
|
403
|
+
type: control.type,
|
|
404
|
+
inline: FontSizeControl,
|
|
405
|
+
},
|
|
406
|
+
'controls',
|
|
407
|
+
);
|
|
408
|
+
})();
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Font size entity for Draftail.
|
|
3
|
+
* Registers an entity type (FONT_SIZE) that stores an arbitrary font size.
|
|
4
|
+
* The entity is applied by `font-size` control when a custom size is entered.
|
|
5
|
+
* See:`src/draftail_text_utils/static/draftail_text_utils/js/font_family.js`
|
|
6
|
+
*/
|
|
7
|
+
(function () {
|
|
8
|
+
const React = window.React;
|
|
9
|
+
const ENTITY_TYPE = 'FONT_SIZE';
|
|
10
|
+
const STYLE_KEY = 'style';
|
|
11
|
+
const DATASET_KEY = 'data-entity-type';
|
|
12
|
+
|
|
13
|
+
const FontSizeDecorator = ({ children, contentState, entityKey }) => {
|
|
14
|
+
const entity = contentState.getEntity(entityKey);
|
|
15
|
+
const { size: fontSize } = entity.getData();
|
|
16
|
+
return React.createElement(
|
|
17
|
+
'span',
|
|
18
|
+
{
|
|
19
|
+
[STYLE_KEY]: { fontSize },
|
|
20
|
+
[DATASET_KEY]: ENTITY_TYPE,
|
|
21
|
+
},
|
|
22
|
+
children,
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const FontSizeSource = ({ onClose }) => {
|
|
27
|
+
onClose();
|
|
28
|
+
return null;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
window.draftail.registerPlugin(
|
|
32
|
+
{
|
|
33
|
+
type: ENTITY_TYPE,
|
|
34
|
+
source: FontSizeSource,
|
|
35
|
+
decorator: FontSizeDecorator,
|
|
36
|
+
},
|
|
37
|
+
'entityTypes',
|
|
38
|
+
);
|
|
39
|
+
})();
|