@vaadin/rich-text-editor 25.0.0-alpha14 → 25.0.0-alpha16

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/rich-text-editor",
3
- "version": "25.0.0-alpha14",
3
+ "version": "25.0.0-alpha16",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -35,21 +35,21 @@
35
35
  ],
36
36
  "dependencies": {
37
37
  "@open-wc/dedupe-mixin": "^1.3.0",
38
- "@vaadin/button": "25.0.0-alpha14",
39
- "@vaadin/component-base": "25.0.0-alpha14",
40
- "@vaadin/confirm-dialog": "25.0.0-alpha14",
41
- "@vaadin/overlay": "25.0.0-alpha14",
42
- "@vaadin/text-field": "25.0.0-alpha14",
43
- "@vaadin/tooltip": "25.0.0-alpha14",
44
- "@vaadin/vaadin-themable-mixin": "25.0.0-alpha14",
38
+ "@vaadin/button": "25.0.0-alpha16",
39
+ "@vaadin/component-base": "25.0.0-alpha16",
40
+ "@vaadin/confirm-dialog": "25.0.0-alpha16",
41
+ "@vaadin/overlay": "25.0.0-alpha16",
42
+ "@vaadin/text-field": "25.0.0-alpha16",
43
+ "@vaadin/tooltip": "25.0.0-alpha16",
44
+ "@vaadin/vaadin-themable-mixin": "25.0.0-alpha16",
45
45
  "lit": "^3.0.0"
46
46
  },
47
47
  "devDependencies": {
48
- "@vaadin/a11y-base": "25.0.0-alpha14",
49
- "@vaadin/chai-plugins": "25.0.0-alpha14",
50
- "@vaadin/test-runner-commands": "25.0.0-alpha14",
48
+ "@vaadin/a11y-base": "25.0.0-alpha16",
49
+ "@vaadin/chai-plugins": "25.0.0-alpha16",
50
+ "@vaadin/test-runner-commands": "25.0.0-alpha16",
51
51
  "@vaadin/testing-helpers": "^2.0.0",
52
- "@vaadin/vaadin-lumo-styles": "25.0.0-alpha14",
52
+ "@vaadin/vaadin-lumo-styles": "25.0.0-alpha16",
53
53
  "sinon": "^18.0.0"
54
54
  },
55
55
  "cvdlName": "vaadin-rich-text-editor",
@@ -57,5 +57,5 @@
57
57
  "web-types.json",
58
58
  "web-types.lit.json"
59
59
  ],
60
- "gitHead": "8ebeeeca4b5b6564eff954d6582d0d6760464e51"
60
+ "gitHead": "4b316158a4a4f702f032bc9940fc82f0faa840f4"
61
61
  }
@@ -24,9 +24,11 @@ export const icons = css`
24
24
  --_vaadin-icon-h1: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M2.243 4.493v7.5m0 0v7.502m0-7.501h10.5m0-7.5v7.5m0 0v7.501m4.501-8.627 2.25-1.5v10.126m0 0h-2.25m2.25 0h2.25" /></svg>');
25
25
  --_vaadin-icon-h2: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M21.75 19.5H16.5v-1.609a2.25 2.25 0 0 1 1.244-2.012l2.89-1.445c.651-.326 1.116-.955 1.116-1.683 0-.498-.04-.987-.118-1.463-.135-.825-.835-1.422-1.668-1.489a15.202 15.202 0 0 0-3.464.12M2.243 4.492v7.5m0 0v7.502m0-7.501h10.5m0-7.5v7.5m0 0v7.501" /></svg>');
26
26
  --_vaadin-icon-h3: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M20.905 14.626a4.52 4.52 0 0 1 .738 3.603c-.154.695-.794 1.143-1.504 1.208a15.194 15.194 0 0 1-3.639-.104m4.405-4.707a4.52 4.52 0 0 0 .738-3.603c-.154-.696-.794-1.144-1.504-1.209a15.19 15.19 0 0 0-3.639.104m4.405 4.708H18M2.243 4.493v7.5m0 0v7.502m0-7.501h10.5m0-7.5v7.5m0 0v7.501" /></svg>');
27
+ --_vaadin-icon-indent: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M3.874 19.403h16.545M3.874 5.379h16.545M12.147 12.391h8.272M3.881 9.578v5.593l2.754-2.796-2.754-2.797"/></svg>');
27
28
  --_vaadin-icon-italic: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M5.248 20.246H9.05m0 0h3.696m-3.696 0 5.893-16.502m0 0h-3.697m3.697 0h3.803" /></svg>');
28
29
  --_vaadin-icon-list-number: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M8.242 5.992h12m-12 6.003H20.24m-12 5.999h12M4.117 7.495v-3.75H2.99m1.125 3.75H2.99m1.125 0H5.24m-1.92 2.577a1.125 1.125 0 1 1 1.591 1.59l-1.83 1.83h2.16M2.99 15.745h1.125a1.125 1.125 0 0 1 0 2.25H3.74m0-.002h.375a1.125 1.125 0 0 1 0 2.25H2.99" /></svg>');
29
30
  --_vaadin-icon-list-bullet: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M8.25 6.75h12M8.25 12h12m-12 5.25h12M3.75 6.75h.007v.008H3.75V6.75Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0ZM3.75 12h.007v.008H3.75V12Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm-.375 5.25h.007v.008H3.75v-.008Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z" /></svg>');
31
+ --_vaadin-icon-outdent: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" ><path stroke-linecap="round" stroke-linejoin="round" d="M3.874 19.403h16.545M3.874 5.379h16.545m-8.272 7.012h8.272M6.631 9.586v5.61l-2.757-2.805 2.757-2.805"/></svg>');
30
32
  --_vaadin-icon-quote: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M0 0h24v24H0z" fill="none"/><path d="M6 17h3l2-4V7H5v6h3zm8 0h3l2-4V7h-6v6h3z"/></svg>');
31
33
  --_vaadin-icon-strikethrough: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M12 12a8.912 8.912 0 0 1-.318-.079c-1.585-.424-2.904-1.247-3.76-2.236-.873-1.009-1.265-2.19-.968-3.301.59-2.2 3.663-3.29 6.863-2.432A8.186 8.186 0 0 1 16.5 5.21M6.42 17.81c.857.99 2.176 1.812 3.761 2.237 3.2.858 6.274-.23 6.863-2.431.233-.868.044-1.779-.465-2.617M3.75 12h16.5" /></svg>');
32
34
  --_vaadin-icon-subscript: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor"><path d="M760-160v-80q0-17 11.5-28.5T800-280h80v-40H760v-40h120q17 0 28.5 11.5T920-320v40q0 17-11.5 28.5T880-240h-80v40h120v40H760Zm-525-80 185-291-172-269h106l124 200h4l123-200h107L539-531l186 291H618L482-457h-4L342-240H235Z"/></svg>');
@@ -51,6 +51,12 @@ const base = css`
51
51
  `;
52
52
 
53
53
  export const content = css`
