Skip to content

Commit b189f4d

Browse files
committed
feat: message-list item attachments
1 parent b52505a commit b189f4d

File tree

4 files changed

+117
-2
lines changed

4 files changed

+117
-2
lines changed

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

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,18 @@ export const MessageListMixin = (superClass) =>
2727
* userImg: string,
2828
* userColorIndex: number,
2929
* className: string,
30-
* theme: string
30+
* theme: string,
31+
* attachments: Array<{
32+
* name: string,
33+
* url: string,
34+
* type: string
35+
* }>
3136
* }>
3237
* ```
38+
*
39+
* When a message has attachments, they are rendered in the attachments slot.
40+
* Image attachments (type starting with "image/") show a thumbnail preview,
41+
* while other attachments show a document icon with the file name.
3342
*/
3443
items: {
3544
type: Array,
@@ -141,7 +150,7 @@ export const MessageListMixin = (superClass) =>
141150
class="${ifDefined(item.className)}"
142151
@focusin="${this._onMessageFocusIn}"
143152
style="${ifDefined(loadingMarkdown ? 'visibility: hidden' : undefined)}"
144-
>${this.markdown
153+
>${this.__renderAttachments(item)}${this.markdown
145154
? html`<vaadin-markdown .content=${item.text}></vaadin-markdown>`
146155
: item.text}<vaadin-avatar slot="avatar"></vaadin-avatar
147156
></vaadin-message>
@@ -153,6 +162,60 @@ export const MessageListMixin = (superClass) =>
153162
);
154163
}
155164

165+
/**
166+
* Renders attachments for a message item.
167+
* @param {Object} item - The message item
168+
* @return {import('lit').TemplateResult | string}
169+
* @private
170+
*/
171+
__renderAttachments(item) {
172+
const attachments = item.attachments;
173+
if (!attachments || attachments.length === 0) {
174+
return '';
175+
}
176+
177+
return html`
178+
<div slot="attachments" class="vaadin-message-attachments">
179+
${attachments.map((attachment) => this.__renderAttachment(attachment))}
180+
</div>
181+
`;
182+
}
183+
184+
/**
185+
* Renders a single attachment.
186+
* @param {Object} attachment - The attachment object with name, url, and type properties
187+
* @return {import('lit').TemplateResult}
188+
* @private
189+
*/
190+
__renderAttachment(attachment) {
191+
const isImage = attachment.type && attachment.type.startsWith('image/');
192+
193+
if (isImage) {
194+
return html`
195+
<a
196+
class="vaadin-message-attachment vaadin-message-attachment-image"
197+
href="${attachment.url}"
198+
target="_blank"
199+
rel="noopener noreferrer"
200+
>
201+
<img src="${attachment.url}" alt="${attachment.name || ''}" />
202+
</a>
203+
`;
204+
}
205+
206+
return html`
207+
<a
208+
class="vaadin-message-attachment vaadin-message-attachment-file"
209+
href="${attachment.url}"
210+
target="_blank"
211+
rel="noopener noreferrer"
212+
>
213+
<span class="vaadin-message-attachment-icon"></span>
214+
<span class="vaadin-message-attachment-name">${attachment.name || attachment.url}</span>
215+
</a>
216+
`;
217+
}
218+
156219
/** @private */
157220
_scrollToLastMessage() {
158221
if (this.items.length > 0) {

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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,45 @@ class MessageList extends SlotStylesMixin(MessageListMixin(ElementMixin(Themable
9292
${tag} :where(vaadin-markdown > :is(h1, h2, h3, h4, h5, h6, p, ul, ol):last-child) {
9393
margin-bottom: 0;
9494
}
95+
96+
${tag} .vaadin-message-attachments {
97+
display: flex;
98+
flex-wrap: wrap;
99+
gap: 6px;
100+
}
101+
102+
${tag} .vaadin-message-attachment {
103+
display: inline-flex;
104+
align-items: center;
105+
color: inherit;
106+
}
107+
108+
${tag} .vaadin-message-attachment-image img {
109+
display: block;
110+
max-width: 200px;
111+
max-height: 150px;
112+
}
113+
114+
${tag} .vaadin-message-attachment-file {
115+
gap: 6px;
116+
}
117+
118+
${tag} .vaadin-message-attachment-icon {
119+
display: inline-block;
120+
width: 1em;
121+
height: 1em;
122+
background: currentColor;
123+
mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/></svg>');
124+
mask-size: contain;
125+
mask-repeat: no-repeat;
126+
flex-shrink: 0;
127+
}
128+
129+
${tag} .vaadin-message-attachment-name {
130+
overflow: hidden;
131+
text-overflow: ellipsis;
132+
white-space: nowrap;
133+
}
95134
`,
96135
];
97136
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ import { MessageMixin } from './vaadin-message-mixin.js';
2323
* user-img = "/static/img/avatar.jpg">There is no real ending. It's just the place where you stop the story.</vaadin-message>
2424
* ```
2525
*
26+
* ### Slots
27+
*
28+
* Slot name | Description
29+
* --------------|----------------
30+
* `attachments` | Content to be displayed above the message text. Used for file and image attachments.
31+
*
2632
* ### Styling
2733
*
2834
* The following shadow DOM parts are available for styling:
@@ -70,6 +76,7 @@ class Message extends MessageMixin(ElementMixin(ThemableMixin(PolylitMixin(LumoI
7076
<span part="name">${this.userName}</span>
7177
<span part="time">${this.time}</span>
7278
</div>
79+
<slot name="attachments"></slot>
7380
<div part="message"><slot></slot></div>
7481
</div>
7582
`;

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ snapshots["vaadin-message default"] =
1111
<span part="time">
1212
</span>
1313
</div>
14+
<slot name="attachments">
15+
</slot>
1416
<div part="message">
1517
<slot>
1618
</slot>
@@ -30,6 +32,8 @@ snapshots["vaadin-message userName"] =
3032
<span part="time">
3133
</span>
3234
</div>
35+
<slot name="attachments">
36+
</slot>
3337
<div part="message">
3438
<slot>
3539
</slot>
@@ -49,6 +53,8 @@ snapshots["vaadin-message time"] =
4953
long ago
5054
</span>
5155
</div>
56+
<slot name="attachments">
57+
</slot>
5258
<div part="message">
5359
<slot>
5460
</slot>

0 commit comments

Comments
 (0)