@vaadin/rich-text-editor 24.4.0-rc2 → 24.5.0-alpha2

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": "24.4.0-rc2",
3
+ "version": "24.5.0-alpha2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -41,19 +41,19 @@
41
41
  "dependencies": {
42
42
  "@open-wc/dedupe-mixin": "^1.3.0",
43
43
  "@polymer/polymer": "^3.0.0",
44
- "@vaadin/button": "24.4.0-rc2",
45
- "@vaadin/component-base": "24.4.0-rc2",
46
- "@vaadin/confirm-dialog": "24.4.0-rc2",
47
- "@vaadin/text-field": "24.4.0-rc2",
48
- "@vaadin/tooltip": "24.4.0-rc2",
49
- "@vaadin/vaadin-lumo-styles": "24.4.0-rc2",
50
- "@vaadin/vaadin-material-styles": "24.4.0-rc2",
51
- "@vaadin/vaadin-themable-mixin": "24.4.0-rc2",
44
+ "@vaadin/button": "24.5.0-alpha2",
45
+ "@vaadin/component-base": "24.5.0-alpha2",
46
+ "@vaadin/confirm-dialog": "24.5.0-alpha2",
47
+ "@vaadin/text-field": "24.5.0-alpha2",
48
+ "@vaadin/tooltip": "24.5.0-alpha2",
49
+ "@vaadin/vaadin-lumo-styles": "24.5.0-alpha2",
50
+ "@vaadin/vaadin-material-styles": "24.5.0-alpha2",
51
+ "@vaadin/vaadin-themable-mixin": "24.5.0-alpha2",
52
52
  "lit": "^3.0.0"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@esm-bundle/chai": "^4.3.4",
56
- "@vaadin/a11y-base": "24.4.0-rc2",
56
+ "@vaadin/a11y-base": "24.5.0-alpha2",
57
57
  "@vaadin/testing-helpers": "^0.6.0",
58
58
  "gulp": "^4.0.2",
59
59
  "gulp-cli": "^2.3.0",
@@ -65,5 +65,5 @@
65
65
  "web-types.json",
66
66
  "web-types.lit.json"
67
67
  ],
68
- "gitHead": "ea3d99e8cf67a337e959d5cab849f80464c7c7e5"
68
+ "gitHead": "403f153bf4351f220cb6172485a0575ddf0d0fba"
69
69
  }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2000 - 2024 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, html, LitElement } from 'lit';
12
+ import { defineCustomElement } from '@vaadin/component-base/src/define.js';
13
+ import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
14
+ import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
15
+ import { OverlayMixin } from '@vaadin/overlay/src/vaadin-overlay-mixin.js';
16
+ import { PositionMixin } from '@vaadin/overlay/src/vaadin-overlay-position-mixin.js';
17
+ import { overlayStyles } from '@vaadin/overlay/src/vaadin-overlay-styles.js';
18
+ import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
19
+ import { RichTextEditorPopupMixin } from './vaadin-rich-text-editor-popup-mixin.js';
20
+
21
+ /**
22
+ * An element used internally by `<vaadin-rich-text-editor>`. Not intended to be used separately.
23
+ * @private
24
+ */
25
+ class RichTextEditorPopup extends RichTextEditorPopupMixin(PolylitMixin(LitElement)) {
26
+ static get is() {
27
+ return 'vaadin-rich-text-editor-popup';
28
+ }
29
+
30
+ static get styles() {
31
+ return css`
32
+ :host {
33
+ display: none;
34
+ }
35
+ `;
36
+ }
37
+
38
+ /** @protected */
39
+ render() {
40
+ return html`
41
+ <vaadin-rich-text-editor-popup-overlay
42
+ .renderer="${this.renderer}"
43
+ .opened="${this.opened}"
44
+ .positionTarget="${this.target}"
45
+ no-vertical-overlap
46
+ horizontal-align="start"
47
+ vertical-align="top"
48
+ focus-trap
49
+ @opened-changed="${this._onOpenedChanged}"
50
+ @vaadin-overlay-escape-press="${this._onOverlayEscapePress}"
51
+ ></vaadin-rich-text-editor-popup-overlay>
52
+ `;
53
+ }
54
+
55
+ /** @private */
56
+ _onOpenedChanged(event) {
57
+ this.opened = event.detail.value;
58
+ }
59
+ }
60
+
61
+ defineCustomElement(RichTextEditorPopup);
62
+
63
+ export { RichTextEditorPopup };
64
+
65
+ /**
66
+ * An element used internally by `<vaadin-rich-text-editor>`. Not intended to be used separately.
67
+ * @private
68
+ */
69
+ class RichTextEditorPopupOverlay extends PositionMixin(
70
+ OverlayMixin(DirMixin(ThemableMixin(PolylitMixin(LitElement)))),
71
+ ) {
72
+ static get is() {
73
+ return 'vaadin-rich-text-editor-popup-overlay';
74
+ }
75
+
76
+ static get styles() {
77
+ return overlayStyles;
78
+ }
79
+
80
+ /** @protected */
81
+ render() {
82
+ return html`
83
+ <div id="backdrop" part="backdrop" hidden></div>
84
+ <div part="overlay" id="overlay">
85
+ <div part="content" id="content"><slot></slot></div>
86
+ </div>
87
+ `;
88
+ }
89
+ }
90
+
91
+ defineCustomElement(RichTextEditorPopupOverlay);
@@ -12,6 +12,7 @@ import '@vaadin/button/src/vaadin-lit-button.js';
12
12
  import '@vaadin/confirm-dialog/src/vaadin-lit-confirm-dialog.js';