54
+ :host {
55
+ --_item-indent: var(--vaadin-padding-l);
56
+ --_marker-indent: calc(var(--vaadin-padding-s) / 2);
57
+ --_list-indent: calc(var(--_item-indent) + var(--_marker-indent));
58
+ }
59
+
54
60
  [part='content'] {
55
61
  box-sizing: border-box;
56
62
  display: flex;
@@ -87,8 +93,7 @@ export const content = css`
87
93
  outline: none;
88
94
  overflow-y: auto;
89
95
  padding: var(--vaadin-rich-text-editor-editor-padding, var(--vaadin-padding-container));
90
- tab-size: 4;
91
- -moz-tab-size: 4;
96
+ tab-size: calc(var(--_item-indent) * 2);
92
97
  text-align: left;
93
98
  white-space: pre-wrap;
94
99
  word-wrap: break-word;
@@ -128,24 +133,44 @@ export const content = css`
128
133
  padding: var(--vaadin-padding-container);
129
134
  }
130
135
 
136
+ /* lists */
137
+ .ql-editor ol {
138
+ padding-inline-start: var(--_list-indent);
139
+ }
140
+
131
141
  .ql-editor li {
132
142
  list-style-type: none;
133
143
  position: relative;
144
+ padding-inline-start: var(--_item-indent);
134
145
  }
135
146
 
136
147
  .ql-editor li > .ql-ui::before {
137
148
  display: inline-block;
138
- margin-left: -1.5em;
139
- margin-right: 0.3em;
140
- text-align: right;
149
+ width: var(--_item-indent);
150
+ margin-inline: calc(var(--_item-indent) * -1) var(--_marker-indent);
151
+ text-align: end;
141
152
  white-space: nowrap;
142
- width: 1.2em;
143
153
  }
144
154
 
