From 10272ec864f4707ac9a156289d6e1efd759b2c3f Mon Sep 17 00:00:00 2001 From: Jouni Koivuviita Date: Tue, 11 Aug 2026 15:49:17 +0300 Subject: [PATCH] wip --- dev/messages-ai-chat.html | 314 ++++++++++++++---- dev/playground/messages.html | 77 +++++ packages/aura/src/color.css | 1 + packages/aura/src/components/message-list.css | 26 ++ packages/aura/src/surface.css | 1 + .../vaadin-message-input-base-styles.js | 29 +- .../vaadin-message-input-button-styles.js | 15 +- .../src/vaadin-message-input-mixin.js | 8 +- .../message-input/src/vaadin-message-input.js | 6 + .../src/styles/vaadin-message-base-styles.js | 97 ++++++ .../vaadin-message-list-base-styles.d.ts | 8 + .../styles/vaadin-message-list-base-styles.js | 77 +++++ .../src/vaadin-message-list-mixin.js | 78 ++++- .../message-list/src/vaadin-message-list.js | 26 +- .../message-list/src/vaadin-message-mixin.js | 14 +- .../vaadin-upload-file-list-base-styles.js | 4 + .../src/vaadin-upload-file-list-mixin.js | 1 + .../src/components/message-input.css | 16 + 18 files changed, 701 insertions(+), 97 deletions(-) create mode 100644 dev/playground/messages.html create mode 100644 packages/message-list/src/styles/vaadin-message-list-base-styles.d.ts create mode 100644 packages/message-list/src/styles/vaadin-message-list-base-styles.js diff --git a/dev/messages-ai-chat.html b/dev/messages-ai-chat.html index 127e9ea58ac..9f594a524e8 100644 --- a/dev/messages-ai-chat.html +++ b/dev/messages-ai-chat.html @@ -14,41 +14,75 @@ margin: 0; } - #chat { - display: flex; - flex-direction: column; + vaadin-split-layout, + .chat { height: 100%; } - #compose-area { - display: flex; - flex-direction: column; - gap: 8px; - padding: 8px; + .chat { + display: grid; + grid-template-rows: minmax(0, 1fr) auto; + + .footer { + box-sizing: border-box; + width: 100%; + padding: var(--vaadin-message-padding, var(--vaadin-padding-s) var(--vaadin-padding-m)); + padding-top: 0; + max-width: var(--vaadin-message-list-max-width); + margin-inline: auto; + } + + vaadin-message-list { + mask-image: linear-gradient(#000 calc(100% - var(--vaadin-padding-m)), transparent); + } + } + + #group-chat { + --vaadin-message-list-max-width: 30em; + } + + #assistant-chat { + --vaadin-message-list-max-width: 50em; } - #input-layout { + .prompt-buttons { display: flex; - align-items: baseline; - gap: 8px; + flex-wrap: wrap; + gap: var(--vaadin-gap-xs); } - vaadin-message-list { - flex: 1 1 auto; + .disclaimer { + font-size: 0.8em; + color: var(--vaadin-text-color-secondary); + text-align: center; } - vaadin-message-input { - flex: 1; + theme-switcher { + bottom: 50%; } - vaadin-upload-drop-zone { - height: 100%; + .controls { + position: fixed; + z-index: 1; + top: 0.5em; + right: 0.5em; + background: var(--vaadin-background-color); + padding: 0.5em; + display: grid; + gap: var(--vaadin-gap-s); + border: 1px solid var(--vaadin-border-color); + border-radius: var(--vaadin-radius-m); + box-shadow: 0 2px 3px -1px hsl(0 0 0 / 0.2); } - -
- - -
- -
- - - - -
+
+ + +
+ +
+ +
- - + + + + + +
+ diff --git a/dev/playground/messages.html b/dev/playground/messages.html new file mode 100644 index 00000000000..330c4e1c035 --- /dev/null +++ b/dev/playground/messages.html @@ -0,0 +1,77 @@ + + + + + + + Messages + + + + + + + + + + +

Default

+ + Typing… + Typing… + Typing… + + + + +

