@vaadin/rich-text-editor 25.0.0-alpha7 → 25.0.0-alpha9

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-alpha7",
3
+ "version": "25.0.0-alpha9",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -21,6 +21,8 @@
21
21
  "type": "module",
22
22
  "files": [
23
23
  "src",
24
+ "!src/styles/*-base-styles.d.ts",
25
+ "!src/styles/*-base-styles.js",
24
26
  "theme",
25
27
  "vaadin-*.d.ts",
26
28
  "vaadin-*.js",
@@ -36,20 +38,20 @@
36
38
  ],
37
39
  "dependencies": {
38
40
  "@open-wc/dedupe-mixin": "^1.3.0",
39
- "@vaadin/button": "25.0.0-alpha7",
40
- "@vaadin/component-base": "25.0.0-alpha7",
41
- "@vaadin/confirm-dialog": "25.0.0-alpha7",
42
- "@vaadin/overlay": "25.0.0-alpha7",
43
- "@vaadin/text-field": "25.0.0-alpha7",
44
- "@vaadin/tooltip": "25.0.0-alpha7",
45
- "@vaadin/vaadin-lumo-styles": "25.0.0-alpha7",
46
- "@vaadin/vaadin-themable-mixin": "25.0.0-alpha7",
41
+ "@vaadin/button": "25.0.0-alpha9",
42
+ "@vaadin/component-base": "25.0.0-alpha9",
43
+ "@vaadin/confirm-dialog": "25.0.0-alpha9",
44
+ "@vaadin/overlay": "25.0.0-alpha9",
45
+ "@vaadin/text-field": "25.0.0-alpha9",
46
+ "@vaadin/tooltip": "25.0.0-alpha9",
47
+ "@vaadin/vaadin-lumo-styles": "25.0.0-alpha9",
48
+ "@vaadin/vaadin-themable-mixin": "25.0.0-alpha9",
47
49
  "lit": "^3.0.0"
48
50
  },
49
51
  "devDependencies": {
50
- "@vaadin/a11y-base": "25.0.0-alpha7",
51
- "@vaadin/chai-plugins": "25.0.0-alpha7",
52
- "@vaadin/test-runner-commands": "25.0.0-alpha7",
52
+ "@vaadin/a11y-base": "25.0.0-alpha9",
53
+ "@vaadin/chai-plugins": "25.0.0-alpha9",
54
+ "@vaadin/test-runner-commands": "25.0.0-alpha9",
53
55
  "@vaadin/testing-helpers": "^2.0.0",
54
56
  "sinon": "^18.0.0"
55
57
  },
@@ -58,5 +60,5 @@
58
60
  "web-types.json",
59
61
  "web-types.lit.json"
60
62
  ],
61
- "gitHead": "87f72707ce6866892f8be5782fa0da008e87dcbc"
63
+ "gitHead": "bbe4720721e0955ffc87a79b412bee38b1f0eb1e"
62
64
  }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2000 - 2025 Vaadin Ltd.