145
- .ql-editor li[data-list='bullet'] {
146
- list-style-type: disc;
155
+ .ql-editor li[data-list='bullet'] > .ql-ui::before {
156
+ content: '\\2022';
157
+ font-size: 1.5rem;
158
+ line-height: 1rem;
159
+ align-self: baseline;
160
+ vertical-align: text-top;
147
161
  }
148
162
 
163
+ .ql-editor p,
164
+ .ql-editor h1,
165
+ .ql-editor h2,
166
+ .ql-editor h3,
167
+ .ql-editor h4,
168
+ .ql-editor h5,
169
+ .ql-editor h6 {
170
+ counter-set: list-0 list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
171
+ }
172
+
173
+ /* 0 */
149
174
  .ql-editor li[data-list='ordered'] {
150
175
  counter-increment: list-0;
151
176
  }
@@ -153,6 +178,182 @@ export const content = css`
153
178
  .ql-editor li[data-list='ordered'] > .ql-ui::before {
154
179
  content: counter(list-0, decimal) '. ';
155
180
  }
181
+
182
+ /* 1 */
183
+ .ql-editor li[data-list='ordered'].ql-indent-1 {
184
+ counter-increment: list-1;
185
+ }
186
+
187
+ .ql-editor li[data-list='ordered'].ql-indent-1 > .ql-ui::before {
188
+ content: counter(list-1, lower-alpha) '. ';
189
+ }
190
+
191
+ .ql-editor li[data-list].ql-indent-1 {
192
+ counter-set: list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
193
+ }
194
+
195
+ /* 2 */
196
+ .ql-editor li[data-list='ordered'].ql-indent-2 {
197
+ counter-increment: list-2;
198
+ }
199
+
200
+ .ql-editor li[data-list='ordered'].ql-indent-2 > .ql-ui::before {
201
+ content: counter(list-2, lower-roman) '. ';
202
+ }
203
+
204
+ .ql-editor li[data-list].ql-indent-2 {
205
+ counter-set: list-3 list-4 list-5 list-6 list-7 list-8 list-9;
206
+ }
207
+
208
+ /* 3 */
209
+ .ql-editor li[data-list='ordered'].ql-indent-3 {
210
+ counter-increment: list-3;
211
+ }
212
+
213
+ .ql-editor li[data-list='ordered'].ql-indent-3 > .ql-ui::before {
214
+ content: counter(list-3, decimal) '. ';
215
+ }
216
+
217
+ .ql-editor li[data-list].ql-indent-3 {
218
+ counter-set: list-4 list-5 list-6 list-7 list-8 list-9;
219
+ }
220
+
221
+ /* 4 */
222
+ .ql-editor li[data-list='ordered'].ql-indent-4 {
223
+ counter-increment: list-4;
224
+ }
225
+
226
+ .ql-editor li[data-list='ordered'].ql-indent-4 > .ql-ui::before {
227
+ content: counter(list-4, lower-alpha) '. ';
228
+ }
229
+
230
+ .ql-editor li[data-list].ql-indent-4 {
231
+ counter-set: list-5 list-6 list-7 list-8 list-9;
232
+ }
233
+
234
+ /* 5 */
235
+ .ql-editor li[data-list='ordered'].ql-indent-5 {
236
+ counter-increment: list-5;
237
+ }
238
+
239
+ .ql-editor li[data-list='ordered'].ql-indent-5 > .ql-ui::before {
240
+ content: counter(list-5, lower-roman) '. ';
241
+ }
242
+
243
+ .ql-editor li[data-list].ql-indent-5 {
244
+ counter-set: list-6 list-7 list-8 list-9;
245
+ }
246
+
247
+ /* 6 */
248
+ .ql-editor li[data-list='ordered'].ql-indent-6 {
249
+ counter-increment: list-6;
250
+ }
251
+
252
+ .ql-editor li[data-list='ordered'].ql-indent-6 > .ql-ui::before {
253
+ content: counter(list-6, decimal) '. ';
254
+ }
255
+
256
+ .ql-editor li[data-list].ql-indent-6 {
257
+ counter-set: list-7 list-8 list-9;
258
+ }
259
+
260
+ /* 7 */
261
+ .ql-editor li[data-list='ordered'].ql-indent-7 {
262
+ counter-increment: list-7;
263
+ }
264
+
265
+ .ql-editor li[data-list='ordered'].ql-indent-7 > .ql-ui::before {
266
+ content: counter(list-7, lower-alpha) '. ';
267
+ }
268
+
269
+ .ql-editor li[data-list].ql-indent-7 {
270
+ counter-set: list-8 list-9;
271
+ }
272
+
273
+ /* 8 */
274
+ .ql-editor li[data-list='ordered'].ql-indent-8 {
275
+ counter-increment: list-8;
276
+ }
277
+
278
+ .ql-editor li[data-list='ordered'].ql-indent-8 > .ql-ui::before {
279
+ content: counter(list-8, lower-roman) '. ';
280
+ }
281
+
282
+ .ql-editor li[data-list].ql-indent-8 {
283
+ counter-set: list-9;
284
+ }
285
+
286
+ /* indent 1 */
287
+ .ql-editor .ql-indent-1 {
288
+ padding-inline-start: calc(var(--_item-indent) * 2);
289
+ }
290
+
291
+ .ql-editor li.ql-indent-1 {
292
+ padding-inline-start: calc(var(--_list-indent) + var(--_item-indent) * 2);
293
+ }
294
+
295
+ /* indent 2 */
296
+ .ql-editor .ql-indent-2 {
297
+ padding-inline-start: calc(var(--_item-indent) * 4);
298
+ }
299
+
300
+ .ql-editor li.ql-indent-2 {
301
+ padding-inline-start: calc(var(--_list-indent) * 2 + var(--_item-indent) * 3);
302
+ }
303
+
304
+ /* indent 3 */
305
+ .ql-editor .ql-indent-3 {
306
+ padding-inline-start: calc(var(--_item-indent) * 6);
307
+ }
308
+
309
+ .ql-editor li.ql-indent-3 {
310
+ padding-inline-start: calc(var(--_list-indent) * 3 + var(--_item-indent) * 4);
311
+ }
312
+
313
+ /* indent 4 */
314
+ .ql-editor .ql-indent-4 {
315
+ padding-inline-start: calc(var(--_item-indent) * 8);
316
+ }
317
+
318
+ .ql-editor li.ql-indent-4 {
319
+ padding-inline-start: calc(var(--_list-indent) * 4 + var(--_item-indent) * 5);
320
+ }
321
+
322
+ /* indent 5 */
323
+ .ql-editor .ql-indent-5 {
324
+ padding-inline-start: calc(var(--_item-indent) * 10);
325
+ }
326
+
327
+ .ql-editor li.ql-indent-5 {
328
+ padding-inline-start: calc(var(--_list-indent) * 5 + var(--_item-indent) * 6);
329
+ }
330
+
331
+ /* indent 6 */
332
+ .ql-editor .ql-indent-6 {
333
+ padding-inline-start: calc(var(--_item-indent) * 12);
334
+ }
335
+
336
+ .ql-editor li.ql-indent-6 {
337
+ padding-inline-start: calc(var(--_list-indent) * 6 + var(--_item-indent) * 7);
338
+ }
339
+
340
+ /* indent 7 */
341
+ .ql-editor .ql-indent-7 {
342
+ padding-inline-start: calc(var(--_item-indent) * 14);
343
+ }
344
+
345
+ .ql-editor li.ql-indent-7 {
346
+ padding-inline-start: calc(var(--_list-indent) * 7 + var(--_item-indent) * 8);
347
+ }
348
+
349
+ /* indent 8 */
350
+ .ql-editor .ql-indent-8 {
351
+ padding-inline-start: calc(var(--_item-indent) * 16);
352
+ }
353
+
354
+ .ql-editor li.ql-indent-8 {
355
+ padding-inline-start: calc(var(--_list-indent) * 8 + var(--_item-indent) * 9);
356
+ }
156
357
  /* quill core end */
157
358
 
158
359
  blockquote {
@@ -229,7 +430,7 @@ const toolbar = css`
229
430
  z-index: 1;
230
431
  }