13
13
  import '@vaadin/text-field/src/vaadin-lit-text-field.js';
14
14
  import '@vaadin/tooltip/src/vaadin-lit-tooltip.js';
15
+ import './vaadin-lit-rich-text-editor-popup.js';
15
16
  import { html, LitElement } from 'lit';
16
17
  import { defineCustomElement } from '@vaadin/component-base/src/define.js';
17
18
  import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
@@ -84,6 +85,25 @@ class RichTextEditor extends RichTextEditorMixin(ElementMixin(ThemableMixin(Poly
84
85
  <vaadin-tooltip for="btn-strike" .text="${this.i18n.strike}"></vaadin-tooltip>
85
86
  </span>
86
87
 
88
+ <span part="toolbar-group toolbar-group-style">
89
+ <!-- Color -->
90
+ <button
91
+ id="btn-color"
92
+ type="button"
93
+ part="toolbar-button toolbar-button-color"
94
+ @click="${this.__onColorClick}"
95
+ ></button>
96
+ <vaadin-tooltip for="btn-color" .text="${this.i18n.color}"></vaadin-tooltip>
97
+ <!-- Background -->
98
+ <button
99
+ id="btn-background"
100
+ type="button"
101
+ part="toolbar-button toolbar-button-background"
102
+ @click="${this.__onBackgroundClick}"
103
+ ></button>
104
+ <vaadin-tooltip for="btn-background" .text="${this.i18n.background}"></vaadin-tooltip>
105
+ </span>
106
+
87
107
  <span part="toolbar-group toolbar-group-heading">
88
108
  <!-- Header buttons -->
89
109
  <button
@@ -265,9 +285,35 @@ class RichTextEditor extends RichTextEditorMixin(ElementMixin(ThemableMixin(Poly
265
285
  ${this.i18n.cancel}
266
286
  </vaadin-button>
267
287
  </vaadin-confirm-dialog>
288
+
289
+ <vaadin-rich-text-editor-popup
290
+ id="colorPopup"
291
+ .colors="${this.colorOptions}"
292
+ .opened="${this._colorEditing}"
293
+ @color-selected="${this.__onColorSelected}"
294
+ @opened-changed="${this.__onColorEditingChanged}"
295
+ ></vaadin-rich-text-editor-popup>
296
+
297
+ <vaadin-rich-text-editor-popup
298
+ id="backgroundPopup"
299
+ .colors="${this.colorOptions}"
300
+ .opened="${this._backgroundEditing}"
301
+ @color-selected="${this.__onBackgroundSelected}"
302
+ @opened-changed="${this.__onBackgroundEditingChanged}"
303
+ ></vaadin-rich-text-editor-popup>
268
304
  `;
269
305
  }
270
306
 
307
+ /** @private */
308
+ __onBackgroundEditingChanged(event) {
309
+ this._backgroundEditing = event.detail.value;
310
+ }
311
+
312
+ /** @private */
313
+ __onColorEditingChanged(event) {
314
+ this._colorEditing = event.detail.value;
315
+ }
316
+
271
317
  /** @private */
272
318
  _onLinkEditingChanged(event) {
273
319
  this._linkEditing = event.detail.value;
@@ -17,6 +17,8 @@ export interface RichTextEditorI18n {
17
17
  italic: string;
18
18
  underline: string;
19
19
  strike: string;
20
+ color: string;
21
+ background: string;
20
22
  h1: string;
21
23
  h2: string;
22
24
  h3: string;
@@ -96,6 +98,16 @@ export declare class RichTextEditorMixinClass {
96
98
  */
97
99
  i18n: RichTextEditorI18n;
98
100
 
101
+ /**
102
+ * The list of colors used by the background and text color
103
+ * selection controls. Should contain an array of HEX strings.
104
+ *
105
+ * When user selects `#000000` (black) as a text color,
106
+ * or `#ffffff` (white) as a background color, it resets
107
+ * the corresponding format for the selected text.
108
+ */
109
+ colorOptions: string[];
110
+
99
111
  /**
100
112
  * Sets content represented by HTML snippet into the editor.
101
113
  * The snippet is interpreted by [Quill's Clipboard matchers](https://quilljs.com/docs/modules/clipboard/#matchers),
@@ -146,6 +146,8 @@ export const RichTextEditorMixin = (superClass) =>
146
146
  italic: 'italic',
147
147
  underline: 'underline',
148
148
  strike: 'strike',
149
+ color: 'color',
150
+ background: 'background',
149
151
  h1: 'h1',
150
152
  h2: 'h2',
151
153
  h3: 'h3',
@@ -169,6 +171,28 @@ export const RichTextEditorMixin = (superClass) =>
169
171
  },
170
172
  },
171
173
 
174
+ /**
175
+ * The list of colors used by the background and text color
176
+ * selection controls. Should contain an array of HEX strings.
177
+ *
178
+ * When user selects `#000000` (black) as a text color,
179
+ * or `#ffffff` (white) as a background color, it resets
180
+ * the corresponding format for the selected text.
181
+ */
182
+ colorOptions: {
183
+ type: Array,
184
+ value: () => {
185
+ /* prettier-ignore */
186
+ return [
187
+ '#000000', '#e60000', '#ff9900', '#ffff00', '#008a00', '#0066cc', '#9933ff',
188
+ '#ffffff', '#facccc', '#ffebcc', '#ffffcc', '#cce8cc', '#cce0f5', '#ebd6ff',
189
+ '#bbbbbb', '#f06666', '#ffc266', '#ffff66', '#66b966', '#66a3e0', '#c285ff',
190
+ '#888888', '#a10000', '#b26b00', '#b2b200', '#006100', '#0047b2', '#6b24b2',
191
+ '#444444', '#5c0000', '#663d00', '#666600', '#003700', '#002966', '#3d1466'
192
+ ];
193
+ },
194
+ },
195
+
172
196
  /** @private */
173
197
  _editor: {
174
198
  type: Object,
@@ -210,6 +234,30 @@ export const RichTextEditorMixin = (superClass) =>
210
234
  type: String,
211
235
  value: '',
212
236
  },
237
+
238
+ /** @private */
239
+ _colorEditing: {
240
+ type: Boolean,
241
+ value: false,
242
+ },
243
+
244
+ /** @private */
245
+ _colorValue: {
246
+ type: String,
247
+ value: '',
248
+ },
249
+
250
+ /** @private */
251
+ _backgroundEditing: {
252
+ type: Boolean,
253
+ value: false,
254
+ },
255
+
256
+ /** @private */
257
+ _backgroundValue: {
258
+ type: String,
259
+ value: '',
260
+ },
213
261
  };
214
262
  }
215
263
 
@@ -315,6 +363,15 @@ export const RichTextEditorMixin = (superClass) =>
315
363
  });
316
364
  });