4
+ *
5
+ * This program is available under Vaadin Commercial License and Service Terms.
6
+ *
7
+ *
8
+ * See https://vaadin.com/commercial-license-and-service-terms for the full
9
+ * license.
10
+ */
11
+ import { css } from 'lit';
12
+
13
+ export const icons = css`
14
+ :host {
15
+ --_vaadin-icon-align-center: url('data:image/svg+xml;utf8,<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M18 10H6M21 6H3M21 14H3M18 18H6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>');
16
+ --_vaadin-icon-align-left: url('data:image/svg+xml;utf8,<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M16 10H3M20 6H3M20 14H3M16 18H3" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>');
17
+ --_vaadin-icon-align-right: url('data:image/svg+xml;utf8,<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M21 10H8M21 6H4M21 14H4M21 18H8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>');
18
+ --_vaadin-icon-background: url('data:image/svg+xml,<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M14.6 12L12 9.4L8 13.4L10.575 16L14.6 12ZM13.425 8L16 10.575L20 6.6L17.4 4L13.425 8ZM11.325 7.275L16.725 12.675L12 17.425C11.6 17.825 11.1292 18.025 10.5875 18.025C10.0458 18.025 9.575 17.825 9.175 17.425L8.5 18H3.5L6.65 14.875C6.25 14.475 6.04167 13.9958 6.025 13.4375C6.00833 12.8792 6.2 12.4 6.6 12L11.325 7.275ZM11.325 7.275L16 2.6C16.4 2.2 16.8708 2 17.4125 2C17.9542 2 18.425 2.2 18.825 2.6L21.425 5.175C21.825 5.575 22.025 6.04583 22.025 6.5875C22.025 7.12917 21.825 7.6 21.425 8L16.725 12.675L11.325 7.275Z" fill="currentColor"/></svg>');
19
+ --_vaadin-icon-bold: 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-linejoin="round" d="M6.75 3.744h-.753v8.25h7.125a4.125 4.125 0 0 0 0-8.25H6.75Zm0 0v.38m0 16.122h6.747a4.5 4.5 0 0 0 0-9.001h-7.5v9h.753Zm0 0v-.37m0-15.751h6a3.75 3.75 0 1 1 0 7.5h-6m0-7.5v7.5m0 0v8.25m0-8.25h6.375a4.125 4.125 0 0 1 0 8.25H6.75m.747-15.38h4.875a3.375 3.375 0 0 1 0 6.75H7.497v-6.75Zm0 7.5h5.25a3.75 3.75 0 0 1 0 7.5h-5.25v-7.5Z" /></svg>');
20
+ --_vaadin-icon-clear: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor"><path d="m528-546-93-93-121-121h486v120H568l-40 94ZM792-56 460-388l-80 188H249l119-280L56-792l56-56 736 736-56 56Z"/></svg>');
21
+ --_vaadin-icon-code: url('data:image/svg+xml,<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M16 18L22 12L16 6M8 6L2 12L8 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>');
22
+ --_vaadin-icon-color: url('data:image/svg+xml,<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5.5 17L10.75 3H13.25L18.5 17H16.1L14.85 13.4H9.2L7.9 17H5.5ZM9.9 11.4H14.1L12.05 5.6H11.95L9.9 11.4Z" fill="currentColor"/></svg>');
23
+ --_vaadin-icon-color-underline: url('data:image/svg+xml,<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M2 24V20H22V24H2Z" fill="currentColor"/></svg>');
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
+ --_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
+ --_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-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
+ --_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
+ --_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>');
30
+ --_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
+ --_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
+ --_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>');
33
+ --_vaadin-icon-superscript: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor"><path d="M760-600v-80q0-17 11.5-28.5T800-720h80v-40H760v-40h120q17 0 28.5 11.5T920-760v40q0 17-11.5 28.5T880-680h-80v40h120v40H760ZM235-160l185-291-172-269h106l124 200h4l123-200h107L539-451l186 291H618L482-377h-4L342-160H235Z"/></svg>');
34
+ --_vaadin-icon-underline: 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="M17.995 3.744v7.5a6 6 0 1 1-12 0v-7.5m-2.25 16.502h16.5" /></svg>');
35
+ }
36
+ `;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2000 - 2025 Vaadin Ltd.
4
+ *
5
+ * This program is available under Vaadin Commercial License and Service Terms.
6
+ *
7
+ *
8
+ * See https://vaadin.com/commercial-license-and-service-terms for the full
9
+ * license.
10
+ */
11
+ import type { CSSResult } from 'lit';
12
+
13
+ export const richTextEditorStyles: CSSResult;
@@ -0,0 +1,346 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2000 - 2025 Vaadin Ltd.
4
+ *
5
+ * This program is available under Vaadin Commercial License and Service Terms.
6
+ *
7
+ *
8
+ * See https://vaadin.com/commercial-license-and-service-terms for the full
9
+ * license.
10
+ */
11
+ import { css } from 'lit';
12
+ import { icons } from './vaadin-rich-text-editor-base-icons.js';
13
+
14
+ const base = css`
15
+ :host {
16
+ background: var(--vaadin-rich-text-editor-background, var(--vaadin-background-color));
17
+ border: var(--vaadin-input-field-border-width, 1px) solid
18
+ var(--vaadin-input-field-border-color, var(--vaadin-border-color-strong));
19
+ border-radius: var(--vaadin-input-field-border-radius, var(--vaadin-radius-m));
20
+ box-sizing: border-box;
21
+ display: flex;
22
+ flex-direction: column;
23
+ overflow: hidden;
24
+ }
25
+
26
+ :host(:focus-within) {
27
+ outline: var(--vaadin-focus-ring-width) solid var(--vaadin-focus-ring-color);
28
+ outline-offset: -1px;
29
+ }
30
+
31
+ :host([hidden]) {
32
+ display: none !important;
33
+ }
34
+
35
+ .announcer {
36
+ clip: rect(0, 0, 0, 0);
37
+ position: fixed;
38
+ }
39
+
40
+ input[type='file'] {
41
+ display: none;
42
+ }
43
+
44
+ .vaadin-rich-text-editor-container {
45
+ display: flex;
46
+ flex: auto;
47
+ flex-direction: column;
48
+ max-height: inherit;
49
+ min-height: inherit;
50
+ }
51
+ `;
52
+
53
+ export const content = css`
54
+ [part='content'] {
55
+ box-sizing: border-box;
56
+ display: flex;
57
+ flex: auto;
58
+ flex-direction: column;
59
+ overflow: hidden;
60
+ position: relative;
61
+ }
62
+
63
+ /*
64
+ Quill core styles.
65
+ CSS selectors removed: margin & padding reset, check list, indentation, video, colors, ordered & unordered list, h1-6, anchor
66
+ */
67
+ .ql-clipboard {
68
+ height: 1px;
69
+ left: -100000px;
70
+ overflow-y: hidden;
71
+ position: absolute;
72
+ top: 50%;
73
+ }
74
+
75
+ .ql-clipboard p {
76
+ margin: 0;
77
+ padding: 0;
78
+ }
79
+
80
+ .ql-editor {
81
+ box-sizing: border-box;
82
+ color: var(--vaadin-rich-text-editor-editor-color, var(--vaadin-color));
83
+ flex: 1;
84
+ font-size: var(--vaadin-rich-text-editor-editor-font-size, inherit);
85
+ height: 100%;
86
+ line-height: var(--vaadin-rich-text-editor-editor-line-height, inherit);
87
+ outline: none;
88
+ overflow-y: auto;
89
+ padding: var(--vaadin-rich-text-editor-editor-padding, var(--vaadin-padding-container));
90
+ tab-size: 4;
91
+ -moz-tab-size: 4;
92
+ text-align: left;
93
+ white-space: pre-wrap;
94
+ word-wrap: break-word;
95
+ }
96
+
97
+ .ql-editor > * {
98
+ cursor: text;
99
+ }
100
+
101
+ .ql-align-left {
102
+ text-align: left;
103
+ }
104
+
105
+ .ql-direction-rtl {
106
+ direction: rtl;
107
+ text-align: inherit;
108
+ }
109
+
110
+ .ql-align-center {
111
+ text-align: center;
112
+ }
113
+
114
+ .ql-align-justify {
115
+ text-align: justify;
116
+ }
117
+
118
+ .ql-align-right {
119
+ text-align: right;
120
+ }
121
+ /* quill core end */
122
+
123
+ blockquote {
124
+ border-inline-start: 4px solid var(--vaadin-border-color);
125
+ margin: var(--vaadin-padding-container);
126
+ padding-inline-start: var(--vaadin-padding);
127
+ }
128
+
129
+ code,
130
+ pre {
131
+ background-color: var(--vaadin-background-container);
132
+ border-radius: var(--vaadin-radius-s);
133
+ }
134
+
135
+ pre {
136
+ white-space: pre-wrap;
137
+ margin-block: var(--vaadin-padding);
138
+ padding: var(--vaadin-padding-container);
139
+ }
140
+
141
+ code {
142
+ padding: 0.125rem 0.25rem;
143
+ }
144
+
145
+ img {
146
+ max-width: 100%;
147
+ }
148
+
149
+ /* RTL specific styles */
150
+ :host([dir='rtl']) .ql-editor {
151
+ direction: rtl;
152
+ text-align: right;
153
+ }
154
+ `;
155
+
156
+ const toolbar = css`
157
+ [part='toolbar'] {
158
+ background-color: var(--vaadin-rich-text-editor-toolbar-background, var(--vaadin-background-container));
159
+ display: flex;
160
+ flex-shrink: 0;
161
+ flex-wrap: wrap;
162
+ gap: var(--vaadin-rich-text-editor-toolbar-gap, var(--vaadin-gap-container-inline));
163
+ padding: var(--vaadin-rich-text-editor-toolbar-padding, var(--vaadin-padding));
164
+ }
165
+
166
+ [part~='toolbar-group'] {
167
+ display: flex;
168
+ }
169
+
170
+ [part~='toolbar-button'] {
171
+ background: var(--vaadin-rich-text-editor-toolbar-button-background, var(--vaadin-background-container));
172
+ border: var(--vaadin-rich-text-editor-toolbar-button-border-width, 1px) solid
173
+ var(--vaadin-rich-text-editor-toolbar-button-border-color, transparent);
174
+ border-radius: var(--vaadin-rich-text-editor-toolbar-button-border-radius, var(--vaadin-radius-m));
175
+ color: var(--vaadin-rich-text-editor-toolbar-button-text-color, var(--vaadin-color));
176
+ cursor: var(--vaadin-clickable-cursor);
177
+ flex-shrink: 0;
178
+ font-family: var(--vaadin-rich-text-editor-toolbar-button-font-family, inherit);
179
+ font-size: var(--vaadin-rich-text-editor-toolbar-button-font-size, inherit);
180
+ font-weight: var(--vaadin-rich-text-editor-toolbar-button-font-weight, 500);
181
+ height: var(--vaadin-rich-text-editor-toolbar-button-height, auto);
182
+ line-height: var(--vaadin-rich-text-editor-toolbar-button-line-height, inherit);
183
+ padding: var(--vaadin-rich-text-editor-toolbar-button-padding, var(--vaadin-padding-container));
184
+ position: relative;
185
+ }
186
+
187
+ [part~='toolbar-button']::before {
188
+ background: currentcolor;
189
+ content: '';
190
+ display: block;
191
+ height: var(--vaadin-icon-size, 1lh);
192
+ width: var(--vaadin-icon-size, 1lh);
193
+ }
194
+
195
+ [part~='toolbar-button']:focus {
196
+ outline: var(--vaadin-focus-ring-width) solid var(--vaadin-focus-ring-color);
197
+ outline-offset: 1px;
198
+ z-index: 1;
199
+ }
200
+
201
+ [part~='toolbar-button'][on],
202
+ [part~='toolbar-button'][aria-expanded='true'] {
203
+ --vaadin-rich-text-editor-toolbar-button-background: var(--vaadin-background-container-strong);
204
+ }
205
+
206
+ [part~='toolbar-button-undo']::before {
207
+ mask-image: var(--_vaadin-icon-undo);
208
+ }
209
+
210
+ [part~='toolbar-button-redo']::before {
211
+ mask-image: var(--_vaadin-icon-redo);
212
+ }
213
+
214
+ [part~='toolbar-button-bold']::before {
215
+ mask-image: var(--_vaadin-icon-bold);
216
+ }
217
+
218
+ [part~='toolbar-button-italic']::before {
219
+ mask-image: var(--_vaadin-icon-italic);
220
+ }
221
+
222
+ [part~='toolbar-button-underline']::before {
223
+ mask-image: var(--_vaadin-icon-underline);
224
+ }
225
+
226
+ [part~='toolbar-button-strike']::before {
227
+ mask-image: var(--_vaadin-icon-strikethrough);
228
+ }
229
+
230
+ [part~='toolbar-button-color']::before {
231
+ mask-image: var(--_vaadin-icon-color);
232
+ }
233
+
234
+ [part~='toolbar-button-color']::after {
235
+ background-color: var(--_color-value, currentColor);
236
+ }
237
+
238
+ [part~='toolbar-button-background']::before {
239
+ mask-image: var(--_vaadin-icon-background);
240
+ }
241
+
242
+ [part~='toolbar-button-background']::after {
243
+ background-color: var(--_background-value, currentColor);
244
+ }
245
+
246
+ [part~='toolbar-button-color']::after,
247
+ [part~='toolbar-button-background']::after {
248
+ bottom: 50%;
249
+ content: '';
250
+ display: block;
251
+ height: var(--vaadin-icon-size, 1lh);
252
+ mask-image: var(--_vaadin-icon-color-underline);
253
+ position: absolute;
254
+ transform: translateY(50%);
255
+ width: var(--vaadin-icon-size, 1lh);
256
+ }
257
+
258
+ [part~='toolbar-button-h1']::before {
259
+ mask-image: var(--_vaadin-icon-h1);
260
+ }
261
+
262
+ [part~='toolbar-button-h2']::before {
263
+ mask-image: var(--_vaadin-icon-h2);
264
+ }
265
+
266
+ [part~='toolbar-button-h3']::before {
267
+ mask-image: var(--_vaadin-icon-h3);
268
+ }
269
+
270
+ [part~='toolbar-button-subscript']::before {
271
+ mask-image: var(--_vaadin-icon-subscript);
272
+ }
273
+
274
+ [part~='toolbar-button-superscript']::before {
275
+ mask-image: var(--_vaadin-icon-superscript);
276
+ }
277
+
278
+ [part~='toolbar-button-list-ordered']::before {
279
+ mask-image: var(--_vaadin-icon-list-number);
280
+ }
281
+
282
+ [part~='toolbar-button-list-bullet']::before {
283
+ mask-image: var(--_vaadin-icon-list-bullet);
284
+ }
285
+
286
+ [part~='toolbar-button-align-left']::before {
287
+ mask-image: var(--_vaadin-icon-align-left);
288
+ }
289
+
290
+ [part~='toolbar-button-align-center']::before {
291
+ mask-image: var(--_vaadin-icon-align-center);
292
+ }
293
+
294
+ [part~='toolbar-button-align-right']::before {
295
+ mask-image: var(--_vaadin-icon-align-right);
296
+ }
297
+
298
+ [part~='toolbar-button-image']::before {
299
+ mask-image: var(--_vaadin-icon-image);
300
+ }
301
+
302
+ [part~='toolbar-button-link']::before {
303
+ mask-image: var(--_vaadin-icon-link);
304
+ }
305
+
306
+ [part~='toolbar-button-blockquote']::before {
307
+ mask-image: var(--_vaadin-icon-quote);
308
+ }
309
+
310
+ [part~='toolbar-button-code-block']::before {
311
+ mask-image: var(--_vaadin-icon-code);
312
+ }
313
+
314
+ [part~='toolbar-button-clean']::before {
315
+ mask-image: var(--_vaadin-icon-clear);
316
+ }
317
+
318
+ @media (forced-colors: active) {
319
+ [part~='toolbar-button']::before {
320
+ background: CanvasText;
321
+ }
322
+
323
+ [part~='toolbar-button'][on] {
324
+ background: Highlight;
325
+ }
326
+
327
+ [part~='toolbar-button'][on]::before {
328
+ background: HighlightText;
329
+ }
330
+ }
331
+ `;
332
+
333
+ const states = css`
334
+ :host([readonly]) [part='toolbar'] {
335
+ display: none;
336
+ }
337
+
338
+ :host([disabled]) {
339
+ pointer-events: none;
340
+ opacity: 0.5;
341
+ -webkit-user-select: none;
342
+ user-select: none;
343
+ }
344
+ `;
345
+
346
+ export const richTextEditorStyles = [icons, base, content, toolbar, states];
@@ -0,0 +1,43 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2000 - 2025 Vaadin Ltd.
4
+ *
5
+ * This program is available under Vaadin Commercial License and Service Terms.
6
+ *
7
+ *
8
+ * See https://vaadin.com/commercial-license-and-service-terms for the full
9
+ * license.
10
+ */
11
+ import { css } from 'lit';
12
+ import { overlayStyles } from '@vaadin/overlay/src/styles/vaadin-overlay-base-styles.js';
13
+
14
+ export const richTextEditorPopupOverlay = css`
15
+ [part='overlay'] {
16
+ padding: var(--vaadin-rich-text-editor-overlay-padding, var(--vaadin-padding-container));
17
+ }
18
+
19
+ [part='content'] {
20
+ display: grid;
21
+ gap: var(--vaadin-rich-text-editor-overlay-gap, var(--vaadin-gap-container-inline));
22
+ grid-template-columns: repeat(7, minmax(0, 1fr));
23
+ }
24
+
25
+ [part='content'] ::slotted(button) {
26
+ border: var(--vaadin-rich-text-editor-overlay-color-option-border-width, 1px) solid
27
+ var(--vaadin-rich-text-editor-overlay-color-option-border-color, transparent);
28
+ border-radius: var(--vaadin-rich-text-editor-overlay-color-option-border-radius, 9999px);
29
+ cursor: var(--vaadin-clickable-cursor);
30
+ font-size: var(--vaadin-rich-text-editor-overlay-color-option-font-size, inherit);
31
+ height: var(--vaadin-rich-text-editor-overlay-color-option-height, 1lh);
32
+ line-height: var(--vaadin-rich-text-editor-overlay-color-option-line-height, inherit);
33
+ padding: var(--vaadin-rich-text-editor-overlay-color-option-padding, 0);
34
+ width: var(--vaadin-rich-text-editor-overlay-color-option-width, 1lh);
35
+ }
36
+
37
+ [part='content'] ::slotted(button:focus-visible) {
38
+ outline: var(--vaadin-focus-ring-width) solid var(--vaadin-focus-ring-color);
39
+ outline-offset: 1px;
40
+ }
41
+ `;
42
+
43
+ export const richTextEditorPopupOverlayStyles = [overlayStyles, richTextEditorPopupOverlay];
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2000 - 2025 Vaadin Ltd.
4
+ *
5
+ * This program is available under Vaadin Commercial License and Service Terms.
6
+ *
7
+ *
8
+ * See https://vaadin.com/commercial-license-and-service-terms for the full
9
+ * license.
10
+ */
11
+ import { overlayStyles } from '@vaadin/overlay/src/styles/vaadin-overlay-core-styles.js';
12
+
13
+ export const richTextEditorPopupOverlayStyles = [overlayStyles];
@@ -176,7 +176,7 @@ export const RichTextEditorMixin = (superClass) =>
176
176
  '#ffffff', '#facccc', '#ffebcc', '#ffffcc', '#cce8cc', '#cce0f5', '#ebd6ff',