231
432
 
232
- [part~='toolbar-button'][on],
433
+ [part~='toolbar-button-pressed'],
233
434
  [part~='toolbar-button'][aria-expanded='true'] {
234
435
  --vaadin-rich-text-editor-toolbar-button-background: var(--vaadin-background-container-strong);
235
436
  }
@@ -314,6 +515,14 @@ const toolbar = css`
314
515
  mask-image: var(--_vaadin-icon-list-bullet);
315
516
  }
316
517
 
518
+ [part~='toolbar-button-outdent']::before {
519
+ mask-image: var(--_vaadin-icon-outdent);
520
+ }
521
+
522
+ [part~='toolbar-button-indent']::before {
523
+ mask-image: var(--_vaadin-icon-indent);
524
+ }
525
+
317
526
  [part~='toolbar-button-align-left']::before {
318
527
  mask-image: var(--_vaadin-icon-align-left);
319
528
  }
@@ -351,11 +560,11 @@ const toolbar = css`
351
560
  background: CanvasText;
352
561
  }
353
562
 
354
- [part~='toolbar-button'][on] {
563
+ [part~='toolbar-button-pressed'] {
355
564
  background: Highlight;
356
565
  }
357
566
 
358
- [part~='toolbar-button'][on]::before {
567
+ [part~='toolbar-button-pressed']::before {
359
568
  background: HighlightText;
360
569
  }
361
570
  }
@@ -27,6 +27,8 @@ export interface RichTextEditorI18n {
27
27
  superscript?: string;
28
28
  listOrdered?: string;
29
29
  listBullet?: string;
30
+ outdent?: string;
31
+ indent?: string;
30
32
  alignLeft?: string;
31
33
  alignCenter?: string;
32
34
  alignRight?: string;
@@ -77,6 +77,8 @@ const DEFAULT_I18N = {
77
77
  superscript: 'superscript',
78
78
  listOrdered: 'list ordered',
79
79
  listBullet: 'list bullet',
80
+ outdent: 'outdent',
81
+ indent: 'indent',
80
82
  alignLeft: 'align left',
81
83
  alignCenter: 'align center',
82
84
  alignRight: 'align right',
@@ -410,9 +412,9 @@ export const RichTextEditorMixin = (superClass) =>
410
412
  // Set up tooltip to show when hovering or focusing toolbar buttons
411
413
  this._tooltip = document.createElement('vaadin-tooltip');
412
414
  this._tooltip.slot = 'tooltip';
413
- // Create dummy aria target, as toolbar buttons already have aria-label, and also cannot be linked with the
414
- // tooltip being in the light DOM
415
- this._tooltip.ariaTarget = document.createElement('div');
415
+ // Set ariaTarget to null, as toolbar buttons already have aria-label,
416
+ // and also cannot be linked with the tooltip being in the light DOM
417
+ this._tooltip.ariaTarget = null;
416
418
  this.append(this._tooltip);
417
419
 
418
420
  const buttons = this.shadowRoot.querySelectorAll('[part~="toolbar-button"]');
@@ -527,7 +529,6 @@ export const RichTextEditorMixin = (superClass) =>
527
529
  toolbar.controls.forEach((pair) => {
528
530
  const input = pair[1];
529
531
  const isActive = input.classList.contains('ql-active');
530
- input.toggleAttribute('on', isActive);
531
532
  input.part.toggle('toolbar-button-pressed', isActive);
532
533
  });
533
534
  };
@@ -541,15 +542,9 @@ export const RichTextEditorMixin = (superClass) =>
541
542
  };
542
543
 
543
544
  const keyboard = this._editor.keyboard;
544
- const bindings = keyboard.bindings.Tab;
545
545
 
546
- // Exclude Quill shift-tab bindings, except for code block,
547
- // as some of those are breaking when on a newline in the list
548
- // https://github.com/vaadin/vaadin-rich-text-editor/issues/67
549
- const originalBindings = bindings.filter((b) => !b.shiftKey || (b.format && b.format['code-block']));
550
- const moveFocusBinding = { key: 'Tab', shiftKey: true, handler: focusToolbar };
551
-
552
- keyboard.bindings.Tab = [...originalBindings, moveFocusBinding];
546
+ // Shift + Tab focuses a toolbar button unless we are in list / code block
547
+ keyboard.addBinding({ key: 'Tab', shiftKey: true, handler: focusToolbar });
553
548
 
554
549
  // Alt-f10 focuses a toolbar button
555
550
  keyboard.addBinding({ key: 'F10', altKey: true, handler: focusToolbar });
@@ -701,16 +696,66 @@ export const RichTextEditorMixin = (superClass) =>
701
696
  __updateHtmlValue() {
702
697
  // We have to use this instead of `innerHTML` to get correct tags like `<pre>` etc.
703
698
  let content = this._editor.getSemanticHTML();
704
- // Replace Quill align classes with inline styles
705
- [this.__dir === 'rtl' ? 'left' : 'right', 'center', 'justify'].forEach((align) => {
706
- content = content.replace(
707
- new RegExp(` class=[\\\\]?"\\s?ql-align-${align}[\\\\]?"`, 'gu'),
708
- ` style="text-align: ${align}"`,
709
- );
699
+ // Remove Quill classes, e.g. ql-syntax, except for align and indent
700
+ content = content.replace(/class="([^"]*)"/gu, (_match, group1) => {
701
+ const classes = group1.split(' ').filter((className) => {
702
+ return !className.startsWith('ql-') || className.startsWith('ql-align') || className.startsWith('ql-indent');
703
+ });
704
+ return `class="${classes.join(' ')}"`;
710
705
  });
706
+ // Process align and indent classes
707
+ content = this.__processQuillClasses(content);
711
708
  this._setHtmlValue(content);
712
709
  }