317
365
 
366
+ this._editor.on('editor-change', () => {
367
+ const selection = this._editor.getSelection();
368
+ if (selection) {
369
+ const format = this._editor.getFormat(selection.index, selection.length);
370
+ this._toolbar.style.setProperty('--_color-value', format.color || null);
371
+ this._toolbar.style.setProperty('--_background-value', format.background || null);
372
+ }
373
+ });
374
+
318
375
  const TAB_KEY = 9;
319
376
 
320
377
  editorContent.addEventListener('keydown', (e) => {
@@ -367,6 +424,9 @@ export const RichTextEditorMixin = (superClass) =>
367
424
 
368
425
  this._addToolbarListeners();
369
426
 
427
+ this.$.backgroundPopup.target = this.shadowRoot.querySelector('#btn-background');
428
+ this.$.colorPopup.target = this.shadowRoot.querySelector('#btn-color');
429
+
370
430
  requestAnimationFrame(() => {
371
431
  this.$.linkDialog.$.dialog.$.overlay.addEventListener('vaadin-overlay-open', () => {
372
432
  this.$.linkUrl.focus();
@@ -668,6 +728,36 @@ export const RichTextEditorMixin = (superClass) =>
668
728
  }
669
729
  }
670
730
 
731
+ /** @private */
732
+ __onColorClick() {
733
+ this._colorEditing = true;
734
+ }
735
+
736
+ /** @private */
737
+ __onColorSelected(event) {
738
+ const color = event.detail.color;
739
+ this._colorValue = color === '#000000' ? null : color;
740
+ this._markToolbarClicked();
741
+ this._editor.format('color', this._colorValue, SOURCE.USER);
742
+ this._toolbar.style.setProperty('--_color-value', this._colorValue);
743
+ this._colorEditing = false;
744
+ }
745
+
746
+ /** @private */
747
+ __onBackgroundClick() {
748
+ this._backgroundEditing = true;
749
+ }
750
+
751
+ /** @private */
752
+ __onBackgroundSelected(event) {
753
+ const color = event.detail.color;
754
+ this._backgroundValue = color === '#ffffff' ? null : color;
755
+ this._markToolbarClicked();
756
+ this._editor.format('background', this._backgroundValue, SOURCE.USER);
757
+ this._toolbar.style.setProperty('--_background-value', this._backgroundValue);
758
+ this._backgroundEditing = false;
759
+ }
760
+
671
761
  /** @private */
672
762
  __updateHtmlValue() {
673
763
  const editor = this.shadowRoot.querySelector('.ql-editor');
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2000 - 2024 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
+
12
+ /**
13
+ * @polymerMixin
14
+ */
15
+ export const RichTextEditorPopupMixin = (superClass) =>
16
+ class RichTextEditorPopupMixinClass extends superClass {
17
+ static get properties() {
18
+ return {
19
+ target: {
20
+ type: Object,
21
+ },
22
+
23
+ opened: {
24
+ type: Boolean,
25
+ notify: true,
26
+ },
27
+
28
+ colors: {
29
+ type: Array,
30
+ },
31
+
32
+ renderer: {
33
+ type: Object,
34
+ },
35
+ };
36
+ }
37
+
38
+ static get observers() {
39
+ return ['__openedOrTargetChanged(opened, target)', '__colorsChanged(colors)'];
40
+ }
41
+
42
+ /** @private */
43
+ __colorsChanged(colors) {
44
+ this.renderer = (root) => {
45
+ if (!root.firstChild) {
46
+ colors.forEach((color) => {
47
+ const btn = document.createElement('button');
48
+ btn.style.background = color;
49
+ btn.dataset.color = color;
50
+ btn.addEventListener('click', () => {
51
+ this.dispatchEvent(new CustomEvent('color-selected', { detail: { color } }));
52
+ });
53
+ root.appendChild(btn);
54
+ });
55
+ }
56
+ };
57
+ }
58
+
59
+ /** @private */
60
+ __openedOrTargetChanged(opened, target) {
61
+ if (target) {
62
+ target.setAttribute('aria-expanded', opened ? 'true' : 'false');
63
+ }
64
+ }
65
+
66
+ /** @protected */
67
+ _onOverlayEscapePress() {
68
+ this.target.focus();
69
+ }
70
+ };
@@ -0,0 +1,88 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2000 - 2024 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 { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
12
+ import { defineCustomElement } from '@vaadin/component-base/src/define.js';
13
+ import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
14
+ import { OverlayMixin } from '@vaadin/overlay/src/vaadin-overlay-mixin.js';
15
+ import { PositionMixin } from '@vaadin/overlay/src/vaadin-overlay-position-mixin.js';
16
+ import { overlayStyles } from '@vaadin/overlay/src/vaadin-overlay-styles.js';
17
+ import { registerStyles, ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
18
+ import { RichTextEditorPopupMixin } from './vaadin-rich-text-editor-popup-mixin.js';
19
+
20
+ registerStyles('vaadin-rich-text-editor-popup-overlay', [overlayStyles], {
21
+ moduleId: 'vaadin-rich-text-editor-popup-overlay-styles',
22
+ });
23
+
24
+ /**
25
+ * An element used internally by `<vaadin-rich-text-editor>`. Not intended to be used separately.
26
+ *
27
+ * @customElement
28
+ * @extends HTMLElement
29
+ * @mixes RichTextEditorPopupMixin
30
+ * @private
31
+ */
32
+ class RichTextEditorPopup extends RichTextEditorPopupMixin(PolymerElement) {
33
+ static get is() {
34
+ return 'vaadin-rich-text-editor-popup';
35
+ }
36
+
37
+ static get template() {
38
+ return html`
39
+ <style>
40
+ :host {
41
+ display: none;
42
+ }
43
+ </style>
44
+ <vaadin-rich-text-editor-popup-overlay
45
+ renderer="[[renderer]]"
46
+ opened="{{opened}}"
47
+ position-target="[[target]]"
48
+ no-vertical-overlap
49
+ horizontal-align="start"
50
+ vertical-align="top"
51
+ on-vaadin-overlay-escape-press="_onOverlayEscapePress"
52
+ focus-trap
53
+ ></vaadin-rich-text-editor-popup-overlay>
54
+ `;
55
+ }
56
+ }
57
+
58
+ defineCustomElement(RichTextEditorPopup);
59
+
60
+ export { RichTextEditorPopup };
61
+
62
+ /**
63
+ * An element used internally by `<vaadin-rich-text-editor>`. Not intended to be used separately.
64
+ *
65
+ * @customElement
66
+ * @extends HTMLElement
67
+ * @mixes DirMixin
68
+ * @mixes ThemableMixin
69
+ * @mixes OverlayMixin
70
+ * @mixes PositionMixin
71
+ * @private
72
+ */
73
+ class RichTextEditorPopupOverlay extends PositionMixin(OverlayMixin(DirMixin(ThemableMixin(PolymerElement)))) {
74
+ static get is() {
75
+ return 'vaadin-rich-text-editor-popup-overlay';
76
+ }
77
+
78
+ static get template() {
79
+ return html`
80
+ <div id="backdrop" part="backdrop" hidden></div>
81
+ <div part="overlay" id="overlay">
82
+ <div part="content" id="content"><slot></slot></div>
83
+ </div>
84
+ `;
85
+ }
86
+ }
87
+
88
+ defineCustomElement(RichTextEditorPopupOverlay);
@@ -141,6 +141,40 @@ export const buttonsStyles = css`
141
141
  content: '</>';
142
142
  font-size: 0.875em;
143
143
  }
144
+
145
+ [part~='toolbar-button-background']::before,
146
+ [part~='toolbar-button-color']::before {
147
+ content: 'A';
148
+ font-size: 1em;
149
+ }
150
+
151
+ [part~='toolbar-button-color']::after {
152
+ content: '';
153
+ position: absolute;
154
+ bottom: 4px;
155
+ left: 25%;
156
+ right: 25%;
157
+ width: 50%;
158
+ height: 4px;
159
+ background-color: var(--_color-value, currentColor);
160
+ }
161
+
162
+ [part~='toolbar-button-background']::before {
163
+ z-index: 1;
164
+ }
165
+
166
+ [part~='toolbar-button-background']::after {
167
+ content: '';
168
+ position: absolute;
169
+ inset: 20%;
170
+ background: repeating-linear-gradient(
171
+ 135deg,
172
+ var(--_background-value, currentColor),
173
+ var(--_background-value, currentColor) 1px,
174
+ transparent 1px,
175
+ transparent 2px
176
+ );
177
+ }
144
178
  `;
145
179
 
146
180
  export const toolbarStyles = [iconsStyles, buttonsStyles];
@@ -57,6 +57,7 @@ export interface RichTextEditorEventMap extends HTMLElementEventMap, RichTextEdi
57
57
  * `toolbar-group-history` | The group for histroy controls
58
58
  * `toolbar-group-emphasis` | The group for emphasis controls
59
59
  * `toolbar-group-heading` | The group for heading controls
60
+ * `toolbar-group-style` | The group for style controls
60
61
  * `toolbar-group-glyph-transformation` | The group for glyph transformation controls
61
62
  * `toolbar-group-group-list` | The group for group list controls
62
63
  * `toolbar-group-alignment` | The group for alignment controls
@@ -71,6 +72,8 @@ export interface RichTextEditorEventMap extends HTMLElementEventMap, RichTextEdi
71
72
  * `toolbar-button-italic` | The "italic" button
72
73
  * `toolbar-button-underline` | The "underline" button
73
74
  * `toolbar-button-strike` | The "strike-through" button
75
+ * `toolbar-button-color` | The "color" button
76
+ * `toolbar-button-background` | The "background" button
74
77
  * `toolbar-button-h1` | The "header 1" button
75
78
  * `toolbar-button-h2` | The "header 2" button
76
79
  * `toolbar-button-h3` | The "header 3" button
@@ -12,6 +12,7 @@ import '@vaadin/button/src/vaadin-button.js';
12
12
  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
+ import './vaadin-rich-text-editor-popup.js';
15
16
  import './vaadin-rich-text-editor-toolbar-styles.js';
16
17
  import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
17
18
  import { defineCustomElement } from '@vaadin/component-base/src/define.js';
@@ -55,6 +56,7 @@ registerStyles('vaadin-rich-text-editor', richTextEditorStyles, { moduleId: 'vaa
55
56
  * `toolbar-group-history` | The group for histroy controls
56
57
  * `toolbar-group-emphasis` | The group for emphasis controls
57
58
  * `toolbar-group-heading` | The group for heading controls
59
+ * `toolbar-group-style` | The group for style controls
58
60
  * `toolbar-group-glyph-transformation` | The group for glyph transformation controls
59
61
  * `toolbar-group-group-list` | The group for group list controls
60
62
  * `toolbar-group-alignment` | The group for alignment controls
@@ -69,6 +71,8 @@ registerStyles('vaadin-rich-text-editor', richTextEditorStyles, { moduleId: 'vaa
69
71
  * `toolbar-button-italic` | The "italic" button
70
72
  * `toolbar-button-underline` | The "underline" button
71
73
  * `toolbar-button-strike` | The "strike-through" button
74
+ * `toolbar-button-color` | The "color" button
75
+ * `toolbar-button-background` | The "background" button
72
76
  * `toolbar-button-h1` | The "header 1" button
73
77
  * `toolbar-button-h2` | The "header 2" button
74
78
  * `toolbar-button-h3` | The "header 3" button
@@ -130,6 +134,25 @@ class RichTextEditor extends RichTextEditorMixin(ElementMixin(ThemableMixin(Poly
130
134
  <vaadin-tooltip for="btn-strike" text="[[i18n.strike]]"></vaadin-tooltip>
131
135
  </span>
132
136
 
137
+ <span part="toolbar-group toolbar-group-style">
138
+ <!-- Color -->
139
+ <button
140
+ id="btn-color"
141
+ type="button"
142
+ part="toolbar-button toolbar-button-color"
143
+ on-click="__onColorClick"
144
+ ></button>
145
+ <vaadin-tooltip for="btn-color" text="[[i18n.color]]"></vaadin-tooltip>
146
+ <!-- Background -->
147
+ <button
148
+ id="btn-background"
149
+ type="button"
150
+ part="toolbar-button toolbar-button-background"
151
+ on-click="__onBackgroundClick"
152
+ ></button>
153
+ <vaadin-tooltip for="btn-background" text="[[i18n.background]]"></vaadin-tooltip>
154
+ </span>
155
+
133
156
  <span part="toolbar-group toolbar-group-heading">
134
157
  <!-- Header buttons -->
135
158
  <button
@@ -305,6 +328,20 @@ class RichTextEditor extends RichTextEditorMixin(ElementMixin(ThemableMixin(Poly
305
328
  [[i18n.cancel]]
306
329
  </vaadin-button>
307
330
  </vaadin-confirm-dialog>
331
+
332
+ <vaadin-rich-text-editor-popup
333
+ id="colorPopup"
334
+ colors="[[colorOptions]]"
335
+ opened="{{_colorEditing}}"
336
+ on-color-selected="__onColorSelected"
337
+ ></vaadin-rich-text-editor-popup>
338
+
339
+ <vaadin-rich-text-editor-popup
340
+ id="backgroundPopup"
341
+ colors="[[colorOptions]]"
342
+ opened="{{_backgroundEditing}}"
343
+ on-color-selected="__onBackgroundSelected"
344
+ ></vaadin-rich-text-editor-popup>
308
345
  `;
309
346
  }
310
347
 
@@ -3,9 +3,37 @@ import '@vaadin/vaadin-lumo-styles/sizing.js';
3
3
  import '@vaadin/vaadin-lumo-styles/spacing.js';
4
4
  import '@vaadin/vaadin-lumo-styles/style.js';
5
5
  import { color } from '@vaadin/vaadin-lumo-styles/color.js';
6
+ import { overlay } from '@vaadin/vaadin-lumo-styles/mixins/overlay.js';
6
7
  import { typography } from '@vaadin/vaadin-lumo-styles/typography.js';
7
8
  import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
8
9
 
10
+ const popupOverlay = css`
11
+ [part='overlay'] {
12
+ margin: var(--lumo-space-xs) 0;
13
+ }
14
+
15
+ [part='content'] {
16
+ padding: var(--lumo-space-xs);
17
+ max-width: calc(7 * (var(--_button-size) + var(--_button-margin) * 2));
18
+ display: flex;
19
+ flex-wrap: wrap;
20
+ justify-content: center;
21
+ --_button-size: 1.25rem;
22
+ --_button-margin: 3px;
23
+ }
24
+
25
+ [part='content'] ::slotted(button) {
26
+ border: none;
27
+ width: var(--_button-size);
28
+ height: var(--_button-size);
29
+ margin: var(--_button-margin);
30
+ }
31
+ `;
32
+
33
+ registerStyles('vaadin-rich-text-editor-popup-overlay', [overlay, popupOverlay], {
34
+ moduleId: 'lumo-rich-text-editor-popup-overlay',
35
+ });
36
+
9
37
  const richTextEditor = css`
10
38
  :host {
11
39
  min-height: calc(var(--lumo-size-m) * 8);
@@ -32,7 +60,8 @@ const richTextEditor = css`
32
60
  transition: background-color 100ms, color 100ms;
33
61
  }
34
62
 
35
- [part~='toolbar-button']:focus {
63
+ [part~='toolbar-button']:focus,
64
+ [part~='toolbar-button'][aria-expanded='true'] {
36
65
  outline: none;
37
66
  box-shadow: 0 0 0 var(--_focus-ring-width) var(--_focus-ring-color);
38
67
  }
@@ -40,7 +69,6 @@ const richTextEditor = css`
40
69
  [part~='toolbar-button']:hover {
41
70
  background-color: var(--lumo-contrast-5pct);
42
71
  color: var(--lumo-contrast-80pct);
43
- box-shadow: none;
44
72
  }
45
73
 
46
74
  @media (hover: none) {
@@ -73,6 +101,8 @@ const richTextEditor = css`
73
101
  }
74
102
 
75
103
  [part~='toolbar-button-bold']::before,
104
+ [part~='toolbar-button-background']::before,
105
+ [part~='toolbar-button-color']::before,
76
106
  [part~='toolbar-button-italic']::before,
77
107
  [part~='toolbar-button-underline']::before,
78
108
  [part~='toolbar-button-strike']::before {
@@ -84,6 +114,21 @@ const richTextEditor = css`
84
114
  font-weight: 700;
85
115
  }
86
116
 
117
+ [part~='toolbar-button-background']::before {
118
+ background-color: var(--lumo-base-color);
119
+ background-image: linear-gradient(var(--lumo-contrast-5pct), var(--lumo-contrast-5pct));
120
+ }
121
+
122
+ [part~='toolbar-button-background']:hover::before {
123
+ background-image: linear-gradient(var(--lumo-contrast-5pct), var(--lumo-contrast-5pct)),
124
+ linear-gradient(var(--lumo-contrast-5pct), var(--lumo-contrast-5pct));
125
+ }
126
+
127
+ [part~='toolbar-button-background']:active::before {
128
+ background-image: linear-gradient(var(--lumo-contrast-5pct), var(--lumo-contrast-5pct)),
129
+ linear-gradient(var(--lumo-contrast-10pct), var(--lumo-contrast-10pct));
130
+ }
131
+
87
132
  [part~='toolbar-button-h1']::before,
88
133
  [part~='toolbar-button-h2']::before,
89
134
  [part~='toolbar-button-h3']::before {
@@ -1,7 +1,35 @@
1
1
  import '@vaadin/vaadin-material-styles/color.js';
2
+ import { overlay } from '@vaadin/vaadin-material-styles/mixins/overlay.js';
2
3
  import { typography } from '@vaadin/vaadin-material-styles/typography.js';
3
4
  import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
4
5
 
6
+ const popupOverlay = css`
7
+ [part='overlay'] {
8
+ margin: 0.25rem 0;
9
+ }
10
+
11
+ [part='content'] {
12
+ padding: 0.25rem;
13
+ max-width: calc(7 * (var(--_button-size) + var(--_button-margin) * 2));
14
+ display: flex;
15
+ flex-wrap: wrap;
16
+ justify-content: center;
17
+ --_button-size: 1.25rem;
18
+ --_button-margin: 3px;
19
+ }
20
+
21
+ [part='content'] ::slotted(button) {
22
+ border: none;
23
+ width: var(--_button-size);
24
+ height: var(--_button-size);
25
+ margin: var(--_button-margin);
26
+ }
27
+ `;
28
+
29
+ registerStyles('vaadin-rich-text-editor-popup-overlay', [overlay, popupOverlay], {
30
+ moduleId: 'material-rich-text-editor-popup-overlay',
31
+ });
32
+
5
33
  const richTextEditor = css`
6
34
  :host {
7
35
  background-color: var(--material-background-color);
@@ -37,6 +65,10 @@ const richTextEditor = css`
37
65
  color: inherit;
38
66
  }
39
67
 
68
+ [part~='toolbar-button'][aria-expanded='true'] {
69
+ outline: -webkit-focus-ring-color auto 1px;
70
+ }
71
+
40
72
  [part~='toolbar-button'][on] {
41
73
  background-color: rgba(0, 0, 0, 0.1);
42
74
  color: inherit;
@@ -71,6 +103,14 @@ const richTextEditor = css`
71
103
  font-size: 20px;
72
104
  }
73
105
 
106
+ [part~='toolbar-button-background']::before {
107
+ background-color: var(--material-secondary-background-color);
108
+ }
109
+
110
+ [part~='toolbar-button-background']::after {
111
+ inset: 0.25rem;
112
+ }
113
+
74
114
  /* TODO unsupported selector */
75
115
  [part='content'] > .ql-editor {
76
116
  padding: 0 16px;
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": "24.4.0-rc2",
4
+ "version": "24.5.0-alpha2",
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-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-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```\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",
@@ -86,6 +86,17 @@
86
86
  "RichTextEditorI18n"
87
87
  ]
88
88
  }
89
+ },
90
+ {
91
+ "name": "colorOptions",
92
+ "description": "The list of colors used by the background and text color\nselection controls. Should contain an array of HEX strings.\n\nWhen user selects `#000000` (black) as a text color,\nor `#ffffff` (white) as a background color, it resets\nthe corresponding format for the selected text.",
93
+ "value": {
94
+ "type": [
95
+ "Array",
96
+ "null",
97
+ "undefined"
98
+ ]
99
+ }
89
100
  }
90
101
  ],
91
102
  "events": [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/rich-text-editor",
4
- "version": "24.4.0-rc2",
4
+ "version": "24.5.0-alpha2",
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-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-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```\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
  {
@@ -47,6 +47,13 @@
47
47
  "kind": "expression"
48
48
  }
49
49
  },
50
+ {
51
+ "name": ".colorOptions",
52
+ "description": "The list of colors used by the background and text color\nselection controls. Should contain an array of HEX strings.\n\nWhen user selects `#000000` (black) as a text color,\nor `#ffffff` (white) as a background color, it resets\nthe corresponding format for the selected text.",
53
+ "value": {
54
+ "kind": "expression"
55
+ }
56
+ },
50
57
  {
51
58
  "name": "@change",
52
59
  "description": "Fired when the user commits a value change.",