177
177
  '#bbbbbb', '#f06666', '#ffc266', '#ffff66', '#66b966', '#66a3e0', '#c285ff',
178
178
  '#888888', '#a10000', '#b26b00', '#b2b200', '#006100', '#0047b2', '#6b24b2',
179
- '#444444', '#5c0000', '#663d00', '#666600', '#003700', '#002966', '#3d1466'
179
+ '#444444', '#5c0000', '#663d00', '#666600', '#003700', '#002966', '#3d1466',
180
180
  ];
181
181
  },
182
182
  },
@@ -416,13 +416,32 @@ export const RichTextEditorMixin = (superClass) =>
416
416
  // Flush pending htmlValue only once the editor is fully initialized
417
417
  this.__flushPendingHtmlValue();
418
418
 
419
- this.$.backgroundPopup.target = this.shadowRoot.querySelector('#btn-background');
420
- this.$.colorPopup.target = this.shadowRoot.querySelector('#btn-color');
419
+ this.querySelector('[slot="color-popup"]').target = this.shadowRoot.querySelector('#btn-color');
420
+ this.querySelector('[slot="background-popup"]').target = this.shadowRoot.querySelector('#btn-background');
421
+
422
+ // Set up tooltip to show when hovering or focusing toolbar buttons
423
+ this._tooltip = document.createElement('vaadin-tooltip');
424
+ this._tooltip.slot = 'tooltip';
425
+ // Create dummy aria target, as toolbar buttons already have aria-label, and also cannot be linked with the
426
+ // tooltip being in the light DOM
427
+ this._tooltip.ariaTarget = document.createElement('div');
428
+ this.append(this._tooltip);
429
+
430
+ const buttons = this.shadowRoot.querySelectorAll('[part~="toolbar-button"]');
431
+ buttons.forEach((button) => {
432
+ button.addEventListener('mouseenter', this.__showTooltip.bind(this));
433
+ button.addEventListener('focusin', this.__showTooltip.bind(this));
434
+ });
435
+ }
421
436
 