713
710
 
711
+ /** @private */
712
+ __processQuillClasses(content) {
713
+ const tempDiv = document.createElement('div');
714
+ tempDiv.innerHTML = content;
715
+ // Process only elements with align or indent classes
716
+ const elementsToProcess = tempDiv.querySelectorAll('[class*="ql-align"], [class*="ql-indent"]');
717
+ elementsToProcess.forEach((element) => {
718
+ this.__processAlignClasses(element);
719
+ this.__processIndentClasses(element);
720
+ element.removeAttribute('class');
721
+ });
722
+ return tempDiv.innerHTML;
723
+ }
724
+
725
+ /** @private */
726
+ __processAlignClasses(element) {
727
+ let styleText = element.getAttribute('style') || '';
728
+ const alignments = [this.__dir === 'rtl' ? 'left' : 'right', 'center', 'justify'];
729
+ alignments.forEach((align) => {
730
+ if (element.classList.contains(`ql-align-${align}`)) {
731
+ const newStyle = `text-align: ${align}`;
732
+ styleText = styleText ? `${styleText}; ${newStyle}` : newStyle;
733
+ element.setAttribute('style', styleText);
734
+ element.classList.remove(`ql-align-${align}`);
735
+ }
736
+ });
737
+ }
738
+
739
+ /** @private */
740
+ __processIndentClasses(element) {
741
+ const indentClass = Array.from(element.classList).find((className) => className.startsWith('ql-indent-'));
742
+ if (indentClass) {
743
+ const level = parseInt(indentClass.replace('ql-indent-', '').trim(), 10);
744
+ const tabs = '\t'.repeat(level);
745
+ // Add tabs to content
746
+ const firstChild = element.firstChild;
747
+ if (firstChild && firstChild.nodeType === Node.TEXT_NODE) {
748
+ firstChild.textContent = tabs + firstChild.textContent;
749
+ } else if (element.childNodes.length > 0) {
750
+ const tabNode = document.createTextNode(tabs);
751
+ element.insertBefore(tabNode, element.firstChild);
752
+ } else {
753
+ element.textContent = tabs;
754
+ }
755
+ element.classList.remove(indentClass);
756
+ }
757
+ }
758
+
714
759
  /**
715
760
  * Sets content represented by HTML snippet into the editor.
716
761
  * The snippet is interpreted by [Quill's Clipboard matchers](https://quilljs.com/docs/modules/clipboard/#matchers),
@@ -174,11 +174,11 @@ class RichTextEditorPopupOverlay extends PositionMixin(
174
174
  }
175
175
 
176
176
  /**
177
- * Override method from OverlayFocusMixin to use owner as modal root
177
+ * Override method from OverlayFocusMixin to use owner as focus trap root
178
178
  * @protected
179
179
  * @override
180
180
  */
181
- get _modalRoot() {
181
+ get _focusTrapRoot() {
182
182
  return this.owner;
183
183
  }
184
184
  }
@@ -41,11 +41,10 @@ export interface RichTextEditorEventMap extends HTMLElementEventMap, RichTextEdi
41
41
  *
42
42
  * The following state attributes are available for styling:
43
43
  *
44
- * Attribute | Description | Part name
45
- * -------------|-------------|------------
46
- * `disabled` | Set to a disabled text editor | :host
47
- * `readonly` | Set to a readonly text editor | :host
48
- * `on` | Set to a toolbar button applied to the selected text | toolbar-button
44
+ * Attribute | Description
45
+ * -------------|------------------------------
46
+ * `disabled` | Set to a disabled text editor
47
+ * `readonly` | Set to a readonly text editor
49
48
  *
50
49
  * The following shadow DOM parts are available for styling:
51
50
  *
@@ -54,12 +53,13 @@ export interface RichTextEditorEventMap extends HTMLElementEventMap, RichTextEdi
54
53
  * `content` | The content wrapper
55
54
  * `toolbar` | The toolbar wrapper
56
55
  * `toolbar-group` | The group for toolbar controls
57
- * `toolbar-group-history` | The group for histroy controls
56
+ * `toolbar-group-history` | The group for history controls
58
57
  * `toolbar-group-emphasis` | The group for emphasis controls
59
58
  * `toolbar-group-heading` | The group for heading controls
60
59
  * `toolbar-group-style` | The group for style controls
61
60
  * `toolbar-group-glyph-transformation` | The group for glyph transformation controls
62
61
  * `toolbar-group-list` | The group for list controls
62
+ * `toolbar-group-indent` | The group for indentation controls
63
63
  * `toolbar-group-alignment` | The group for alignment controls
64
64
  * `toolbar-group-rich-text` | The group for rich text controls
65
65
  * `toolbar-group-block` | The group for preformatted block controls
@@ -81,6 +81,8 @@ export interface RichTextEditorEventMap extends HTMLElementEventMap, RichTextEdi
81
81
  * `toolbar-button-superscript` | The "superscript" button
82
82
  * `toolbar-button-list-ordered` | The "ordered list" button
83
83
  * `toolbar-button-list-bullet` | The "bullet list" button
84
+ * `toolbar-button-outdent` | The "decrease indentation" button
85
+ * `toolbar-button-indent` | The "increase indentation" button
84
86
  * `toolbar-button-align-left` | The "left align" button
