Skip to content

Commit c837499

Browse files
authored
refactor!: use custom button component in message-input (#10173)
1 parent 972c58e commit c837499

13 files changed

+216
-47
lines changed

packages/message-input/src/styles/vaadin-message-input-base-styles.js

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -42,41 +42,4 @@ export const messageInputStyles = css`
4242
--vaadin-focus-ring-width: 0;
4343
--vaadin-input-field-background: transparent !important;
4444
}
45-
46-
::slotted([slot='button']) {
47-
flex: none;
48-
align-self: end;
49-
margin: var(--vaadin-input-field-padding, var(--vaadin-padding-container));
50-
--vaadin-button-border-width: 0;
51-
--vaadin-button-background: transparent;
52-
--vaadin-button-text-color: var(--vaadin-color);
53-
--vaadin-button-padding: 0;
54-
}
55-
56-
:host([theme~='icon-button']) ::slotted([slot='button']) {
57-
width: var(--vaadin-icon-size, 1lh);
58-
height: var(--vaadin-icon-size, 1lh);
59-
color: transparent;
60-
position: relative;
61-
contain: strict;
62-
}
63-
64-
:host([theme~='icon-button']) ::slotted([slot='button'])::before {
65-
content: '';
66-
position: absolute;
67-
inset: 0;
68-
mask-image: var(--_vaadin-icon-paper-airplane);
69-
background: var(--vaadin-button-text-color);
70-
}
71-
72-
:host([dir='rtl'][theme~='icon-button']) ::slotted([slot='button'])::before {
73-
scale: -1;
74-
}
75-
76-
@media (forced-colors: active) {
77-
:host([theme~='icon-button']) ::slotted([slot='button']) {
78-
forced-color-adjust: none;
79-
--vaadin-button-text-color: CanvasText;
80-
}
81-
}
8245
`;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2021 - 2025 Vaadin Ltd.
4+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5+
*/
6+
import type { CSSResult } from 'lit';
7+
8+
export const messageInputButtonStyles: CSSResult;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2021 - 2025 Vaadin Ltd.
4+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5+
*/
6+
import '@vaadin/component-base/src/styles/style-props.js';
7+
import { css } from 'lit';
8+
9+
export const messageInputButtonStyles = css`
10+
:host {
11+
flex: none;
12+
align-self: end;
13+
margin: var(--vaadin-input-field-padding, var(--vaadin-padding-container));
14+
--vaadin-button-border-width: 0;
15+
--vaadin-button-background: transparent;
16+
--vaadin-button-text-color: var(--vaadin-color);
17+
--vaadin-button-padding: 0;
18+
}
19+
20+
:host([theme~='icon-button']) {
21+
width: var(--vaadin-icon-size, 1lh);
22+
height: var(--vaadin-icon-size, 1lh);
23+
color: transparent;
24+
position: relative;
25+
contain: strict;
26+
}
27+
28+
:host([theme~='icon-button'])::before {
29+
content: '';
30+
position: absolute;
31+
inset: 0;
32+
mask-image: var(--_vaadin-icon-paper-airplane);
33+
background: var(--vaadin-button-text-color);
34+
}
35+
36+
:host([dir='rtl'][theme~='icon-button'])::before {
37+
scale: -1;
38+
}
39+
40+
@media (forced-colors: active) {
41+
:host([theme~='icon-button']) {
42+
forced-color-adjust: none;
43+
--vaadin-button-text-color: CanvasText;
44+
}
45+
}
46+
`;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2021 - 2025 Vaadin Ltd.
4+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5+
*/
6+
import { ButtonMixin } from '@vaadin/button/src/vaadin-button-mixin.js';
7+
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
8+
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
9+
10+
/**
11+
* An element used internally by `<vaadin-message-input>`. Not intended to be used separately.
12+
*/
13+
declare class MessageInputButton extends ButtonMixin(DirMixin(ThemableMixin(HTMLElement))) {}
14+
15+
declare global {
16+
interface HTMLElementTagNameMap {
17+
'vaadin-message-input-button': MessageInputButton;
18+
}
19+
}
20+
21+
export { MessageInputButton };
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2021 - 2025 Vaadin Ltd.
4+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5+
*/
6+
import { html, LitElement } from 'lit';
7+
import { buttonStyles } from '@vaadin/button/src/styles/vaadin-button-base-styles.js';
8+
import { ButtonMixin } from '@vaadin/button/src/vaadin-button-mixin.js';
9+
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
10+
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
11+
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
12+
import { LumoInjectionMixin } from '@vaadin/vaadin-themable-mixin/lumo-injection-mixin.js';
13+
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
14+
import { messageInputButtonStyles } from './styles/vaadin-message-input-button-styles.js';
15+
16+
/**
17+
* An element used internally by `<vaadin-message-input>`. Not intended to be used separately.
18+
*
19+
* @customElement
20+
* @extends HTMLElement
21+
* @mixes ButtonMixin
22+
* @mixes DirMixin
23+
* @mixes ThemableMixin
24+
* @private
25+
*/
26+
class MessageInputButton extends ButtonMixin(DirMixin(ThemableMixin(PolylitMixin(LumoInjectionMixin(LitElement))))) {
27+
static get is() {
28+
return 'vaadin-message-input-button';
29+
}
30+
31+
static get styles() {
32+
return [buttonStyles, messageInputButtonStyles];
33+
}
34+
35+
/** @protected */
36+
render() {
37+
return html`
38+
<div class="vaadin-button-container">
39+
<span part="label">
40+
<slot></slot>
41+
</span>
42+
</div>
43+
`;
44+
}
45+
}
46+
47+
defineCustomElement(MessageInputButton);
48+
49+
export { MessageInputButton };

packages/message-input/src/vaadin-message-input-mixin.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export const MessageInputMixin = (superClass) =>
5656

5757
static get observers() {
5858
return [
59-
'__buttonPropsChanged(_button, disabled, __effectiveI18n)',
59+
'__buttonPropsChanged(_button, disabled, _theme, __effectiveI18n)',
6060
'__textAreaPropsChanged(_textArea, disabled, __effectiveI18n, value)',
6161
];
6262
}
@@ -90,7 +90,7 @@ export const MessageInputMixin = (superClass) =>
9090
ready() {
9191
super.ready();
9292

93-
this._buttonController = new SlotController(this, 'button', 'vaadin-button', {
93+
this._buttonController = new SlotController(this, 'button', 'vaadin-message-input-button', {
9494
initializer: (btn) => {
9595
btn.addEventListener('click', () => {
9696
this.__submit();
@@ -134,10 +134,19 @@ export const MessageInputMixin = (superClass) =>
134134
}
135135

136136
/** @private */
137-
__buttonPropsChanged(button, disabled, effectiveI18n) {
137+
__buttonPropsChanged(button, disabled, theme, effectiveI18n) {
138138
if (button) {
139139
button.disabled = disabled;
140140
button.textContent = effectiveI18n.send;
141+
142+
// Only set theme attribute on the default button
143+
if (button === this._buttonController.defaultNode) {
144+
if (theme) {
145+
button.setAttribute('theme', theme);
146+
} else {
147+
button.removeAttribute('theme');
148+
}
149+
}
141150
}
142151
}
143152

packages/message-input/src/vaadin-message-input.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ export type MessageInputEventMap = HTMLElementEventMap & MessageInputCustomEvent
4242
* `disabled` | Set when the element is disabled
4343
* `has-tooltip` | Set when the element has a slotted tooltip
4444
*
45+
* ### Internal components
46+
*
47+
* In addition to `<vaadin-message-input>` itself, the following internal
48+
* components are themable:
49+
*
50+
* - `<vaadin-message-input-button>` - has the same API as `<vaadin-button>`
51+
* - `<vaadin-text-area>`
52+
*
4553
* See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
4654
*/
4755
declare class MessageInput extends MessageInputMixin(ThemableMixin(ElementMixin(HTMLElement))) {

packages/message-input/src/vaadin-message-input.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
* Copyright (c) 2021 - 2025 Vaadin Ltd.
44
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
55
*/
6-
import '@vaadin/button/src/vaadin-button.js';
76
import '@vaadin/text-area/src/vaadin-text-area.js';
7+
import './vaadin-message-input-button.js';
88
import { html, LitElement } from 'lit';
99
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
1010
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
@@ -35,6 +35,14 @@ import { MessageInputMixin } from './vaadin-message-input-mixin.js';
3535
* `disabled` | Set when the element is disabled
3636
* `has-tooltip` | Set when the element has a slotted tooltip
3737
*
38+
* ### Internal components
39+
*
40+
* In addition to `<vaadin-message-input>` itself, the following internal
41+
* components are themable:
42+
*
43+
* - `<vaadin-message-input-button>` - has the same API as `<vaadin-button>`
44+
* - `<vaadin-text-area>`
45+
*
3846
* See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
3947
*
4048
* @customElement

packages/message-input/test/dom/__snapshots__/message-input.test.snap.js

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ export const snapshots = {};
33

44
snapshots["vaadin-message-input default"] =
55
`<vaadin-message-input>
6-
<vaadin-button
6+
<vaadin-message-input-button
77
role="button"
88
slot="button"
99
tabindex="0"
1010
>
1111
Send
12-
</vaadin-button>
12+
</vaadin-message-input-button>
1313
<vaadin-text-area
1414
placeholder="Message"
1515
slot="textarea"
@@ -42,15 +42,15 @@ snapshots["vaadin-message-input default"] =
4242

4343
snapshots["vaadin-message-input disabled"] =
4444
`<vaadin-message-input disabled="">
45-
<vaadin-button
45+
<vaadin-message-input-button
4646
aria-disabled="true"
4747
disabled=""
4848
role="button"
4949
slot="button"
5050
tabindex="-1"
5151
>
5252
Send
53-
</vaadin-button>
53+
</vaadin-message-input-button>
5454
<vaadin-text-area
5555
aria-disabled="true"
5656
disabled=""
@@ -85,3 +85,43 @@ snapshots["vaadin-message-input disabled"] =
8585
`;
8686
/* end snapshot vaadin-message-input disabled */
8787

88+
snapshots["vaadin-message-input theme"] =
89+
`<vaadin-message-input theme="icon-only">
90+
<vaadin-message-input-button
91+
role="button"
92+
slot="button"
93+
tabindex="0"
94+
theme="icon-only"
95+
>
96+
Send
97+
</vaadin-message-input-button>
98+
<vaadin-text-area
99+
placeholder="Message"
100+
slot="textarea"
101+
>
102+
<label
103+
for="textarea-vaadin-text-area-3"
104+
id="label-vaadin-text-area-0"
105+
slot="label"
106+
>
107+
</label>
108+
<div
109+
hidden=""
110+
id="error-message-vaadin-text-area-2"
111+
slot="error-message"
112+
>
113+
</div>
114+
<textarea
115+
aria-label="Message"
116+
enterkeyhint="send"
117+
id="textarea-vaadin-text-area-3"
118+
placeholder="Message"
119+
rows="1"
120+
slot="textarea"
121+
>
122+
</textarea>
123+
</vaadin-text-area>
124+
</vaadin-message-input>
125+
`;
126+
/* end snapshot vaadin-message-input theme */
127+

packages/message-input/test/dom/message-input.test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,10 @@ describe('vaadin-message-input', () => {
2020
input.disabled = true;
2121
await expect(input).dom.to.equalSnapshot();
2222
});
23+
24+
it('theme', async () => {
25+
input.setAttribute('theme', 'icon-only');
26+
await nextUpdate(input);
27+
await expect(input).dom.to.equalSnapshot();
28+
});
2329
});

0 commit comments

Comments
 (0)