422
- requestAnimationFrame(() => {
423
- this.$.linkDialog.$.dialog.$.overlay.addEventListener('vaadin-overlay-open', () => {
424
- this.$.linkUrl.focus();
425
- });
437
+ /** @private */
438
+ __showTooltip(event) {
439
+ const target = event.target;
440
+ this._tooltip.target = target;
441
+ this._tooltip.text = target.ariaLabel;
442
+ this._tooltip._stateController.open({
443
+ focus: event.type === 'focusin',
444
+ hover: event.type === 'mouseenter',
426
445
  });
427
446
  }
428
447
 
@@ -652,7 +671,8 @@ export const RichTextEditorMixin = (superClass) =>
652
671
  if (e.keyCode === 13) {
653
672
  e.preventDefault();
654
673
  e.stopPropagation();
655
- this.$.confirmLink.click();
674
+ this._onLinkEditConfirm();
675
+ this._closeLinkDialog();
656
676
  }
657
677
  }
658
678
 
@@ -799,10 +819,7 @@ export const RichTextEditorMixin = (superClass) =>
799
819
  timeOut.after(timeout),
800
820
  () => {
801
821
  const formatting = Array.from(this.shadowRoot.querySelectorAll('[part="toolbar"] .ql-active'))
802
- .map((button) => {
803
- const tooltip = this.shadowRoot.querySelector(`[for="${button.id}"]`);
804
- return tooltip.text;
805
- })
822
+ .map((button) => button.getAttribute('aria-label'))
806
823
  .join(', ');
807
824
  announcer.textContent = formatting;
808
825
  },
@@ -12,11 +12,11 @@ import { css, html, LitElement, render } from 'lit';
12
12
  import { defineCustomElement } from '@vaadin/component-base/src/define.js';
13
13
  import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
14
14
  import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
15
- import { overlayStyles } from '@vaadin/overlay/src/styles/vaadin-overlay-core-styles.js';
16
15
  import { OverlayMixin } from '@vaadin/overlay/src/vaadin-overlay-mixin.js';
17
16
  import { PositionMixin } from '@vaadin/overlay/src/vaadin-overlay-position-mixin.js';
18
17
  import { LumoInjectionMixin } from '@vaadin/vaadin-themable-mixin/lumo-injection-mixin.js';
19
18
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
19
+ import { richTextEditorPopupOverlayStyles } from './styles/vaadin-rich-text-editor-popup-overlay-core-styles.js';
20
20
 
21
21
  /**
22
22
  * An element used internally by `<vaadin-rich-text-editor>`. Not intended to be used separately.
@@ -32,8 +32,15 @@ class RichTextEditorPopup extends PolylitMixin(LitElement) {
32
32
 
33
33
  static get styles() {
34
34
  return css`
35
- :host {
36
- display: none;
35
+ :host([opened]),
36
+ :host([opening]),
37
+ :host([closing]) {
38
+ display: contents !important;
39
+ }
40
+
41
+ :host,
42
+ :host([hidden]) {
43
+ display: none !important;
37
44
  }
38
45
  `;
39
46
  }
@@ -46,16 +53,13 @@ class RichTextEditorPopup extends PolylitMixin(LitElement) {
46
53
 
47
54
  opened: {
48
55
  type: Boolean,
56
+ reflectToAttribute: true,
49
57
  notify: true,
50
58
  },
51
59
 
52
60
  colors: {
53
61
  type: Array,
54
62
  },
55
-
56
- renderer: {
57
- type: Object,
58
- },
59
63
  };
60
64
  }
61
65
 
@@ -67,16 +71,20 @@ class RichTextEditorPopup extends PolylitMixin(LitElement) {
67
71
  render() {
68
72
  return html`
69
73
  <vaadin-rich-text-editor-popup-overlay
70
- .renderer="${this.renderer}"
74
+ id="overlay"
75
+ .owner="${this}"
71
76
  .opened="${this.opened}"
72
77
  .positionTarget="${this.target}"
73
78
  no-vertical-overlap
74
79
  horizontal-align="start"
75
80
  vertical-align="top"
76
81
  focus-trap
82
+ exportparts="overlay, content"
77
83
  @opened-changed="${this._onOpenedChanged}"
78
84
  @vaadin-overlay-escape-press="${this._onOverlayEscapePress}"
79
- ></vaadin-rich-text-editor-popup-overlay>
85
+ >
86
+ <slot></slot>
87
+ </vaadin-rich-text-editor-popup-overlay>
80
88
  `;
81
89
  }
82
90
 
@@ -98,19 +106,17 @@ class RichTextEditorPopup extends PolylitMixin(LitElement) {
98
106
 
99
107
  /** @private */
100
108
  __colorsChanged(colors) {
101
- this.renderer = (root) => {
102
- render(
103
- html`
104
- ${colors.map(
105
- (color) => html`
106
- <button data-color="${color}" style="background: ${color}" @click="${this._onColorClick}"></button>
107
- `,
108
- )}
109
- `,
110
- root,
111
- { host: this },
112
- );
113
- };
109
+ render(
110
+ html`
111
+ ${colors.map(
112
+ (color) => html`
113
+ <button data-color="${color}" style="background: ${color}" @click="${this._onColorClick}"></button>
114
+ `,
115
+ )}
116
+ `,
117
+ this,
118
+ { host: this },
119
+ );
114
120
  }
115
121
 
116
122
  /** @private */
@@ -137,25 +143,44 @@ export { RichTextEditorPopup };
137
143
  * @private
138
144
  */
139
145
  class RichTextEditorPopupOverlay extends PositionMixin(
140
- OverlayMixin(DirMixin(ThemableMixin(LumoInjectionMixin(PolylitMixin(LitElement))))),
146
+ OverlayMixin(DirMixin(ThemableMixin(PolylitMixin(LumoInjectionMixin(LitElement))))),
141
147
  ) {
142
148
  static get is() {
143
149
  return 'vaadin-rich-text-editor-popup-overlay';
144
150
  }
145
151
 
146
152
  static get styles() {
147
- return overlayStyles;
153
+ return richTextEditorPopupOverlayStyles;
148
154
  }
149
155
 
150
156
  /** @protected */
151
157
  render() {
152
158
  return html`
153
- <div id="backdrop" part="backdrop" hidden></div>
154
159
  <div part="overlay" id="overlay">
155
- <div part="content" id="content"><slot></slot></div>
160
+ <div part="content" id="content">
161
+ <slot></slot>
162
+ </div>
156
163
  </div>
157
164
  `;
158
165
  }
166
+
167
+ /**
168
+ * Override method from OverlayFocusMixin to use owner as content root
169
+ * @protected
170
+ * @override
171
+ */
172
+ get _contentRoot() {
173
+ return this.owner;
174
+ }
175
+
176
+ /**
177
+ * Override method from OverlayFocusMixin to use owner as modal root
178
+ * @protected
179
+ * @override
180
+ */
181
+ get _modalRoot() {
182
+ return this.owner;
183
+ }
159
184
  }
160
185
 
161
186
  defineCustomElement(RichTextEditorPopupOverlay);
@@ -29,7 +29,7 @@ export interface RichTextEditorEventMap extends HTMLElementEventMap, RichTextEdi
29
29
  * It provides a set of toolbar controls to apply formatting on the content,
30
30
  * which is stored and can be accessed as HTML5 or JSON string.
31
31
  *
32
- * ```
32
+ * ```html
33
33
  * <vaadin-rich-text-editor></vaadin-rich-text-editor>
34
34
  * ```
35
35
  *
@@ -13,7 +13,7 @@ import '@vaadin/confirm-dialog/src/vaadin-confirm-dialog.js';
13
13
  import '@vaadin/text-field/src/vaadin-text-field.js';
14
14
  import '@vaadin/tooltip/src/vaadin-tooltip.js';
15
15
  import './vaadin-rich-text-editor-popup.js';
16
- import { html, LitElement } from 'lit';
16
+ import { html, LitElement, render } from 'lit';
17
17
  import { defineCustomElement } from '@vaadin/component-base/src/define.js';
18
18
  import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
19
19
  import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
@@ -27,7 +27,7 @@ import { RichTextEditorMixin } from './vaadin-rich-text-editor-mixin.js';
27
27
  * It provides a set of toolbar controls to apply formatting on the content,
28
28
  * which is stored and can be accessed as HTML5 or JSON string.
29
29
  *
30
- * ```
30
+ * ```html
31
31
  * <vaadin-rich-text-editor></vaadin-rich-text-editor>
32
32
  * ```
33
33
  *
@@ -101,7 +101,7 @@ import { RichTextEditorMixin } from './vaadin-rich-text-editor-mixin.js';
101
101
  * @mixes ThemableMixin
102
102
  */
103
103
  class RichTextEditor extends RichTextEditorMixin(
104
- ElementMixin(ThemableMixin(LumoInjectionMixin(PolylitMixin(LitElement)))),
104
+ ElementMixin(ThemableMixin(PolylitMixin(LumoInjectionMixin(LitElement)))),
105
105
  ) {
106
106
  static get is() {
107
107
  return 'vaadin-rich-text-editor';
@@ -127,35 +127,51 @@ class RichTextEditor extends RichTextEditorMixin(
127
127
  id="btn-undo"
128
128
  type="button"
129
129
  part="toolbar-button toolbar-button-undo"
130
+ aria-label="${this.__effectiveI18n.undo}"
130
131
  @click="${this._undo}"
131
132
  ></button>
132
- <vaadin-tooltip for="btn-undo" .text="${this.__effectiveI18n.undo}"></vaadin-tooltip>
133
133
 
134
134
  <button
135
135
  id="btn-redo"
136
136
  type="button"
137
137
  part="toolbar-button toolbar-button-redo"
138
+ aria-label="${this.__effectiveI18n.redo}"
138
139
  @click="${this._redo}"
139
140
  ></button>
140
- <vaadin-tooltip for="btn-redo" .text="${this.__effectiveI18n.redo}"></vaadin-tooltip>
141
141
  </span>
142
142
 
143
143
  <span part="toolbar-group toolbar-group-emphasis">
144
144
  <!-- Bold -->
145
- <button id="btn-bold" class="ql-bold" part="toolbar-button toolbar-button-bold"></button>
146
- <vaadin-tooltip for="btn-bold" .text="${this.__effectiveI18n.bold}"></vaadin-tooltip>
145
+ <button
146
+ id="btn-bold"
147
+ class="ql-bold"
148
+ part="toolbar-button toolbar-button-bold"
149
+ aria-label="${this.__effectiveI18n.bold}"
150
+ ></button>
147
151
 
148
152
  <!-- Italic -->
149
- <button id="btn-italic" class="ql-italic" part="toolbar-button toolbar-button-italic"></button>
150
- <vaadin-tooltip for="btn-italic" .text="${this.__effectiveI18n.italic}"></vaadin-tooltip>
153
+ <button
154
+ id="btn-italic"
155
+ class="ql-italic"
156
+ part="toolbar-button toolbar-button-italic"
157
+ aria-label="${this.__effectiveI18n.italic}"
158
+ ></button>
151
159
 
152
160
  <!-- Underline -->
153
- <button id="btn-underline" class="ql-underline" part="toolbar-button toolbar-button-underline"></button>
154
- <vaadin-tooltip for="btn-underline" .text="${this.__effectiveI18n.underline}"></vaadin-tooltip>
161
+ <button
162
+ id="btn-underline"
163
+ class="ql-underline"
164
+ part="toolbar-button toolbar-button-underline"
165
+ aria-label="${this.__effectiveI18n.underline}"
166
+ ></button>
155
167
 
156
168
  <!-- Strike -->
157
- <button id="btn-strike" class="ql-strike" part="toolbar-button toolbar-button-strike"></button>
158
- <vaadin-tooltip for="btn-strike" .text="${this.__effectiveI18n.strike}"></vaadin-tooltip>
169
+ <button
170
+ id="btn-strike"
171
+ class="ql-strike"
172
+ part="toolbar-button toolbar-button-strike"
173
+ aria-label="${this.__effectiveI18n.strike}"
174
+ ></button>
159
175
  </span>
160
176
 
161
177
  <span part="toolbar-group toolbar-group-style">
@@ -164,17 +180,17 @@ class RichTextEditor extends RichTextEditorMixin(
164
180
  id="btn-color"
165
181
  type="button"
166
182
  part="toolbar-button toolbar-button-color"
183
+ aria-label="${this.__effectiveI18n.color}"
167
184
  @click="${this.__onColorClick}"
168
185
  ></button>
169
- <vaadin-tooltip for="btn-color" .text="${this.__effectiveI18n.color}"></vaadin-tooltip>
170
186
  <!-- Background -->
171
187
  <button
172
188
  id="btn-background"
173
189
  type="button"
174
190
  part="toolbar-button toolbar-button-background"
191
+ aria-label="${this.__effectiveI18n.background}"
175
192
  @click="${this.__onBackgroundClick}"
176
193
  ></button>
177
- <vaadin-tooltip for="btn-background" .text="${this.__effectiveI18n.background}"></vaadin-tooltip>
178
194
  </span>
179
195
 
180
196
  <span part="toolbar-group toolbar-group-heading">
@@ -185,24 +201,24 @@ class RichTextEditor extends RichTextEditorMixin(
185
201
  class="ql-header"
186
202
  value="1"
187
203
  part="toolbar-button toolbar-button-h1"
204
+ aria-label="${this.__effectiveI18n.h1}"
188
205
  ></button>
189
- <vaadin-tooltip for="btn-h1" .text="${this.__effectiveI18n.h1}"></vaadin-tooltip>
190
206
  <button
191
207
  id="btn-h2"
192
208
  type="button"
193
209
  class="ql-header"
194
210
  value="2"
195
211
  part="toolbar-button toolbar-button-h2"
212
+ aria-label="${this.__effectiveI18n.h2}"
196
213
  ></button>
197
- <vaadin-tooltip for="btn-h2" .text="${this.__effectiveI18n.h2}"></vaadin-tooltip>
198
214
  <button
199
215
  id="btn-h3"
200
216
  type="button"
201
217
  class="ql-header"
202
218
  value="3"
203
219
  part="toolbar-button toolbar-button-h3"
220
+ aria-label="${this.__effectiveI18n.h3}"
204
221
  ></button>
205
- <vaadin-tooltip for="btn-h3" .text="${this.__effectiveI18n.h3}"></vaadin-tooltip>
206
222
  </span>
207
223
 
208
224
  <span part="toolbar-group toolbar-group-glyph-transformation">
@@ -212,15 +228,15 @@ class RichTextEditor extends RichTextEditorMixin(
212
228
  class="ql-script"
213
229
  value="sub"
214
230
  part="toolbar-button toolbar-button-subscript"
231
+ aria-label="${this.__effectiveI18n.subscript}"
215
232
  ></button>
216
- <vaadin-tooltip for="btn-subscript" .text="${this.__effectiveI18n.subscript}"></vaadin-tooltip>
217
233
  <button
218
234
  id="btn-superscript"
219
235
  class="ql-script"
220
236
  value="super"
221
237
  part="toolbar-button toolbar-button-superscript"
238
+ aria-label="${this.__effectiveI18n.superscript}"
222
239
  ></button>
223
- <vaadin-tooltip for="btn-superscript" text="${this.__effectiveI18n.superscript}"></vaadin-tooltip>
224
240
  </span>
225
241
 
226
242
  <span part="toolbar-group toolbar-group-list">
@@ -231,16 +247,16 @@ class RichTextEditor extends RichTextEditorMixin(
231
247
  class="ql-list"
232
248
  value="ordered"
233
249
  part="toolbar-button toolbar-button-list-ordered"
250
+ aria-label="${this.__effectiveI18n.listOrdered}"
234
251
  ></button>
235
- <vaadin-tooltip for="btn-ol" text="${this.__effectiveI18n.listOrdered}"></vaadin-tooltip>
236
252
  <button
237
253
  id="btn-ul"
238
254
  type="button"
239
255
  class="ql-list"
240
256
  value="bullet"
241
257
  part="toolbar-button toolbar-button-list-bullet"
258
+ aria-label="${this.__effectiveI18n.listBullet}"
242
259
  ></button>
243
- <vaadin-tooltip for="btn-ul" text="${this.__effectiveI18n.listBullet}"></vaadin-tooltip>
244
260
  </span>
245
261
 
246
262
  <span part="toolbar-group toolbar-group-alignment">
@@ -251,24 +267,24 @@ class RichTextEditor extends RichTextEditorMixin(
251
267
  class="ql-align"
252
268
  value=""
253
269
  part="toolbar-button toolbar-button-align-left"
270
+ aria-label="${this.__effectiveI18n.alignLeft}"
254
271
  ></button>
255
- <vaadin-tooltip for="btn-left" .text="${this.__effectiveI18n.alignLeft}"></vaadin-tooltip>
256
272
  <button
257
273
  id="btn-center"
258
274
  type="button"
259
275
  class="ql-align"
260
276
  value="center"
261
277
  part="toolbar-button toolbar-button-align-center"
278
+ aria-label="${this.__effectiveI18n.alignCenter}"
262
279
  ></button>
263
- <vaadin-tooltip for="btn-center" .text="${this.__effectiveI18n.alignCenter}"></vaadin-tooltip>
264
280
  <button
265
281
  id="btn-right"
266
282
  type="button"
267
283
  class="ql-align"
268
284
  value="right"
269
285
  part="toolbar-button toolbar-button-align-right"
286
+ aria-label="${this.__effectiveI18n.alignRight}"
270
287
  ></button>
271
- <vaadin-tooltip for="btn-right" .text="${this.__effectiveI18n.alignRight}"></vaadin-tooltip>
272
288
  </span>
273
289
 
274
290
  <span part="toolbar-group toolbar-group-rich-text">
@@ -277,18 +293,18 @@ class RichTextEditor extends RichTextEditorMixin(
277
293
  id="btn-image"
278
294
  type="button"
279
295
  part="toolbar-button toolbar-button-image"
296
+ aria-label="${this.__effectiveI18n.image}"
280
297
  @touchend="${this._onImageTouchEnd}"
281
298
  @click="${this._onImageClick}"
282
299
  ></button>
283
- <vaadin-tooltip for="btn-image" .text="${this.__effectiveI18n.image}"></vaadin-tooltip>
284
300
  <!-- Link -->
285
301
  <button
286
302
  id="btn-link"
287
303
  type="button"
288
304
  part="toolbar-button toolbar-button-link"
305
+ aria-label="${this.__effectiveI18n.link}"
289
306
  @click="${this._onLinkClick}"
290
307
  ></button>
291
- <vaadin-tooltip for="btn-link" .text="${this.__effectiveI18n.link}"></vaadin-tooltip>
292
308
  </span>
293
309
 
294
310
  <span part="toolbar-group toolbar-group-block">
@@ -298,22 +314,27 @@ class RichTextEditor extends RichTextEditorMixin(
298
314
  type="button"
299
315
  class="ql-blockquote"
300
316
  part="toolbar-button toolbar-button-blockquote"
317
+ aria-label="${this.__effectiveI18n.blockquote}"
301
318
  ></button>
302
- <vaadin-tooltip for="btn-blockquote" .text="${this.__effectiveI18n.blockquote}"></vaadin-tooltip>
303
319
  <!-- Code block -->
304
320
  <button
305
321
  id="btn-code"
306
322
  type="button"
307
323
  class="ql-code-block"
308
324
  part="toolbar-button toolbar-button-code-block"
325
+ aria-label="${this.__effectiveI18n.codeBlock}"
309
326
  ></button>
310
- <vaadin-tooltip for="btn-code" .text="${this.__effectiveI18n.codeBlock}"></vaadin-tooltip>
311
327
  </span>
312
328
 
313
329
  <span part="toolbar-group toolbar-group-format">
314
330
  <!-- Clean -->
315
- <button id="btn-clean" type="button" class="ql-clean" part="toolbar-button toolbar-button-clean"></button>
316
- <vaadin-tooltip for="btn-clean" .text="${this.__effectiveI18n.clean}"></vaadin-tooltip>
331
+ <button
332
+ id="btn-clean"
333
+ type="button"
334
+ class="ql-clean"
335
+ part="toolbar-button toolbar-button-clean"
336
+ aria-label="${this.__effectiveI18n.clean}"
337
+ ></button>
317
338
  </span>
318
339
 
319
340
  <input
@@ -329,54 +350,75 @@ class RichTextEditor extends RichTextEditorMixin(
329
350
  <div class="announcer" aria-live="polite"></div>
330
351
  </div>
331
352
 
332
- <vaadin-confirm-dialog
333
- id="linkDialog"
334
- .opened="${this._linkEditing}"
335
- .header="${this.__effectiveI18n.linkDialogTitle}"
336
- @opened-changed="${this._onLinkEditingChanged}"
337
- >
338
- <vaadin-text-field
339
- id="linkUrl"
340
- .value="${this._linkUrl}"
341
- style="width: 100%;"
342
- @keydown="${this._onLinkKeydown}"
343
- @value-changed="${this._onLinkUrlChanged}"
344
- ></vaadin-text-field>
345
- <vaadin-button id="confirmLink" slot="confirm-button" theme="primary" @click="${this._onLinkEditConfirm}">
346
- ${this.__effectiveI18n.ok}
347
- </vaadin-button>
348
- <vaadin-button
349
- id="removeLink"
350
- slot="reject-button"
351
- theme="error"
352
- @click="${this._onLinkEditRemove}"
353
- ?hidden="${!this._linkRange}"
354
- >
355
- ${this.__effectiveI18n.remove}
356
- </vaadin-button>
357
- <vaadin-button id="cancelLink" slot="cancel-button" @click="${this._onLinkEditCancel}">
358
- ${this.__effectiveI18n.cancel}
359
- </vaadin-button>
360
- </vaadin-confirm-dialog>
361
-
362
- <vaadin-rich-text-editor-popup
363
- id="colorPopup"
364
- .colors="${this.colorOptions}"
365
- .opened="${this._colorEditing}"
366
- @color-selected="${this.__onColorSelected}"
367
- @opened-changed="${this.__onColorEditingChanged}"
368
- ></vaadin-rich-text-editor-popup>
369
-
370
- <vaadin-rich-text-editor-popup
371
- id="backgroundPopup"
372
- .colors="${this.colorOptions}"
373
- .opened="${this._backgroundEditing}"
374
- @color-selected="${this.__onBackgroundSelected}"
375
- @opened-changed="${this.__onBackgroundEditingChanged}"
376
- ></vaadin-rich-text-editor-popup>
353
+ <slot name="tooltip"></slot>
354
+
355
+ <slot name="link-dialog"></slot>
356
+
357
+ <slot name="color-popup"></slot>
358
+
359
+ <slot name="background-popup"></slot>
377
360
  `;
378
361
  }
379
362
 
363
+ /**
364
+ * Override update to render slotted overlays into light DOM after rendering shadow DOM.
365
+ * @param changedProperties
366
+ * @protected
367
+ */
368
+ update(changedProperties) {
369
+ super.update(changedProperties);
370
+
371
+ this.__renderSlottedOverlays();
372
+ }
373
+
374
+ /** @private */
375
+ __renderSlottedOverlays() {
376
+ render(
377
+ html`
378
+ <vaadin-confirm-dialog
379
+ slot="link-dialog"
380
+ cancel-button-visible
381
+ reject-theme="error"
382
+ .opened="${this._linkEditing}"
383
+ .header="${this.__effectiveI18n.linkDialogTitle}"
384
+ .confirmText="${this.__effectiveI18n.ok}"
385
+ .rejectText="${this.__effectiveI18n.remove}"
386
+ .cancelText="${this.__effectiveI18n.cancel}"
387
+ .rejectButtonVisible="${!!this._linkRange}"
388
+ @confirm="${this._onLinkEditConfirm}"
389
+ @cancel="${this._onLinkEditCancel}"
390
+ @reject="${this._onLinkEditRemove}"
391
+ @opened-changed="${this._onLinkEditingChanged}"
392
+ >
393
+ <vaadin-text-field
394
+ .value="${this._linkUrl}"
395
+ style="width: 100%;"
396
+ @keydown="${this._onLinkKeydown}"
397
+ @value-changed="${this._onLinkUrlChanged}"
398
+ ></vaadin-text-field>
399
+ </vaadin-confirm-dialog>
400
+
401
+ <vaadin-rich-text-editor-popup
402
+ slot="color-popup"
403
+ .colors="${this.colorOptions}"
404
+ .opened="${this._colorEditing}"
405
+ @color-selected="${this.__onColorSelected}"
406
+ @opened-changed="${this.__onColorEditingChanged}"
407
+ ></vaadin-rich-text-editor-popup>
408
+
409
+ <vaadin-rich-text-editor-popup
410
+ slot="background-popup"
411
+ .colors="${this.colorOptions}"
412
+ .opened="${this._backgroundEditing}"
413
+ @color-selected="${this.__onBackgroundSelected}"
414
+ @opened-changed="${this.__onBackgroundEditingChanged}"
415
+ ></vaadin-rich-text-editor-popup>
416
+ `,
417
+ this,
418
+ { host: this },
419
+ );
420
+ }
421
+
380
422
  /** @private */
381
423
  __onBackgroundEditingChanged(event) {
382
424
  this._backgroundEditing = event.detail.value;
@@ -389,6 +431,18 @@ class RichTextEditor extends RichTextEditorMixin(
389
431
 
390
432
  /** @private */
391
433
  _onLinkEditingChanged(event) {
434
+ // Autofocus the URL field when the dialog opens
435
+ if (event.detail.value) {
436
+ const confirmDialog = event.target;
437
+ const urlField = confirmDialog.querySelector('vaadin-text-field');
438
+ confirmDialog.$.overlay.addEventListener(
439
+ 'vaadin-overlay-open',
440
+ () => {
441
+ urlField.focus();
442
+ },
443
+ { once: true },
444
+ );
445
+ }
392
446
  this._linkEditing = event.detail.value;
393
447
  }
394
448
 
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-alpha7",
4
+ "version": "25.0.0-alpha9",
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```\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-group-list` | The group for group 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 | 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-group-list` | The group for group 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.",
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-alpha7",
4
+ "version": "25.0.0-alpha9",
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```\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-group-list` | The group for group 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 | 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-group-list` | The group for group 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.",
20
20
  "extension": true,
21
21
  "attributes": [
22
22
  {