85
87
  * `toolbar-button-align-center` | The "center align" button
86
88
  * `toolbar-button-align-right` | The "right align" button
@@ -39,11 +39,10 @@ import { RichTextEditorMixin } from './vaadin-rich-text-editor-mixin.js';
39
39
  *
40
40
  * The following state attributes are available for styling:
41
41
  *
42
- * Attribute | Description | Part name
43
- * -------------|-------------|------------
44
- * `disabled` | Set to a disabled text editor | :host
45
- * `readonly` | Set to a readonly text editor | :host
46
- * `on` | Set to a toolbar button applied to the selected text | toolbar-button
42
+ * Attribute | Description
43
+ * -------------|------------------------------
44
+ * `disabled` | Set to a disabled text editor
45
+ * `readonly` | Set to a readonly text editor
47
46
  *
48
47
  * The following shadow DOM parts are available for styling:
49
48
  *
@@ -52,12 +51,13 @@ import { RichTextEditorMixin } from './vaadin-rich-text-editor-mixin.js';
52
51
  * `content` | The content wrapper
53
52
  * `toolbar` | The toolbar wrapper
54
53
  * `toolbar-group` | The group for toolbar controls
55
- * `toolbar-group-history` | The group for histroy controls
54
+ * `toolbar-group-history` | The group for history controls
56
55
  * `toolbar-group-emphasis` | The group for emphasis controls
57
56
  * `toolbar-group-heading` | The group for heading controls
58
57
  * `toolbar-group-style` | The group for style controls
59
58
  * `toolbar-group-glyph-transformation` | The group for glyph transformation controls
60
59
  * `toolbar-group-list` | The group for list controls
60
+ * `toolbar-group-indent` | The group for indentation controls
61
61
  * `toolbar-group-alignment` | The group for alignment controls
62
62
  * `toolbar-group-rich-text` | The group for rich text controls
63
63
  * `toolbar-group-block` | The group for preformatted block controls
@@ -79,6 +79,8 @@ import { RichTextEditorMixin } from './vaadin-rich-text-editor-mixin.js';
79
79
  * `toolbar-button-superscript` | The "superscript" button
80
80
  * `toolbar-button-list-ordered` | The "ordered list" button
81
81
  * `toolbar-button-list-bullet` | The "bullet list" button
82
+ * `toolbar-button-outdent` | The "decrease indentation" button
83
+ * `toolbar-button-indent` | The "increase indentation" button
82
84
  * `toolbar-button-align-left` | The "left align" button
83
85
  * `toolbar-button-align-center` | The "center align" button
84
86
  * `toolbar-button-align-right` | The "right align" button