Icon-Button Variant

+ + + diff --git a/packages/aura/src/color.css b/packages/aura/src/color.css index 1ac4e831302..eaf76f3c58d 100644 --- a/packages/aura/src/color.css +++ b/packages/aura/src/color.css @@ -68,6 +68,7 @@ vaadin-drawer-toggle, vaadin-dialog, vaadin-menu-bar-button, vaadin-menu-bar-item, +vaadin-message, vaadin-message::part(attachment), vaadin-notification-container, vaadin-notification-card, diff --git a/packages/aura/src/components/message-list.css b/packages/aura/src/components/message-list.css index aaaf7f1fbd7..5814374d91c 100644 --- a/packages/aura/src/components/message-list.css +++ b/packages/aura/src/components/message-list.css @@ -4,6 +4,11 @@ --vaadin-message-attachment-border-width: 1px; } +vaadin-message:not([theme~='full-width']) { + --aura-accent-color-light: var(--vaadin-user-color); + --aura-accent-color-dark: var(--vaadin-user-color); +} + vaadin-message::part(attachment) { --aura-surface-level: 4; background: var(--vaadin-message-attachment-background, var(--aura-surface-color) padding-box); @@ -21,3 +26,24 @@ vaadin-message::part(attachment-icon) { background: color-mix(in srgb, var(--aura-accent-color) 10%, transparent); color: var(--aura-accent-color); } + +vaadin-message-list[theme~='bubble'] > vaadin-message:not([theme~='full-width'], [typing][theme~='text']) { + --aura-surface-level: 3; + --aura-surface-opacity: 0.6; + + &::part(content) { + background: var(--aura-surface-color) padding-box; + border: 1px solid var(--vaadin-border-color-secondary); + box-shadow: var(--aura-shadow-xs); + } + + &[theme~='self'] { + --aura-surface-level: 2; + --aura-surface-opacity: 1; + + &::part(content) { + background: var(--aura-accent-surface) padding-box; + border-color: var(--aura-accent-border-color); + } + } +} diff --git a/packages/aura/src/surface.css b/packages/aura/src/surface.css index ff2a30bb0d7..1037a772674 100644 --- a/packages/aura/src/surface.css +++ b/packages/aura/src/surface.css @@ -22,6 +22,7 @@ vaadin-grid, vaadin-grid-pro, vaadin-master-detail-layout::part(detail), vaadin-menu-bar-button, +vaadin-message, vaadin-message::part(attachment), vaadin-message-input, vaadin-radio-button::part(radio), diff --git a/packages/message-input/src/styles/vaadin-message-input-base-styles.js b/packages/message-input/src/styles/vaadin-message-input-base-styles.js index cef3c7abbfe..9df2be463f8 100644 --- a/packages/message-input/src/styles/vaadin-message-input-base-styles.js +++ b/packages/message-input/src/styles/vaadin-message-input-base-styles.js @@ -10,23 +10,36 @@ export const messageInputStyles = css` :host { box-sizing: border-box; display: flex; + flex-wrap: wrap; max-height: 50vh; flex-shrink: 0; border: var(--vaadin-input-field-border-width, 1px) solid var(--vaadin-input-field-border-color, var(--vaadin-border-color)); border-radius: var(--vaadin-input-field-border-radius, var(--vaadin-radius-m)); background: var(--vaadin-input-field-background, var(--vaadin-background-color)); + padding: var( + --vaadin-input-field-padding, + var(--vaadin-padding-block-container) var(--vaadin-padding-inline-container) + ); + gap: var(--vaadin-input-field-gap, var(--vaadin-gap-s)); } :host([hidden]) { display: none !important; } - :host(:focus-within) { + :host(:has(textarea:focus)) { outline: var(--vaadin-focus-ring-width) solid var(--vaadin-focus-ring-color); outline-offset: calc(var(--vaadin-input-field-border-width, 1px) * -1); } + @scope { + :scope:has(textarea:focus) { + outline: var(--vaadin-focus-ring-width) solid var(--vaadin-focus-ring-color); + outline-offset: calc(var(--vaadin-input-field-border-width, 1px) * -1); + } + } + :host([disabled]) { --vaadin-input-field-value-color: var(--vaadin-input-field-disabled-text-color, var(--vaadin-text-color-disabled)); --vaadin-input-field-background: var( @@ -38,8 +51,20 @@ export const messageInputStyles = css` ::slotted([slot='textarea']) { flex: 1; - --vaadin-input-field-border-width: 0 !important; + --vaadin-input-field-padding: 0px !important; + --vaadin-input-field-border-width: 0px !important; --vaadin-focus-ring-width: 0; --vaadin-input-field-background: transparent !important; + --vaadin-input-field-disabled-background: transparent !important; + --vaadin-input-field-border-radius: 0px !important; + align-self: center; + } + + ::slotted([slot='button']) { + margin-inline-start: auto; + } + + slot:is([name='header'], [name='footer'])::slotted(*) { + width: 100%; } `; diff --git a/packages/message-input/src/styles/vaadin-message-input-button-styles.js b/packages/message-input/src/styles/vaadin-message-input-button-styles.js index 4432ecd2d66..67b38ec3b64 100644 --- a/packages/message-input/src/styles/vaadin-message-input-button-styles.js +++ b/packages/message-input/src/styles/vaadin-message-input-button-styles.js @@ -10,13 +10,18 @@ export const messageInputButtonStyles = css` :host { flex: none; align-self: end; - margin: var( - --vaadin-input-field-padding, - var(--vaadin-padding-block-container) var(--vaadin-padding-inline-container) - ); --vaadin-button-border-width: 0; --vaadin-button-background: transparent; - --vaadin-button-text-color: var(--vaadin-text-color); + --vaadin-button-text-color: var(--vaadin-text-color-secondary); --vaadin-button-padding: 0; + --vaadin-button-border-radius: var(--vaadin-radius-s); + } + + :host(:is([focus-ring], :focus-visible)) { + outline-offset: 0.125em; + } + + :host(:hover) { + --vaadin-button-text-color: var(--vaadin-text-color); } `; diff --git a/packages/message-input/src/vaadin-message-input-mixin.js b/packages/message-input/src/vaadin-message-input-mixin.js index 3a3f033eb65..d24732791cc 100644 --- a/packages/message-input/src/vaadin-message-input-mixin.js +++ b/packages/message-input/src/vaadin-message-input-mixin.js @@ -124,6 +124,8 @@ export const MessageInputMixin = (superClass) => this._tooltipController = new TooltipController(this); this.addController(this._tooltipController); + + this.addEventListener('click', () => this._textArea.focus()); } focus(options) { @@ -136,7 +138,11 @@ export const MessageInputMixin = (superClass) => __buttonPropsChanged(button, disabled, effectiveI18n, value) { if (button) { button.disabled = disabled || !value; - button.textContent = effectiveI18n.send; + if (button.localName === 'vaadin-message-input-button') { + button.textContent = effectiveI18n.send; + } else if (button.textContent.trim().length === 0) { + button.setAttribute('aria-label', effectiveI18n.send); + } } } diff --git a/packages/message-input/src/vaadin-message-input.js b/packages/message-input/src/vaadin-message-input.js index 9eeecf6f8dd..0d713eb8e5c 100644 --- a/packages/message-input/src/vaadin-message-input.js +++ b/packages/message-input/src/vaadin-message-input.js @@ -65,10 +65,16 @@ class MessageInput extends MessageInputMixin( /** @protected */ render() { return html` + + + + + + `; } diff --git a/packages/message-list/src/styles/vaadin-message-base-styles.js b/packages/message-list/src/styles/vaadin-message-base-styles.js index 45099d01296..10bf7e418fa 100644 --- a/packages/message-list/src/styles/vaadin-message-base-styles.js +++ b/packages/message-list/src/styles/vaadin-message-base-styles.js @@ -6,10 +6,18 @@ import '@vaadin/component-base/src/styles/style-props.js'; import { css } from 'lit'; +CSS.registerProperty({ + name: '--_vaadin-message-typing-mask-pos', + syntax: '', + inherits: false, + initialValue: 0, +}); + export const messageStyles = css` :host { display: flex; flex-direction: row; + box-sizing: border-box; padding: var(--vaadin-message-padding, var(--vaadin-padding-s) var(--vaadin-padding-m)); gap: var(--vaadin-message-gap, var(--vaadin-gap-xs) var(--vaadin-gap-s)); outline-offset: calc(var(--vaadin-focus-ring-width) * -1); @@ -29,6 +37,9 @@ export const messageStyles = css` flex-direction: column; flex-grow: 1; gap: inherit; + background: var(--vaadin-message-content-background, transparent); + padding: var(--vaadin-message-content-padding, 0); + border-radius: var(--vaadin-message-content-border-radius, 0); } [part='header'] { @@ -41,6 +52,7 @@ export const messageStyles = css` } [part='name'] { + display: var(--vaadin-message-name-display, inline); font-size: var(--vaadin-message-name-font-size, inherit); font-weight: var(--vaadin-message-name-font-weight, 500); color: var(--vaadin-message-name-color, var(--vaadin-text-color)); @@ -62,6 +74,12 @@ export const messageStyles = css` ::slotted([slot='avatar']) { flex: none; + visibility: var(--vaadin-message-avatar-visibility, visible); + display: var(--vaadin-message-avatar-display); + } + + ::slotted(vaadin-avatar-group) { + width: fit-content; } ::slotted(vaadin-markdown) { @@ -73,6 +91,7 @@ export const messageStyles = css` flex-wrap: wrap; gap: var(--vaadin-gap-s); padding-bottom: var(--vaadin-gap-xs); + justify-content: var(--vaadin-message-attachments-alignment, start); } [part~='attachment'] { @@ -132,4 +151,82 @@ export const messageStyles = css` padding: var(--vaadin-message-attachment-padding, var(--vaadin-padding-s)); padding-inline-start: 0; } + + :host([typing]:not([theme~='text'])) [part='message'], + :host([typing][theme~='text']) [part='content'] { + mask-image: linear-gradient( + 90deg, + hsla(0, 0%, 0%, 0.4) calc(var(--_vaadin-message-typing-mask-pos) - max(60px, 60%)), + hsl(0, 0%, 0%) calc(var(--_vaadin-message-typing-mask-pos) - max(40px, 40%)), + hsl(0, 0%, 0%), + calc(var(--_vaadin-message-typing-mask-pos) - max(20px, 20%)), + hsla(0, 0%, 0%, 0.4) var(--_vaadin-message-typing-mask-pos) + ); + animation: --_vaadin-message-typing-slide 1.5s ease-in-out infinite; + width: fit-content !important; + color: var(--vaadin-text-color) !important; + + [part='time'] { + display: none; + } + + [part='header'] { + display: contents; + } + } + + @keyframes --_vaadin-message-typing-slide { + 100% { + --_vaadin-message-typing-mask-pos: calc(100% + max(60px, 60%)); + } + } + + :host([typing][theme~='ellipsis']) { + [part='message'] { + line-height: inherit !important; + color: var(--vaadin-text-color-secondary) !important; + } + + [part='message']::before { + content: ''; + display: block; + width: var(--vaadin-icon-size, 1lh); + height: var(--vaadin-icon-size, 1lh); + mask: var(--_vaadin-icon-ellipsis); + background: currentColor; + } + + [part='header'], + [part='message'] slot { + display: none; + } + + [part='content'] { + align-self: center; + } + } + + :host([typing][theme~='text']) { + display: block; + + slot[name='avatar'] { + display: none; + } + + [part='content'] { + display: block; + } + + [part='header'], + [part='name'], + [part='message'] { + display: contents; + color: inherit; + font-size: inherit; + } + + [part='message'] { + text-transform: lowercase; + } + } `; diff --git a/packages/message-list/src/styles/vaadin-message-list-base-styles.d.ts b/packages/message-list/src/styles/vaadin-message-list-base-styles.d.ts new file mode 100644 index 00000000000..adc8994f855 --- /dev/null +++ b/packages/message-list/src/styles/vaadin-message-list-base-styles.d.ts @@ -0,0 +1,8 @@ +/** + * @license + * Copyright (c) 2021 - 2026 Vaadin Ltd. + * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ + */ +import type { CSSResult } from 'lit'; + +export const messageListStyles: CSSResult; diff --git a/packages/message-list/src/styles/vaadin-message-list-base-styles.js b/packages/message-list/src/styles/vaadin-message-list-base-styles.js new file mode 100644 index 00000000000..7d3d6dea5c4 --- /dev/null +++ b/packages/message-list/src/styles/vaadin-message-list-base-styles.js @@ -0,0 +1,77 @@ +/** + * @license + * Copyright (c) 2021 - 2026 Vaadin Ltd. + * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ + */ +import '@vaadin/component-base/src/styles/style-props.js'; +import { css } from 'lit'; + +export const messageListStyles = css` + :host { + display: block; + overflow: auto !important; + padding: var(--vaadin-message-list-padding, var(--vaadin-padding-xs) 0); + scroll-padding: var(--vaadin-message-list-padding, var(--vaadin-padding-xs) 0); + scroll-snap-type: y proximity; + box-sizing: border-box; + scrollbar-gutter: stable both-edges; + } + + :host([hidden]) { + display: none !important; + } + + :host([theme~='sticky-typing-indicator']) slot[name='typing-indicator']::slotted(*) { + position: sticky; + bottom: 0; + margin-top: auto; + } + + [part='list'] { + display: flex; + flex-direction: column; + box-sizing: border-box; + margin-inline: auto; + width: 100%; + height: 100%; + max-width: var(--vaadin-message-list-max-width, none); + } + + .scroll-lock { + scroll-snap-align: var(--_vaadin-message-list-scroll-snap-align, none); + height: 0; + } + + :host([theme~='bubble']) ::slotted(vaadin-message) { + --vaadin-message-name-font-size: 0.9em; + } + + :host([theme~='bubble']) ::slotted(vaadin-message[theme~='full-width']) { + padding-block: var(--vaadin-padding-xl); + } + + :host([theme~='bubble']) ::slotted(vaadin-message:not([theme~='full-width'], :where([typing][theme~='text']))) { + --vaadin-message-content-background: var(--vaadin-background-container); + --vaadin-message-content-padding: var(--vaadin-padding-s) var(--vaadin-padding-m); + --vaadin-message-content-border-radius: 0 var(--vaadin-radius-l) var(--vaadin-radius-l) var(--vaadin-radius-l); + + width: fit-content; + max-width: calc(100% - 2em); + } + + :host([theme~='bubble']) ::slotted(vaadin-message[theme~='self']) { + --vaadin-message-content-border-radius: var(--vaadin-radius-l) 0 var(--vaadin-radius-l) var(--vaadin-radius-l); + --vaadin-message-avatar-visibility: hidden; + --vaadin-message-content-background: linear-gradient(color-mix(in srgb, var(--vaadin-user-color) 10%, transparent)) + var(--vaadin-background-color); + --vaadin-message-attachments-alignment: end; + --vaadin-message-name-display: none; + align-items: end; + align-self: end; + } + + :host([theme~='bubble'][theme~='one-to-one']) ::slotted(vaadin-message) { + --vaadin-message-name-display: none; + --vaadin-message-avatar-display: none; + } +`; diff --git a/packages/message-list/src/vaadin-message-list-mixin.js b/packages/message-list/src/vaadin-message-list-mixin.js index f79e36cbdb7..9315189f906 100644 --- a/packages/message-list/src/vaadin-message-list-mixin.js +++ b/packages/message-list/src/vaadin-message-list-mixin.js @@ -65,6 +65,33 @@ export const MessageListMixin = (superClass) => observer: '__announceChanged', sync: true, }, + + /** + * An array of objects which will be rendered as avatars. + * The user objects can have the following properties: + * ```js + * Array<{ + * name: string, + * abbr: string, + * img: string, + * colorIndex: number + * }> + * ``` + */ + usersTyping: { + type: Array, + observer: '__usersTypingChanged', + sync: true, + }, + + typingIndicatorText: { + type: String, + value: 'Typing…', + }, + + typingIndicatorTheme: { + type: String, + }, }; } @@ -145,6 +172,14 @@ export const MessageListMixin = (superClass) => requestAnimationFrame(() => { if (items.length > oldItems.length && closeToBottom) { this._scrollToLastMessage(); + + const loadingMarkdown = this.markdown && !customElements.get('vaadin-markdown'); + + if (loadingMarkdown) { + customElements.whenDefined('vaadin-markdown').then(() => { + this._scrollToLastMessage(); + }); + } } }); } @@ -163,6 +198,35 @@ export const MessageListMixin = (superClass) => this._renderMessages(this.items); } + /** @private */ + __usersTypingChanged(users) { + let typingIndicator = this.querySelector('vaadin-message[slot="typing-indicator"]'); + if (!typingIndicator) { + typingIndicator = document.createElement('vaadin-message'); + typingIndicator.slot = 'typing-indicator'; + typingIndicator.setAttribute('typing', ''); + const avatars = document.createElement('vaadin-avatar-group'); + avatars.maxItemsVisible = 100; + avatars.slot = 'avatar'; + const typingText = document.createElement('span'); + typingIndicator.append(avatars, typingText); + } + if (users.length === 0) { + typingIndicator.remove(); + } else { + const formatter = new Intl.ListFormat(navigator.language, { + type: 'conjunction', + }); + const avatars = typingIndicator.querySelector(':scope > vaadin-avatar-group'); + avatars.items = users; + const typingText = typingIndicator.querySelector(':scope > span'); + typingIndicator.setAttribute('theme', this.typingIndicatorTheme); + typingText.textContent = this.typingIndicatorText; + typingIndicator.userName = formatter.format(users.map((user) => user.name)); + this.append(typingIndicator); + } + } + /** @private */ _renderMessages(items) { // Check if markdown component is still loading @@ -184,10 +248,12 @@ export const MessageListMixin = (superClass) => class="${ifDefined(item.className)}" @focusin="${this._onMessageFocusIn}" @attachment-click="${(e) => this.__onAttachmentClick(e, item)}" - style="${ifDefined(loadingMarkdown ? 'visibility: hidden' : undefined)}" - >${ - this.markdown ? html`` : item.text - }${this.markdown + ? html`` + : item.text} `, )} @@ -206,11 +272,11 @@ export const MessageListMixin = (superClass) => /** @private */ __enableScrollSnapping() { - this.$.list.style.setProperty('--_vaadin-message-list-scroll-snap-align', 'end'); + this.style.setProperty('--_vaadin-message-list-scroll-snap-align', 'end'); // Disable scroll-snapping after a delay to allow the user to freely scroll // without being snapped back to the bottom. this.__debounceScrollSnapping = Debouncer.debounce(this.__debounceScrollSnapping, timeOut.after(500), () => { - this.$.list.style.removeProperty('--_vaadin-message-list-scroll-snap-align'); + this.style.removeProperty('--_vaadin-message-list-scroll-snap-align'); }); } diff --git a/packages/message-list/src/vaadin-message-list.js b/packages/message-list/src/vaadin-message-list.js index 9f976fb6525..7567dfe9b78 100644 --- a/packages/message-list/src/vaadin-message-list.js +++ b/packages/message-list/src/vaadin-message-list.js @@ -4,12 +4,14 @@ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ */ import './vaadin-message.js'; -import { css, html, LitElement } from 'lit'; +import '@vaadin/avatar-group'; +import { html, LitElement } from 'lit'; import { defineCustomElement } from '@vaadin/component-base/src/define.js'; import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js'; import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js'; import { SlotStylesMixin } from '@vaadin/component-base/src/slot-styles-mixin.js'; import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js'; +import { messageListStyles } from './styles/vaadin-message-list-base-styles.js'; import { MessageListMixin } from './vaadin-message-list-mixin.js'; /** @@ -57,25 +59,7 @@ class MessageList extends SlotStylesMixin(MessageListMixin(ElementMixin(Themable } static get styles() { - return css` - :host { - display: block; - overflow: auto; - padding: var(--vaadin-message-list-padding, var(--vaadin-padding-xs) 0); - scroll-padding: var(--vaadin-message-list-padding, var(--vaadin-padding-xs) 0); - scroll-snap-type: y proximity; - } - - :host([hidden]) { - display: none !important; - } - - [part='list']::after { - content: ''; - display: block; - scroll-snap-align: var(--_vaadin-message-list-scroll-snap-align, none); - } - `; + return messageListStyles; } /** @protected */ @@ -83,6 +67,8 @@ class MessageList extends SlotStylesMixin(MessageListMixin(ElementMixin(Themable return html`
+ +
`; } diff --git a/packages/message-list/src/vaadin-message-mixin.js b/packages/message-list/src/vaadin-message-mixin.js index 0a83bddeace..0d4a4948d5f 100644 --- a/packages/message-list/src/vaadin-message-mixin.js +++ b/packages/message-list/src/vaadin-message-mixin.js @@ -85,7 +85,10 @@ export const MessageMixin = (superClass) => } static get observers() { - return ['__avatarChanged(_avatar, userName, userAbbr, userImg, userColorIndex)']; + return [ + '__avatarChanged(_avatar, userName, userAbbr, userImg, userColorIndex)', + '__colorIndexChanged(userColorIndex)', + ]; } /** @protected */ @@ -113,6 +116,15 @@ export const MessageMixin = (superClass) => } } + /** @private */ + __colorIndexChanged(userColorIndex) { + if (userColorIndex !== undefined) { + this.style.setProperty('--vaadin-user-color', `var(--vaadin-user-color-${userColorIndex})`); + } else { + this.style.removeProperty('--vaadin-user-color'); + } + } + /** * Renders attachments for the message. * @private diff --git a/packages/upload/src/styles/vaadin-upload-file-list-base-styles.js b/packages/upload/src/styles/vaadin-upload-file-list-base-styles.js index b24dcbece26..9c2b90ff6ba 100644 --- a/packages/upload/src/styles/vaadin-upload-file-list-base-styles.js +++ b/packages/upload/src/styles/vaadin-upload-file-list-base-styles.js @@ -16,6 +16,10 @@ export const uploadFileListStyles = css` display: none !important; } + :host([empty]) { + display: none; + } + [part='list'] { list-style-type: none; margin: 0; diff --git a/packages/upload/src/vaadin-upload-file-list-mixin.js b/packages/upload/src/vaadin-upload-file-list-mixin.js index 0682449fe58..79f1f1c9d7f 100644 --- a/packages/upload/src/vaadin-upload-file-list-mixin.js +++ b/packages/upload/src/vaadin-upload-file-list-mixin.js @@ -262,6 +262,7 @@ export const UploadFileListMixin = (superClass) => items.forEach((file) => this.__applyI18nToFile(file)); this.requestContentUpdate(); } + this.toggleAttribute('empty', items.length === 0); } /** @private */ diff --git a/packages/vaadin-lumo-styles/src/components/message-input.css b/packages/vaadin-lumo-styles/src/components/message-input.css index 91d66bc1e01..9864de6abd3 100644 --- a/packages/vaadin-lumo-styles/src/components/message-input.css +++ b/packages/vaadin-lumo-styles/src/components/message-input.css @@ -8,6 +8,7 @@ align-items: flex-start; box-sizing: border-box; display: flex; + flex-wrap: wrap; max-height: 50vh; overflow: hidden; flex-shrink: 0; @@ -27,4 +28,19 @@ flex-grow: 1; margin-inline-end: var(--lumo-space-s); } + + :host(:has([slot='prefix'])) ::slotted([slot='textarea']) { + margin-inline-start: var(--lumo-space-s); + } + + @scope { + :scope:has([slot='prefix']) ::slotted([slot='textarea']) { + margin-inline-start: var(--lumo-space-s); + } + } + + ::slotted([slot='header']), + ::slotted([slot='footer']) { + width: 100%; + } }