@@ -259,6 +261,27 @@ class RichTextEditor extends RichTextEditorMixin(
259
261
  ></button>
260
262
  </span>
261
263
 
264
+ <span part="toolbar-group toolbar-group-indent">
265
+ <!-- Decrease -->
266
+ <button
267
+ id="btn-outdent"
268
+ type="button"
269
+ class="ql-indent"
270
+ value="-1"
271
+ part="toolbar-button toolbar-button-outdent"
272
+ aria-label="${this.__effectiveI18n.outdent}"
273
+ ></button>
274
+ <!-- Increase -->
275
+ <button
276
+ id="btn-indent"
277
+ type="button"
278
+ class="ql-indent"
279
+ value="+1"
280
+ part="toolbar-button toolbar-button-indent"
281
+ aria-label="${this.__effectiveI18n.indent}"
282
+ ></button>
283
+ </span>
284
+
262
285
  <span part="toolbar-group toolbar-group-alignment">
263
286
  <!-- Align buttons -->
264
287
  <button
package/web-types.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/rich-text-editor",
4
- "version": "25.0.0-alpha14",
4
+ "version": "25.0.0-alpha16",
5
5
  "description-markup": "markdown",
6
6
  "contributions": {
7
7
  "html": {
8
8
  "elements": [
9
9
  {
10
10
  "name": "vaadin-rich-text-editor",
11
- "description": "`<vaadin-rich-text-editor>` is a Web Component for rich text editing.\nIt provides a set of toolbar controls to apply formatting on the content,\nwhich is stored and can be accessed as HTML5 or JSON string.\n\n```html\n<vaadin-rich-text-editor></vaadin-rich-text-editor>\n```\n\nVaadin Rich Text Editor focuses on the structure, not the styling of content.\nTherefore, the semantic HTML5 tags such as <h1>, <strong> and <ul> are used,\nand CSS usage is limited to most common cases, like horizontal text alignment.\n\n### Styling\n\nThe following state attributes are available for styling:\n\nAttribute | Description | Part name\n-------------|-------------|------------\n`disabled` | Set to a disabled text editor | :host\n`readonly` | Set to a readonly text editor | :host\n`on` | Set to a toolbar button applied to the selected text | toolbar-button\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n-------------------------------------|----------------\n`content` | The content wrapper\n`toolbar` | The toolbar wrapper\n`toolbar-group` | The group for toolbar controls\n`toolbar-group-history` | The group for histroy controls\n`toolbar-group-emphasis` | The group for emphasis controls\n`toolbar-group-heading` | The group for heading controls\n`toolbar-group-style` | The group for style controls\n`toolbar-group-glyph-transformation` | The group for glyph transformation controls\n`toolbar-group-list` | The group for list controls\n`toolbar-group-alignment` | The group for alignment controls\n`toolbar-group-rich-text` | The group for rich text controls\n`toolbar-group-block` | The group for preformatted block controls\n`toolbar-group-format` | The group for format controls\n`toolbar-button` | The toolbar button (applies to all buttons)\n`toolbar-button-pressed` | The toolbar button in pressed state (applies to all buttons)\n`toolbar-button-undo` | The \"undo\" button\n`toolbar-button-redo` | The \"redo\" button\n`toolbar-button-bold` | The \"bold\" button\n`toolbar-button-italic` | The \"italic\" button\n`toolbar-button-underline` | The \"underline\" button\n`toolbar-button-strike` | The \"strike-through\" button\n`toolbar-button-color` | The \"color\" button\n`toolbar-button-background` | The \"background\" button\n`toolbar-button-h1` | The \"header 1\" button\n`toolbar-button-h2` | The \"header 2\" button\n`toolbar-button-h3` | The \"header 3\" button\n`toolbar-button-subscript` | The \"subscript\" button\n`toolbar-button-superscript` | The \"superscript\" button\n`toolbar-button-list-ordered` | The \"ordered list\" button\n`toolbar-button-list-bullet` | The \"bullet list\" button\n`toolbar-button-align-left` | The \"left align\" button\n`toolbar-button-align-center` | The \"center align\" button\n`toolbar-button-align-right` | The \"right align\" button\n`toolbar-button-image` | The \"image\" button\n`toolbar-button-link` | The \"link\" button\n`toolbar-button-blockquote` | The \"blockquote\" button\n`toolbar-button-code-block` | The \"code block\" button\n`toolbar-button-clean` | The \"clean formatting\" button\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.",
11
+ "description": "`<vaadin-rich-text-editor>` is a Web Component for rich text editing.\nIt provides a set of toolbar controls to apply formatting on the content,\nwhich is stored and can be accessed as HTML5 or JSON string.\n\n```html\n<vaadin-rich-text-editor></vaadin-rich-text-editor>\n```\n\nVaadin Rich Text Editor focuses on the structure, not the styling of content.\nTherefore, the semantic HTML5 tags such as <h1>, <strong> and <ul> are used,\nand CSS usage is limited to most common cases, like horizontal text alignment.\n\n### Styling\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n-------------|------------------------------\n`disabled` | Set to a disabled text editor\n`readonly` | Set to a readonly text editor\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n-------------------------------------|----------------\n`content` | The content wrapper\n`toolbar` | The toolbar wrapper\n`toolbar-group` | The group for toolbar controls\n`toolbar-group-history` | The group for history controls\n`toolbar-group-emphasis` | The group for emphasis controls\n`toolbar-group-heading` | The group for heading controls\n`toolbar-group-style` | The group for style controls\n`toolbar-group-glyph-transformation` | The group for glyph transformation controls\n`toolbar-group-list` | The group for list controls\n`toolbar-group-indent` | The group for indentation controls\n`toolbar-group-alignment` | The group for alignment controls\n`toolbar-group-rich-text` | The group for rich text controls\n`toolbar-group-block` | The group for preformatted block controls\n`toolbar-group-format` | The group for format controls\n`toolbar-button` | The toolbar button (applies to all buttons)\n`toolbar-button-pressed` | The toolbar button in pressed state (applies to all buttons)\n`toolbar-button-undo` | The \"undo\" button\n`toolbar-button-redo` | The \"redo\" button\n`toolbar-button-bold` | The \"bold\" button\n`toolbar-button-italic` | The \"italic\" button\n`toolbar-button-underline` | The \"underline\" button\n`toolbar-button-strike` | The \"strike-through\" button\n`toolbar-button-color` | The \"color\" button\n`toolbar-button-background` | The \"background\" button\n`toolbar-button-h1` | The \"header 1\" button\n`toolbar-button-h2` | The \"header 2\" button\n`toolbar-button-h3` | The \"header 3\" button\n`toolbar-button-subscript` | The \"subscript\" button\n`toolbar-button-superscript` | The \"superscript\" button\n`toolbar-button-list-ordered` | The \"ordered list\" button\n`toolbar-button-list-bullet` | The \"bullet list\" button\n`toolbar-button-outdent` | The \"decrease indentation\" button\n`toolbar-button-indent` | The \"increase indentation\" button\n`toolbar-button-align-left` | The \"left align\" button\n`toolbar-button-align-center` | The \"center align\" button\n`toolbar-button-align-right` | The \"right align\" button\n`toolbar-button-image` | The \"image\" button\n`toolbar-button-link` | The \"link\" button\n`toolbar-button-blockquote` | The \"blockquote\" button\n`toolbar-button-code-block` | The \"code block\" button\n`toolbar-button-clean` | The \"clean formatting\" button\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.",
12
12
  "attributes": [
13
13
  {
14
14
  "name": "value",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/rich-text-editor",
4
- "version": "25.0.0-alpha14",
4
+ "version": "25.0.0-alpha16",
5
5
  "description-markup": "markdown",
6
6
  "framework": "lit",
7
7
  "framework-config": {
@@ -16,7 +16,7 @@
16
16
  "elements": [
17
17
  {
18
18
  "name": "vaadin-rich-text-editor",
19
- "description": "`<vaadin-rich-text-editor>` is a Web Component for rich text editing.\nIt provides a set of toolbar controls to apply formatting on the content,\nwhich is stored and can be accessed as HTML5 or JSON string.\n\n```html\n<vaadin-rich-text-editor></vaadin-rich-text-editor>\n```\n\nVaadin Rich Text Editor focuses on the structure, not the styling of content.\nTherefore, the semantic HTML5 tags such as <h1>, <strong> and <ul> are used,\nand CSS usage is limited to most common cases, like horizontal text alignment.\n\n### Styling\n\nThe following state attributes are available for styling:\n\nAttribute | Description | Part name\n-------------|-------------|------------\n`disabled` | Set to a disabled text editor | :host\n`readonly` | Set to a readonly text editor | :host\n`on` | Set to a toolbar button applied to the selected text | toolbar-button\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n-------------------------------------|----------------\n`content` | The content wrapper\n`toolbar` | The toolbar wrapper\n`toolbar-group` | The group for toolbar controls\n`toolbar-group-history` | The group for histroy controls\n`toolbar-group-emphasis` | The group for emphasis controls\n`toolbar-group-heading` | The group for heading controls\n`toolbar-group-style` | The group for style controls\n`toolbar-group-glyph-transformation` | The group for glyph transformation controls\n`toolbar-group-list` | The group for list controls\n`toolbar-group-alignment` | The group for alignment controls\n`toolbar-group-rich-text` | The group for rich text controls\n`toolbar-group-block` | The group for preformatted block controls\n`toolbar-group-format` | The group for format controls\n`toolbar-button` | The toolbar button (applies to all buttons)\n`toolbar-button-pressed` | The toolbar button in pressed state (applies to all buttons)\n`toolbar-button-undo` | The \"undo\" button\n`toolbar-button-redo` | The \"redo\" button\n`toolbar-button-bold` | The \"bold\" button\n`toolbar-button-italic` | The \"italic\" button\n`toolbar-button-underline` | The \"underline\" button\n`toolbar-button-strike` | The \"strike-through\" button\n`toolbar-button-color` | The \"color\" button\n`toolbar-button-background` | The \"background\" button\n`toolbar-button-h1` | The \"header 1\" button\n`toolbar-button-h2` | The \"header 2\" button\n`toolbar-button-h3` | The \"header 3\" button\n`toolbar-button-subscript` | The \"subscript\" button\n`toolbar-button-superscript` | The \"superscript\" button\n`toolbar-button-list-ordered` | The \"ordered list\" button\n`toolbar-button-list-bullet` | The \"bullet list\" button\n`toolbar-button-align-left` | The \"left align\" button\n`toolbar-button-align-center` | The \"center align\" button\n`toolbar-button-align-right` | The \"right align\" button\n`toolbar-button-image` | The \"image\" button\n`toolbar-button-link` | The \"link\" button\n`toolbar-button-blockquote` | The \"blockquote\" button\n`toolbar-button-code-block` | The \"code block\" button\n`toolbar-button-clean` | The \"clean formatting\" button\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.",
19
+ "description": "`<vaadin-rich-text-editor>` is a Web Component for rich text editing.\nIt provides a set of toolbar controls to apply formatting on the content,\nwhich is stored and can be accessed as HTML5 or JSON string.\n\n```html\n<vaadin-rich-text-editor></vaadin-rich-text-editor>\n```\n\nVaadin Rich Text Editor focuses on the structure, not the styling of content.\nTherefore, the semantic HTML5 tags such as <h1>, <strong> and <ul> are used,\nand CSS usage is limited to most common cases, like horizontal text alignment.\n\n### Styling\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n-------------|------------------------------\n`disabled` | Set to a disabled text editor\n`readonly` | Set to a readonly text editor\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n-------------------------------------|----------------\n`content` | The content wrapper\n`toolbar` | The toolbar wrapper\n`toolbar-group` | The group for toolbar controls\n`toolbar-group-history` | The group for history controls\n`toolbar-group-emphasis` | The group for emphasis controls\n`toolbar-group-heading` | The group for heading controls\n`toolbar-group-style` | The group for style controls\n`toolbar-group-glyph-transformation` | The group for glyph transformation controls\n`toolbar-group-list` | The group for list controls\n`toolbar-group-indent` | The group for indentation controls\n`toolbar-group-alignment` | The group for alignment controls\n`toolbar-group-rich-text` | The group for rich text controls\n`toolbar-group-block` | The group for preformatted block controls\n`toolbar-group-format` | The group for format controls\n`toolbar-button` | The toolbar button (applies to all buttons)\n`toolbar-button-pressed` | The toolbar button in pressed state (applies to all buttons)\n`toolbar-button-undo` | The \"undo\" button\n`toolbar-button-redo` | The \"redo\" button\n`toolbar-button-bold` | The \"bold\" button\n`toolbar-button-italic` | The \"italic\" button\n`toolbar-button-underline` | The \"underline\" button\n`toolbar-button-strike` | The \"strike-through\" button\n`toolbar-button-color` | The \"color\" button\n`toolbar-button-background` | The \"background\" button\n`toolbar-button-h1` | The \"header 1\" button\n`toolbar-button-h2` | The \"header 2\" button\n`toolbar-button-h3` | The \"header 3\" button\n`toolbar-button-subscript` | The \"subscript\" button\n`toolbar-button-superscript` | The \"superscript\" button\n`toolbar-button-list-ordered` | The \"ordered list\" button\n`toolbar-button-list-bullet` | The \"bullet list\" button\n`toolbar-button-outdent` | The \"decrease indentation\" button\n`toolbar-button-indent` | The \"increase indentation\" button\n`toolbar-button-align-left` | The \"left align\" button\n`toolbar-button-align-center` | The \"center align\" button\n`toolbar-button-align-right` | The \"right align\" button\n`toolbar-button-image` | The \"image\" button\n`toolbar-button-link` | The \"link\" button\n`toolbar-button-blockquote` | The \"blockquote\" button\n`toolbar-button-code-block` | The \"code block\" button\n`toolbar-button-clean` | The \"clean formatting\" button\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.",
20
20
  "extension": true,
21
21
  "attributes": [
22
22
  {