igniteui-webcomponents 6.2.0 → 6.3.0-alpha.0
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/components/chat/chat-input.d.ts +70 -0
- package/components/chat/chat-input.js +266 -0
- package/components/chat/chat-input.js.map +1 -0
- package/components/chat/chat-message-list.d.ts +111 -0
- package/components/chat/chat-message-list.js +178 -0
- package/components/chat/chat-message-list.js.map +1 -0
- package/components/chat/chat-message.d.ts +57 -0
- package/components/chat/chat-message.js +59 -0
- package/components/chat/chat-message.js.map +1 -0
- package/components/chat/chat-state.d.ts +155 -0
- package/components/chat/chat-state.js +167 -0
- package/components/chat/chat-state.js.map +1 -0
- package/components/chat/chat.d.ts +164 -0
- package/components/chat/chat.js +168 -0
- package/components/chat/chat.js.map +1 -0
- package/components/chat/markdown-util.d.ts +2 -0
- package/components/chat/markdown-util.js +22 -0
- package/components/chat/markdown-util.js.map +1 -0
- package/components/chat/message-attachments.d.ts +45 -0
- package/components/chat/message-attachments.js +127 -0
- package/components/chat/message-attachments.js.map +1 -0
- package/components/chat/themes/chat.base.css.d.ts +1 -0
- package/components/chat/themes/chat.base.css.js +3 -0
- package/components/chat/themes/chat.base.css.js.map +1 -0
- package/components/chat/themes/dark/chat.bootstrap.css.d.ts +1 -0
- package/components/chat/themes/dark/chat.bootstrap.css.js +3 -0
- package/components/chat/themes/dark/chat.bootstrap.css.js.map +1 -0
- package/components/chat/themes/dark/chat.fluent.css.d.ts +1 -0
- package/components/chat/themes/dark/chat.fluent.css.js +3 -0
- package/components/chat/themes/dark/chat.fluent.css.js.map +1 -0
- package/components/chat/themes/dark/chat.indigo.css.d.ts +1 -0
- package/components/chat/themes/dark/chat.indigo.css.js +3 -0
- package/components/chat/themes/dark/chat.indigo.css.js.map +1 -0
- package/components/chat/themes/dark/chat.material.css.d.ts +1 -0
- package/components/chat/themes/dark/chat.material.css.js +3 -0
- package/components/chat/themes/dark/chat.material.css.js.map +1 -0
- package/components/chat/themes/input.base.css.d.ts +1 -0
- package/components/chat/themes/input.base.css.js +3 -0
- package/components/chat/themes/input.base.css.js.map +1 -0
- package/components/chat/themes/input.d.ts +2 -0
- package/components/chat/themes/input.js +35 -0
- package/components/chat/themes/input.js.map +1 -0
- package/components/chat/themes/light/chat.bootstrap.css.d.ts +1 -0
- package/components/chat/themes/light/chat.bootstrap.css.js +3 -0
- package/components/chat/themes/light/chat.bootstrap.css.js.map +1 -0
- package/components/chat/themes/light/chat.fluent.css.d.ts +1 -0
- package/components/chat/themes/light/chat.fluent.css.js +3 -0
- package/components/chat/themes/light/chat.fluent.css.js.map +1 -0
- package/components/chat/themes/light/chat.indigo.css.d.ts +1 -0
- package/components/chat/themes/light/chat.indigo.css.js +3 -0
- package/components/chat/themes/light/chat.indigo.css.js.map +1 -0
- package/components/chat/themes/light/chat.material.css.d.ts +1 -0
- package/components/chat/themes/light/chat.material.css.js +3 -0
- package/components/chat/themes/light/chat.material.css.js.map +1 -0
- package/components/chat/themes/light/chat.shared.css.d.ts +1 -0
- package/components/chat/themes/light/chat.shared.css.js +3 -0
- package/components/chat/themes/light/chat.shared.css.js.map +1 -0
- package/components/chat/themes/message-attachments.base.css.d.ts +1 -0
- package/components/chat/themes/message-attachments.base.css.js +3 -0
- package/components/chat/themes/message-attachments.base.css.js.map +1 -0
- package/components/chat/themes/message-list.base.css.d.ts +1 -0
- package/components/chat/themes/message-list.base.css.js +3 -0
- package/components/chat/themes/message-list.base.css.js.map +1 -0
- package/components/chat/themes/message.base.css.d.ts +1 -0
- package/components/chat/themes/message.base.css.js +3 -0
- package/components/chat/themes/message.base.css.js.map +1 -0
- package/components/chat/themes/shared/chat-message.common.css.d.ts +1 -0
- package/components/chat/themes/shared/chat-message.common.css.js +3 -0
- package/components/chat/themes/shared/chat-message.common.css.js.map +1 -0
- package/components/chat/themes/shared/chat.common.css.d.ts +1 -0
- package/components/chat/themes/shared/chat.common.css.js +3 -0
- package/components/chat/themes/shared/chat.common.css.js.map +1 -0
- package/components/chat/themes/shared/input/input.bootstrap.css.d.ts +1 -0
- package/components/chat/themes/shared/input/input.bootstrap.css.js +3 -0
- package/components/chat/themes/shared/input/input.bootstrap.css.js.map +1 -0
- package/components/chat/themes/shared/input/input.common.css.d.ts +1 -0
- package/components/chat/themes/shared/input/input.common.css.js +3 -0
- package/components/chat/themes/shared/input/input.common.css.js.map +1 -0
- package/components/chat/themes/shared/input/input.fluent.css.d.ts +1 -0
- package/components/chat/themes/shared/input/input.fluent.css.js +3 -0
- package/components/chat/themes/shared/input/input.fluent.css.js.map +1 -0
- package/components/chat/themes/shared/input/input.indigo.css.d.ts +1 -0
- package/components/chat/themes/shared/input/input.indigo.css.js +3 -0
- package/components/chat/themes/shared/input/input.indigo.css.js.map +1 -0
- package/components/chat/themes/shared/input/input.material.css.d.ts +1 -0
- package/components/chat/themes/shared/input/input.material.css.js +3 -0
- package/components/chat/themes/shared/input/input.material.css.js.map +1 -0
- package/components/chat/themes/shared/message-attachments.common.css.d.ts +1 -0
- package/components/chat/themes/shared/message-attachments.common.css.js +3 -0
- package/components/chat/themes/shared/message-attachments.common.css.js.map +1 -0
- package/components/chat/themes/shared/message-list.common.css.d.ts +1 -0
- package/components/chat/themes/shared/message-list.common.css.js +3 -0
- package/components/chat/themes/shared/message-list.common.css.js.map +1 -0
- package/components/chat/themes/themes.d.ts +2 -0
- package/components/chat/themes/themes.js +46 -0
- package/components/chat/themes/themes.js.map +1 -0
- package/components/chat/types.d.ts +190 -0
- package/components/chat/types.js +8 -0
- package/components/chat/types.js.map +1 -0
- package/components/checkbox/themes/dark/switch/switch.bootstrap.css.js +1 -1
- package/components/checkbox/themes/dark/switch/switch.bootstrap.css.js.map +1 -1
- package/components/checkbox/themes/dark/switch/switch.fluent.css.js +1 -1
- package/components/checkbox/themes/dark/switch/switch.fluent.css.js.map +1 -1
- package/components/checkbox/themes/dark/switch/switch.indigo.css.js +1 -1
- package/components/checkbox/themes/dark/switch/switch.indigo.css.js.map +1 -1
- package/components/checkbox/themes/dark/switch/switch.material.css.js +1 -1
- package/components/checkbox/themes/dark/switch/switch.material.css.js.map +1 -1
- package/components/checkbox/themes/light/switch/switch.bootstrap.css.js +1 -1
- package/components/checkbox/themes/light/switch/switch.bootstrap.css.js.map +1 -1
- package/components/checkbox/themes/light/switch/switch.fluent.css.js +1 -1
- package/components/checkbox/themes/light/switch/switch.fluent.css.js.map +1 -1
- package/components/checkbox/themes/light/switch/switch.indigo.css.js +1 -1
- package/components/checkbox/themes/light/switch/switch.indigo.css.js.map +1 -1
- package/components/checkbox/themes/light/switch/switch.material.css.js +1 -1
- package/components/checkbox/themes/light/switch/switch.material.css.js.map +1 -1
- package/components/common/context.d.ts +5 -1
- package/components/common/context.js +2 -1
- package/components/common/context.js.map +1 -1
- package/components/common/definitions/defineAllComponents.js +2 -0
- package/components/common/definitions/defineAllComponents.js.map +1 -1
- package/custom-elements.json +15692 -13967
- package/igniteui-webcomponents.css-data.json +1 -1
- package/igniteui-webcomponents.html-data.json +1 -1
- package/index.d.ts +3 -0
- package/index.js +1 -0
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/web-types.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-message-list.js","sourceRoot":"","sources":["../../../src/components/chat/chat-message-list.ts"],"names":[],"mappings":"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","sourcesContent":["import { consume } from '@lit/context';\nimport { html, LitElement, nothing } from 'lit';\nimport { state } from 'lit/decorators.js';\nimport { repeat } from 'lit/directives/repeat.js';\nimport { chatContext } from '../common/context.js';\nimport {\n addKeybindings,\n arrowDown,\n arrowUp,\n endKey,\n homeKey,\n} from '../common/controllers/key-bindings.js';\nimport { registerComponent } from '../common/definitions/register.js';\nimport IgcChatMessageComponent from './chat-message.js';\nimport type { ChatState } from './chat-state.js';\nimport { styles } from './themes/message-list.base.css.js';\nimport { styles as shared } from './themes/shared/message-list.common.css.js';\nimport type { IgcMessage } from './types.js';\n\n/**\n * A chat message list component that displays a list of chat messages grouped by date.\n *\n * @element igc-chat-message-list\n *\n * This component:\n * - Renders messages using the `<igc-chat-message>` component.\n * - Supports keyboard navigation between messages (Home, End, ArrowUp, ArrowDown).\n * - Manages focus highlighting on active messages.\n * - Automatically scrolls to the bottom when new messages arrive, unless auto-scroll is disabled.\n * - Displays a typing indicator if the chat state option `isComposing` is true.\n *\n * Accessibility:\n * - Uses ARIA roles and properties for grouping and active descendant management.\n * - Message items have role=\"option\" for assistive technologies.\n *\n */\nexport default class IgcChatMessageListComponent extends LitElement {\n /** Tag name of the custom element. */\n public static readonly tagName = 'igc-chat-message-list';\n\n /** Styles applied to the component */\n public static override styles = [styles, shared];\n\n /**\n * Consumed chat state context providing messages, options, and user data.\n * @private\n */\n @consume({ context: chatContext, subscribe: true })\n private _chatState?: ChatState;\n\n /**\n * Currently active message id used for focus and keyboard navigation.\n * @private\n */\n @state()\n private _activeMessageId = '';\n\n /**\n * Registers this component and its dependencies.\n * Used internally for component setup.\n */\n /* blazorSuppress */\n public static register() {\n registerComponent(IgcChatMessageListComponent, IgcChatMessageComponent);\n }\n\n constructor() {\n super();\n addKeybindings(this)\n .set(homeKey, this.navigateToMessage)\n .set(endKey, this.navigateToMessage)\n .set(arrowUp, this.navigateToMessage)\n .set(arrowDown, this.navigateToMessage);\n }\n\n /**\n * Formats a date to a human-readable label.\n * Returns 'Today', 'Yesterday', or localized date string.\n * @param date - Date object to format\n * @returns formatted string\n * @private\n */\n private formatDate(date: Date): string {\n const today = new Date();\n const yesterday = new Date(today);\n yesterday.setDate(yesterday.getDate() - 1);\n\n if (date.toDateString() === today.toDateString()) {\n return 'Today';\n }\n\n if (date.toDateString() === yesterday.toDateString()) {\n return 'Yesterday';\n }\n\n return date.toLocaleDateString('en-US', {\n weekday: 'long',\n month: 'short',\n day: 'numeric',\n });\n }\n\n /**\n * Groups messages by their date labels.\n * @param messages - Array of messages to group\n * @returns Array of groups with date label and messages\n * @private\n */\n private groupMessagesByDate(\n messages: IgcMessage[]\n ): { date: string; messages: IgcMessage[] }[] {\n const grouped: { [key: string]: IgcMessage[] } = {};\n\n messages.forEach((message) => {\n const dateStr = this.formatDate(message.timestamp);\n if (!grouped[dateStr]) {\n grouped[dateStr] = [];\n }\n grouped[dateStr].push(message);\n });\n\n return Object.keys(grouped).map((date) => ({\n date,\n messages: grouped[date],\n }));\n }\n\n /**\n * Scrolls the container to the bottom, typically called after new messages are rendered.\n * @private\n */\n private scrollToBottom() {\n requestAnimationFrame(() => {\n const container = this.shadowRoot?.host as HTMLElement;\n if (container) {\n container.scrollTop = container.scrollHeight;\n }\n });\n }\n\n /**\n * Scrolls the view to a specific message by id.\n * @param messageId - The id of the message to scroll to\n * @private\n */\n private scrollToMessage(messageId: string) {\n const messageElement = this.shadowRoot?.querySelector(\n `#message-${messageId}`\n );\n messageElement?.scrollIntoView();\n }\n\n /**\n * Handles focus entering the message list container.\n * Sets the active message to the last message for keyboard navigation.\n * @private\n */\n private handleFocusIn() {\n if (!this._chatState?.messages || this._chatState.messages.length === 0) {\n return;\n }\n const lastMessage = this._chatState.sortedMessagesIds?.pop() ?? '';\n this._activeMessageId = lastMessage !== '' ? `message-${lastMessage}` : '';\n }\n\n /**\n * Handles focus leaving the message list container.\n * Clears the active message.\n * @private\n */\n private handleFocusOut() {\n this._activeMessageId = '';\n }\n\n /**\n * Handles keyboard navigation within the message list.\n * Supports Home, End, ArrowUp, ArrowDown keys to move active message focus.\n * @param e KeyboardEvent from user input\n * @private\n */\n private navigateToMessage(e: KeyboardEvent) {\n if (!this._chatState?.messages || this._chatState.messages.length === 0) {\n return;\n }\n\n const currentIndex = this._chatState?.sortedMessagesIds.findIndex(\n (id) => `message-${id}` === this._activeMessageId\n );\n let activeMessageId = '';\n const key = e.key.toLowerCase();\n\n switch (key) {\n case 'home':\n activeMessageId = this._chatState.sortedMessagesIds[0];\n break;\n case 'end':\n activeMessageId =\n this._chatState.sortedMessagesIds[\n this._chatState.sortedMessagesIds.length - 1\n ];\n break;\n case 'arrowup':\n if (currentIndex > 0) {\n activeMessageId = this._chatState.sortedMessagesIds[currentIndex - 1];\n }\n break;\n case 'arrowdown':\n if (currentIndex < this._chatState?.messages.length - 1) {\n activeMessageId = this._chatState.sortedMessagesIds[currentIndex + 1];\n }\n break;\n default:\n return; // Ignore other keys\n }\n\n this._activeMessageId = `message-${activeMessageId}`;\n this.scrollToMessage(activeMessageId);\n }\n\n /**\n * Lifecycle: called when the component updates.\n * Scrolls to bottom unless auto-scroll is disabled.\n */\n protected override updated() {\n if (!this._chatState?.options?.disableAutoScroll) {\n this.scrollToBottom();\n }\n }\n\n /**\n * Lifecycle: called after first render.\n * Scrolls to bottom unless auto-scroll is disabled.\n */\n protected override firstUpdated() {\n if (!this._chatState?.options?.disableAutoScroll) {\n this.scrollToBottom();\n }\n }\n\n /**\n * Renders the typing indicator template if composing.\n * @protected\n */\n protected *renderLoadingTemplate() {\n yield html`${this._chatState?.options?.templates?.composingIndicatorTemplate\n ? this._chatState.options.templates.composingIndicatorTemplate\n : html`<div part=\"typing-indicator\">\n <div part=\"typing-dot\"></div>\n <div part=\"typing-dot\"></div>\n <div part=\"typing-dot\"></div>\n </div>`}`;\n }\n\n /**\n * Main render method.\n * Groups messages by date and renders each message.\n * Shows the typing indicator if applicable.\n */\n protected override render() {\n const groupedMessages = this.groupMessagesByDate(\n this._chatState?.messages ?? []\n );\n\n return html`\n <div\n part=\"message-container\"\n aria-activedescendant=${this._activeMessageId}\n role=\"group\"\n aria-label=\"Message list\"\n tabindex=\"0\"\n @focusin=${this.handleFocusIn}\n @focusout=${this.handleFocusOut}\n ></div>\n <div part=\"message-list\">\n ${repeat(\n groupedMessages,\n (group) => group.date,\n (group) => html`\n ${repeat(\n group.messages,\n (message) => message.id,\n (message) => {\n const messageId = `message-${message.id}`;\n return html`\n <igc-chat-message\n id=${messageId}\n role=\"option\"\n part=\"message-item ${this._activeMessageId === messageId\n ? 'active'\n : ''}\"\n .message=${message}\n >\n </igc-chat-message>\n `;\n }\n )}\n `\n )}\n ${\n this._chatState?.options?.isComposing\n ? this.renderLoadingTemplate()\n : nothing\n }\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'igc-chat-message-list': IgcChatMessageListComponent;\n }\n}\n"]}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
import type { IgcMessage } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* A chat message component for displaying individual messages in `<igc-chat>`.
|
|
5
|
+
*
|
|
6
|
+
* @element igc-chat-message
|
|
7
|
+
*
|
|
8
|
+
* This component renders a single chat message including:
|
|
9
|
+
* - Message text (sanitized)
|
|
10
|
+
* - Attachments (if any)
|
|
11
|
+
* - Custom templates for message content and actions (if provided via chat options)
|
|
12
|
+
*
|
|
13
|
+
* It distinguishes sent messages from received messages by comparing
|
|
14
|
+
* the message sender with the current user ID from chat state.
|
|
15
|
+
*
|
|
16
|
+
* The message text is sanitized with DOMPurify before rendering,
|
|
17
|
+
* and can be rendered with a markdown renderer if provided.
|
|
18
|
+
*/
|
|
19
|
+
export default class IgcChatMessageComponent extends LitElement {
|
|
20
|
+
/** Tag name of the custom element. */
|
|
21
|
+
static readonly tagName = "igc-chat-message";
|
|
22
|
+
/** Styles applied to the component. */
|
|
23
|
+
static styles: import("lit").CSSResult[];
|
|
24
|
+
/**
|
|
25
|
+
* Injected chat state context. Provides message data, user info, and options.
|
|
26
|
+
* @private
|
|
27
|
+
*/
|
|
28
|
+
private _chatState?;
|
|
29
|
+
/**
|
|
30
|
+
* Registers this component and its dependencies.
|
|
31
|
+
* This is used internally to set up the component definitions.
|
|
32
|
+
*/
|
|
33
|
+
static register(): void;
|
|
34
|
+
/**
|
|
35
|
+
* The chat message to render.
|
|
36
|
+
*/
|
|
37
|
+
message: IgcMessage | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Sanitizes message text to prevent XSS or invalid HTML.
|
|
40
|
+
* @param text The raw message text
|
|
41
|
+
* @returns Sanitized text safe for HTML rendering
|
|
42
|
+
* @private
|
|
43
|
+
*/
|
|
44
|
+
private sanitizeMessageText;
|
|
45
|
+
/**
|
|
46
|
+
* Renders the chat message template.
|
|
47
|
+
* - Applies 'sent' CSS class if the message sender matches current user.
|
|
48
|
+
* - Uses markdown rendering if configured.
|
|
49
|
+
* - Renders attachments and custom templates if provided.
|
|
50
|
+
*/
|
|
51
|
+
protected render(): import("lit-html").TemplateResult<1>;
|
|
52
|
+
}
|
|
53
|
+
declare global {
|
|
54
|
+
interface HTMLElementTagNameMap {
|
|
55
|
+
'igc-chat-message': IgcChatMessageComponent;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { consume } from '@lit/context';
|
|
8
|
+
import DOMPurify from 'dompurify';
|
|
9
|
+
import { html, LitElement, nothing } from 'lit';
|
|
10
|
+
import { property } from 'lit/decorators.js';
|
|
11
|
+
import IgcAvatarComponent from '../avatar/avatar.js';
|
|
12
|
+
import { chatContext } from '../common/context.js';
|
|
13
|
+
import { registerComponent } from '../common/definitions/register.js';
|
|
14
|
+
import { renderMarkdown } from './markdown-util.js';
|
|
15
|
+
import IgcMessageAttachmentsComponent from './message-attachments.js';
|
|
16
|
+
import { styles } from './themes/message.base.css.js';
|
|
17
|
+
import { styles as shared } from './themes/shared/chat-message.common.css.js';
|
|
18
|
+
class IgcChatMessageComponent extends LitElement {
|
|
19
|
+
static register() {
|
|
20
|
+
registerComponent(IgcChatMessageComponent, IgcMessageAttachmentsComponent, IgcAvatarComponent);
|
|
21
|
+
}
|
|
22
|
+
sanitizeMessageText(text) {
|
|
23
|
+
return DOMPurify.sanitize(text);
|
|
24
|
+
}
|
|
25
|
+
render() {
|
|
26
|
+
const containerPart = `message-container ${this.message?.sender === this._chatState?.currentUserId ? 'sent' : ''}`;
|
|
27
|
+
const sanitizedMessageText = this.sanitizeMessageText(this.message?.text.trim() || '');
|
|
28
|
+
const renderer = this._chatState?.options?.markdownRenderer || renderMarkdown;
|
|
29
|
+
return html `
|
|
30
|
+
<div part=${containerPart}>
|
|
31
|
+
<div part="bubble">
|
|
32
|
+
${this._chatState?.options?.templates?.messageTemplate && this.message
|
|
33
|
+
? this._chatState.options.templates.messageTemplate(this.message)
|
|
34
|
+
: html `${sanitizedMessageText
|
|
35
|
+
? html `<div>${renderer(sanitizedMessageText)}</div>`
|
|
36
|
+
: nothing}`}
|
|
37
|
+
${this.message?.attachments && this.message?.attachments.length > 0
|
|
38
|
+
? html `<igc-message-attachments .message=${this.message}>
|
|
39
|
+
</igc-message-attachments>`
|
|
40
|
+
: nothing}
|
|
41
|
+
${this._chatState?.options?.templates?.messageActionsTemplate &&
|
|
42
|
+
this.message
|
|
43
|
+
? this._chatState.options.templates.messageActionsTemplate(this.message)
|
|
44
|
+
: nothing}
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
`;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
IgcChatMessageComponent.tagName = 'igc-chat-message';
|
|
51
|
+
IgcChatMessageComponent.styles = [styles, shared];
|
|
52
|
+
export default IgcChatMessageComponent;
|
|
53
|
+
__decorate([
|
|
54
|
+
consume({ context: chatContext, subscribe: true })
|
|
55
|
+
], IgcChatMessageComponent.prototype, "_chatState", void 0);
|
|
56
|
+
__decorate([
|
|
57
|
+
property({ attribute: false })
|
|
58
|
+
], IgcChatMessageComponent.prototype, "message", void 0);
|
|
59
|
+
//# sourceMappingURL=chat-message.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-message.js","sourceRoot":"","sources":["../../../src/components/chat/chat-message.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,SAAS,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,kBAAkB,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAEtE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,8BAA8B,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AACtD,OAAO,EAAE,MAAM,IAAI,MAAM,EAAE,MAAM,4CAA4C,CAAC;AAmB9E,MAAqB,uBAAwB,SAAQ,UAAU;IAmBtD,MAAM,CAAC,QAAQ;QACpB,iBAAiB,CACf,uBAAuB,EACvB,8BAA8B,EAC9B,kBAAkB,CACnB,CAAC;IACJ,CAAC;IAcO,mBAAmB,CAAC,IAAY;QACtC,OAAO,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAQkB,MAAM;QACvB,MAAM,aAAa,GAAG,qBAAqB,IAAI,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QACnH,MAAM,oBAAoB,GAAG,IAAI,CAAC,mBAAmB,CACnD,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,CAChC,CAAC;QACF,MAAM,QAAQ,GACZ,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,gBAAgB,IAAI,cAAc,CAAC;QAE/D,OAAO,IAAI,CAAA;kBACG,aAAa;;YAEnB,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,eAAe,IAAI,IAAI,CAAC,OAAO;YACpE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC;YACjE,CAAC,CAAC,IAAI,CAAA,GAAG,oBAAoB;gBACzB,CAAC,CAAC,IAAI,CAAA,QAAQ,QAAQ,CAAC,oBAAoB,CAAC,QAAQ;gBACpD,CAAC,CAAC,OAAO,EAAE;YACf,IAAI,CAAC,OAAO,EAAE,WAAW,IAAI,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,GAAG,CAAC;YACjE,CAAC,CAAC,IAAI,CAAA,qCAAqC,IAAI,CAAC,OAAO;yCAC1B;YAC7B,CAAC,CAAC,OAAO;YACT,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,sBAAsB;YAC7D,IAAI,CAAC,OAAO;YACV,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,sBAAsB,CACtD,IAAI,CAAC,OAAO,CACb;YACH,CAAC,CAAC,OAAO;;;KAGhB,CAAC;IACJ,CAAC;;AA5EsB,+BAAO,GAAG,kBAAkB,CAAC;AAG7B,8BAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;eAL9B,uBAAuB;AAYlC;IADP,OAAO,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;2DACpB;AAmBxB;IADN,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;wDACQ","sourcesContent":["import { consume } from '@lit/context';\nimport DOMPurify from 'dompurify';\nimport { html, LitElement, nothing } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport IgcAvatarComponent from '../avatar/avatar.js';\nimport { chatContext } from '../common/context.js';\nimport { registerComponent } from '../common/definitions/register.js';\nimport type { ChatState } from './chat-state.js';\nimport { renderMarkdown } from './markdown-util.js';\nimport IgcMessageAttachmentsComponent from './message-attachments.js';\nimport { styles } from './themes/message.base.css.js';\nimport { styles as shared } from './themes/shared/chat-message.common.css.js';\nimport type { IgcMessage } from './types.js';\n\n/**\n * A chat message component for displaying individual messages in `<igc-chat>`.\n *\n * @element igc-chat-message\n *\n * This component renders a single chat message including:\n * - Message text (sanitized)\n * - Attachments (if any)\n * - Custom templates for message content and actions (if provided via chat options)\n *\n * It distinguishes sent messages from received messages by comparing\n * the message sender with the current user ID from chat state.\n *\n * The message text is sanitized with DOMPurify before rendering,\n * and can be rendered with a markdown renderer if provided.\n */\nexport default class IgcChatMessageComponent extends LitElement {\n /** Tag name of the custom element. */\n public static readonly tagName = 'igc-chat-message';\n\n /** Styles applied to the component. */\n public static override styles = [styles, shared];\n\n /**\n * Injected chat state context. Provides message data, user info, and options.\n * @private\n */\n @consume({ context: chatContext, subscribe: true })\n private _chatState?: ChatState;\n\n /**\n * Registers this component and its dependencies.\n * This is used internally to set up the component definitions.\n */\n /* blazorSuppress */\n public static register() {\n registerComponent(\n IgcChatMessageComponent,\n IgcMessageAttachmentsComponent,\n IgcAvatarComponent\n );\n }\n\n /**\n * The chat message to render.\n */\n @property({ attribute: false })\n public message: IgcMessage | undefined;\n\n /**\n * Sanitizes message text to prevent XSS or invalid HTML.\n * @param text The raw message text\n * @returns Sanitized text safe for HTML rendering\n * @private\n */\n private sanitizeMessageText(text: string): string {\n return DOMPurify.sanitize(text);\n }\n\n /**\n * Renders the chat message template.\n * - Applies 'sent' CSS class if the message sender matches current user.\n * - Uses markdown rendering if configured.\n * - Renders attachments and custom templates if provided.\n */\n protected override render() {\n const containerPart = `message-container ${this.message?.sender === this._chatState?.currentUserId ? 'sent' : ''}`;\n const sanitizedMessageText = this.sanitizeMessageText(\n this.message?.text.trim() || ''\n );\n const renderer =\n this._chatState?.options?.markdownRenderer || renderMarkdown;\n\n return html`\n <div part=${containerPart}>\n <div part=\"bubble\">\n ${this._chatState?.options?.templates?.messageTemplate && this.message\n ? this._chatState.options.templates.messageTemplate(this.message)\n : html`${sanitizedMessageText\n ? html`<div>${renderer(sanitizedMessageText)}</div>`\n : nothing}`}\n ${this.message?.attachments && this.message?.attachments.length > 0\n ? html`<igc-message-attachments .message=${this.message}>\n </igc-message-attachments>`\n : nothing}\n ${this._chatState?.options?.templates?.messageActionsTemplate &&\n this.message\n ? this._chatState.options.templates.messageActionsTemplate(\n this.message\n )\n : nothing}\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'igc-chat-message': IgcChatMessageComponent;\n }\n}\n"]}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type IgcTextareaComponent from '../textarea/textarea.js';
|
|
2
|
+
import type IgcChatComponent from './chat.js';
|
|
3
|
+
import type { IgcChatComponentEventMap } from './chat.js';
|
|
4
|
+
import type { IgcChatOptions, IgcMessage, IgcMessageAttachment } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Internal state manager for the `<igc-chat>` component.
|
|
7
|
+
*
|
|
8
|
+
* Manages messages, input value, attachments, options, and event emissions.
|
|
9
|
+
*/
|
|
10
|
+
export declare class ChatState {
|
|
11
|
+
private readonly _host;
|
|
12
|
+
/** Reference to the text area input component */
|
|
13
|
+
private _textArea;
|
|
14
|
+
/** The current list of messages */
|
|
15
|
+
private _messages;
|
|
16
|
+
/** Sorted copy of messages by timestamp */
|
|
17
|
+
private _sortedMessages;
|
|
18
|
+
/** Chat options/configuration */
|
|
19
|
+
private _options?;
|
|
20
|
+
/** List of current input attachments */
|
|
21
|
+
private _inputAttachments;
|
|
22
|
+
/** Current input text */
|
|
23
|
+
private _inputValue;
|
|
24
|
+
/**
|
|
25
|
+
* Cache of accepted file types, organized into extensions, mimeTypes, and wildcardTypes
|
|
26
|
+
*/
|
|
27
|
+
private _acceptedTypesCache;
|
|
28
|
+
private _suggestionsPosition;
|
|
29
|
+
/**
|
|
30
|
+
* Array of message IDs sorted by timestamp ascending.
|
|
31
|
+
*/
|
|
32
|
+
get sortedMessagesIds(): string[];
|
|
33
|
+
/**
|
|
34
|
+
* Gets the list of chat messages.
|
|
35
|
+
*/
|
|
36
|
+
get messages(): IgcMessage[];
|
|
37
|
+
/**
|
|
38
|
+
* Sets the list of chat messages.
|
|
39
|
+
* Also sorts messages by timestamp and requests host update.
|
|
40
|
+
*/
|
|
41
|
+
set messages(value: IgcMessage[]);
|
|
42
|
+
/**
|
|
43
|
+
* Gets current chat options.
|
|
44
|
+
*/
|
|
45
|
+
get options(): IgcChatOptions | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Sets chat options and requests host update.
|
|
48
|
+
*/
|
|
49
|
+
set options(value: IgcChatOptions);
|
|
50
|
+
/**
|
|
51
|
+
* Gets the current user ID from options or returns 'user' as fallback.
|
|
52
|
+
*/
|
|
53
|
+
get currentUserId(): string;
|
|
54
|
+
/**
|
|
55
|
+
* Gets the current suggestionsPosition from options or returns the default value 'below-messages'.
|
|
56
|
+
*/
|
|
57
|
+
get suggestionsPosition(): string;
|
|
58
|
+
/**
|
|
59
|
+
* Gets the text area component.
|
|
60
|
+
*/
|
|
61
|
+
get textArea(): IgcTextareaComponent | null;
|
|
62
|
+
/**
|
|
63
|
+
* Sets the text area component.
|
|
64
|
+
*/
|
|
65
|
+
set textArea(value: IgcTextareaComponent);
|
|
66
|
+
/**
|
|
67
|
+
* Gets the list of attachments currently attached to input.
|
|
68
|
+
*/
|
|
69
|
+
get inputAttachments(): IgcMessageAttachment[];
|
|
70
|
+
/**
|
|
71
|
+
* Sets the input attachments and requests host update.
|
|
72
|
+
*/
|
|
73
|
+
set inputAttachments(value: IgcMessageAttachment[]);
|
|
74
|
+
/**
|
|
75
|
+
* Gets the current input value.
|
|
76
|
+
*/
|
|
77
|
+
get inputValue(): string;
|
|
78
|
+
/**
|
|
79
|
+
* Sets the current input value and requests host update.
|
|
80
|
+
*/
|
|
81
|
+
set inputValue(value: string);
|
|
82
|
+
/**
|
|
83
|
+
* Creates an instance of ChatState.
|
|
84
|
+
* @param chat The host `<igc-chat>` component.
|
|
85
|
+
*/
|
|
86
|
+
constructor(chat: IgcChatComponent);
|
|
87
|
+
/**
|
|
88
|
+
* Emits a custom event from the host component.
|
|
89
|
+
* @param name Event name (key of IgcChatComponentEventMap)
|
|
90
|
+
* @param args Event detail or options
|
|
91
|
+
* @returns true if event was not canceled, false otherwise
|
|
92
|
+
*/
|
|
93
|
+
emitEvent(name: keyof IgcChatComponentEventMap, args?: any): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Handles input text changes.
|
|
96
|
+
* Updates internal inputValue and emits 'igcInputChange' event.
|
|
97
|
+
* @param value New input value
|
|
98
|
+
*/
|
|
99
|
+
handleInputChange(value: string): void;
|
|
100
|
+
/**
|
|
101
|
+
* Adds a new chat message.
|
|
102
|
+
* Emits 'igcMessageCreated' event which can be canceled to prevent adding.
|
|
103
|
+
* Clears input value and attachments on success.
|
|
104
|
+
* @param message Partial message object with optional id, sender, timestamp
|
|
105
|
+
*/
|
|
106
|
+
addMessage(message: {
|
|
107
|
+
id?: string;
|
|
108
|
+
text: string;
|
|
109
|
+
sender?: string;
|
|
110
|
+
timestamp?: Date;
|
|
111
|
+
attachments?: IgcMessageAttachment[];
|
|
112
|
+
}): void;
|
|
113
|
+
/**
|
|
114
|
+
* Adds files as attachments to the input.
|
|
115
|
+
* Emits 'igcAttachmentChange' event which can be canceled to prevent adding.
|
|
116
|
+
* @param files Array of File objects to attach
|
|
117
|
+
*/
|
|
118
|
+
attachFiles(files: File[]): void;
|
|
119
|
+
/**
|
|
120
|
+
* Removes an attachment by index.
|
|
121
|
+
* Emits 'igcAttachmentChange' event which can be canceled to prevent removal.
|
|
122
|
+
* @param index Index of the attachment to remove
|
|
123
|
+
*/
|
|
124
|
+
removeAttachment(index: number): void;
|
|
125
|
+
/**
|
|
126
|
+
* Handles when a suggestion is clicked.
|
|
127
|
+
* Adds the suggestion as a new message and focuses the text area.
|
|
128
|
+
* @param suggestion The suggestion string clicked
|
|
129
|
+
*/
|
|
130
|
+
handleSuggestionClick(suggestion: string): void;
|
|
131
|
+
/**
|
|
132
|
+
* Updates the chat options partially.
|
|
133
|
+
* @param options Partial options to merge with current options
|
|
134
|
+
*/
|
|
135
|
+
updateOptions(options: Partial<IgcChatOptions>): void;
|
|
136
|
+
/**
|
|
137
|
+
* Updates the internal cache for accepted file types.
|
|
138
|
+
* Parses the acceptedFiles string option into extensions, mimeTypes, and wildcard types.
|
|
139
|
+
*/
|
|
140
|
+
updateAcceptedTypesCache(): void;
|
|
141
|
+
/**
|
|
142
|
+
* Checks if a file's type or extension is accepted by the chat's acceptedFiles setting.
|
|
143
|
+
* @param file File object to check
|
|
144
|
+
* @param type Optional MIME type override if no file provided
|
|
145
|
+
* @returns True if accepted, false otherwise
|
|
146
|
+
*/
|
|
147
|
+
isFileTypeAccepted(file: File, type?: string): boolean;
|
|
148
|
+
/**
|
|
149
|
+
* Checks if a slot is empty.
|
|
150
|
+
* @param name Slot name to check
|
|
151
|
+
* @returns True if the slot has content, false otherwise
|
|
152
|
+
*/
|
|
153
|
+
hasSlotContent(name: string): boolean;
|
|
154
|
+
}
|
|
155
|
+
export declare function createChatState(host: IgcChatComponent): ChatState;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
export class ChatState {
|
|
2
|
+
get sortedMessagesIds() {
|
|
3
|
+
return this._sortedMessages.map((m) => m.id);
|
|
4
|
+
}
|
|
5
|
+
get messages() {
|
|
6
|
+
return this._messages;
|
|
7
|
+
}
|
|
8
|
+
set messages(value) {
|
|
9
|
+
this._messages = value;
|
|
10
|
+
this._sortedMessages = value.slice().sort((a, b) => {
|
|
11
|
+
return a.timestamp.getTime() - b.timestamp.getTime();
|
|
12
|
+
});
|
|
13
|
+
this._host.requestUpdate();
|
|
14
|
+
}
|
|
15
|
+
get options() {
|
|
16
|
+
return this._options;
|
|
17
|
+
}
|
|
18
|
+
set options(value) {
|
|
19
|
+
this._options = value;
|
|
20
|
+
this._host.requestUpdate();
|
|
21
|
+
}
|
|
22
|
+
get currentUserId() {
|
|
23
|
+
return this._options?.currentUserId ?? 'user';
|
|
24
|
+
}
|
|
25
|
+
get suggestionsPosition() {
|
|
26
|
+
return this._options?.suggestionsPosition ?? this._suggestionsPosition;
|
|
27
|
+
}
|
|
28
|
+
get textArea() {
|
|
29
|
+
return this._textArea;
|
|
30
|
+
}
|
|
31
|
+
set textArea(value) {
|
|
32
|
+
this._textArea = value;
|
|
33
|
+
}
|
|
34
|
+
get inputAttachments() {
|
|
35
|
+
return this._inputAttachments;
|
|
36
|
+
}
|
|
37
|
+
set inputAttachments(value) {
|
|
38
|
+
this._inputAttachments = value;
|
|
39
|
+
this._host.requestUpdate();
|
|
40
|
+
}
|
|
41
|
+
get inputValue() {
|
|
42
|
+
return this._inputValue;
|
|
43
|
+
}
|
|
44
|
+
set inputValue(value) {
|
|
45
|
+
this._inputValue = value;
|
|
46
|
+
this._host.requestUpdate();
|
|
47
|
+
}
|
|
48
|
+
constructor(chat) {
|
|
49
|
+
this._textArea = null;
|
|
50
|
+
this._messages = [];
|
|
51
|
+
this._sortedMessages = [];
|
|
52
|
+
this._inputAttachments = [];
|
|
53
|
+
this._inputValue = '';
|
|
54
|
+
this._acceptedTypesCache = null;
|
|
55
|
+
this._suggestionsPosition = 'below-messages';
|
|
56
|
+
this._host = chat;
|
|
57
|
+
}
|
|
58
|
+
emitEvent(name, args) {
|
|
59
|
+
return this._host.emitEvent(name, args);
|
|
60
|
+
}
|
|
61
|
+
handleInputChange(value) {
|
|
62
|
+
this.inputValue = value;
|
|
63
|
+
this.emitEvent('igcInputChange', { detail: { value: this.inputValue } });
|
|
64
|
+
}
|
|
65
|
+
addMessage(message) {
|
|
66
|
+
const newMessage = {
|
|
67
|
+
id: message.id ?? Date.now().toString(),
|
|
68
|
+
text: message.text,
|
|
69
|
+
sender: message.sender ?? this.currentUserId,
|
|
70
|
+
timestamp: message.timestamp ?? new Date(),
|
|
71
|
+
attachments: message.attachments || [],
|
|
72
|
+
};
|
|
73
|
+
const allowed = this.emitEvent('igcMessageCreated', {
|
|
74
|
+
detail: newMessage,
|
|
75
|
+
cancelable: true,
|
|
76
|
+
});
|
|
77
|
+
if (allowed) {
|
|
78
|
+
if (!this.messages.some((msg) => msg.id === newMessage.id)) {
|
|
79
|
+
this.messages = [...this.messages, newMessage];
|
|
80
|
+
}
|
|
81
|
+
this.inputValue = '';
|
|
82
|
+
this.inputAttachments = [];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
attachFiles(files) {
|
|
86
|
+
const newAttachments = [];
|
|
87
|
+
let count = this.inputAttachments.length;
|
|
88
|
+
files.forEach((file) => {
|
|
89
|
+
if (this.inputAttachments.find((a) => a.name === file.name)) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const isImage = file.type.startsWith('image/');
|
|
93
|
+
newAttachments.push({
|
|
94
|
+
id: Date.now().toString() + count++,
|
|
95
|
+
url: URL.createObjectURL(file),
|
|
96
|
+
name: file.name,
|
|
97
|
+
file: file,
|
|
98
|
+
thumbnail: isImage ? URL.createObjectURL(file) : undefined,
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
const allowed = this.emitEvent('igcAttachmentChange', {
|
|
102
|
+
detail: [...this.inputAttachments, ...newAttachments],
|
|
103
|
+
cancelable: true,
|
|
104
|
+
});
|
|
105
|
+
if (allowed) {
|
|
106
|
+
this.inputAttachments = [...this.inputAttachments, ...newAttachments];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
removeAttachment(index) {
|
|
110
|
+
const allowed = this.emitEvent('igcAttachmentChange', {
|
|
111
|
+
detail: this.inputAttachments.filter((_, i) => i !== index),
|
|
112
|
+
cancelable: true,
|
|
113
|
+
});
|
|
114
|
+
if (allowed) {
|
|
115
|
+
this.inputAttachments = this.inputAttachments.filter((_, i) => i !== index);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
handleSuggestionClick(suggestion) {
|
|
119
|
+
this.addMessage({ text: suggestion });
|
|
120
|
+
if (this.textArea) {
|
|
121
|
+
this.textArea.focus();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
updateOptions(options) {
|
|
125
|
+
this.options = { ...this.options, ...options };
|
|
126
|
+
}
|
|
127
|
+
updateAcceptedTypesCache() {
|
|
128
|
+
if (!this.options?.acceptedFiles) {
|
|
129
|
+
this._acceptedTypesCache = null;
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const types = this.options?.acceptedFiles
|
|
133
|
+
.split(',')
|
|
134
|
+
.map((type) => type.trim().toLowerCase());
|
|
135
|
+
this._acceptedTypesCache = {
|
|
136
|
+
extensions: new Set(types.filter((t) => t.startsWith('.'))),
|
|
137
|
+
mimeTypes: new Set(types.filter((t) => !t.startsWith('.') && !t.endsWith('/*'))),
|
|
138
|
+
wildcardTypes: new Set(types.filter((t) => t.endsWith('/*')).map((t) => t.slice(0, -2))),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
isFileTypeAccepted(file, type = '') {
|
|
142
|
+
if (!this._acceptedTypesCache)
|
|
143
|
+
return true;
|
|
144
|
+
if (file === null && type === '')
|
|
145
|
+
return false;
|
|
146
|
+
const fileType = file != null ? file.type.toLowerCase() : type.toLowerCase();
|
|
147
|
+
const fileExtension = file != null
|
|
148
|
+
? `.${file.name.split('.').pop()?.toLowerCase()}`
|
|
149
|
+
: `.${type.split('/').pop()?.toLowerCase()}`;
|
|
150
|
+
if (this._acceptedTypesCache.extensions.has(fileExtension)) {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
if (this._acceptedTypesCache.mimeTypes.has(fileType)) {
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
const [fileBaseType] = fileType.split('/');
|
|
157
|
+
return this._acceptedTypesCache.wildcardTypes.has(fileBaseType);
|
|
158
|
+
}
|
|
159
|
+
hasSlotContent(name) {
|
|
160
|
+
return (this._host.renderRoot.querySelector(`slot[name=${name}]`)
|
|
161
|
+
?.childNodes.length !== 0);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
export function createChatState(host) {
|
|
165
|
+
return new ChatState(host);
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=chat-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-state.js","sourceRoot":"","sources":["../../../src/components/chat/chat-state.ts"],"names":[],"mappings":"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","sourcesContent":["import type IgcTextareaComponent from '../textarea/textarea.js';\nimport type IgcChatComponent from './chat.js';\nimport type { IgcChatComponentEventMap } from './chat.js';\nimport type {\n IgcChatOptions,\n IgcMessage,\n IgcMessageAttachment,\n} from './types.js';\n\n/**\n * Internal state manager for the `<igc-chat>` component.\n *\n * Manages messages, input value, attachments, options, and event emissions.\n */\nexport class ChatState {\n //#region Internal properties and state /** The host `<igc-chat>` component instance */\n private readonly _host: IgcChatComponent;\n /** Reference to the text area input component */\n private _textArea: IgcTextareaComponent | null = null;\n /** The current list of messages */\n private _messages: IgcMessage[] = [];\n /** Sorted copy of messages by timestamp */\n private _sortedMessages: IgcMessage[] = [];\n /** Chat options/configuration */\n private _options?: IgcChatOptions;\n /** List of current input attachments */\n private _inputAttachments: IgcMessageAttachment[] = [];\n /** Current input text */\n private _inputValue = '';\n /**\n * Cache of accepted file types, organized into extensions, mimeTypes, and wildcardTypes\n */\n private _acceptedTypesCache: {\n extensions: Set<string>;\n mimeTypes: Set<string>;\n wildcardTypes: Set<string>;\n } | null = null;\n\n private _suggestionsPosition: 'below-input' | 'below-messages' =\n 'below-messages';\n //#endregion\n\n //#region Public properties\n /**\n * Array of message IDs sorted by timestamp ascending.\n */\n public get sortedMessagesIds(): string[] {\n return this._sortedMessages.map((m) => m.id);\n }\n\n /**\n * Gets the list of chat messages.\n */\n public get messages(): IgcMessage[] {\n return this._messages;\n }\n\n /**\n * Sets the list of chat messages.\n * Also sorts messages by timestamp and requests host update.\n */\n public set messages(value: IgcMessage[]) {\n this._messages = value;\n this._sortedMessages = value.slice().sort((a, b) => {\n return a.timestamp.getTime() - b.timestamp.getTime();\n });\n this._host.requestUpdate();\n }\n\n /**\n * Gets current chat options.\n */\n public get options(): IgcChatOptions | undefined {\n return this._options;\n }\n\n /**\n * Sets chat options and requests host update.\n */\n public set options(value: IgcChatOptions) {\n this._options = value;\n this._host.requestUpdate();\n }\n\n /**\n * Gets the current user ID from options or returns 'user' as fallback.\n */\n public get currentUserId(): string {\n return this._options?.currentUserId ?? 'user';\n }\n\n /**\n * Gets the current suggestionsPosition from options or returns the default value 'below-messages'.\n */\n public get suggestionsPosition(): string {\n return this._options?.suggestionsPosition ?? this._suggestionsPosition;\n }\n /**\n * Gets the text area component.\n */\n public get textArea(): IgcTextareaComponent | null {\n return this._textArea;\n }\n\n /**\n * Sets the text area component.\n */\n public set textArea(value: IgcTextareaComponent) {\n this._textArea = value;\n }\n\n /**\n * Gets the list of attachments currently attached to input.\n */\n public get inputAttachments(): IgcMessageAttachment[] {\n return this._inputAttachments;\n }\n\n /**\n * Sets the input attachments and requests host update.\n */\n public set inputAttachments(value: IgcMessageAttachment[]) {\n this._inputAttachments = value;\n this._host.requestUpdate(); // Notify the host component to re-render\n }\n\n /**\n * Gets the current input value.\n */\n public get inputValue(): string {\n return this._inputValue;\n }\n\n /**\n * Sets the current input value and requests host update.\n */\n public set inputValue(value: string) {\n this._inputValue = value;\n this._host.requestUpdate();\n }\n //#endregion\n\n /**\n * Creates an instance of ChatState.\n * @param chat The host `<igc-chat>` component.\n */\n constructor(chat: IgcChatComponent) {\n this._host = chat;\n }\n\n //#region Event handlers\n\n /**\n * Emits a custom event from the host component.\n * @param name Event name (key of IgcChatComponentEventMap)\n * @param args Event detail or options\n * @returns true if event was not canceled, false otherwise\n */\n public emitEvent(name: keyof IgcChatComponentEventMap, args?: any) {\n return this._host.emitEvent(name, args);\n }\n\n /**\n * Handles input text changes.\n * Updates internal inputValue and emits 'igcInputChange' event.\n * @param value New input value\n */\n public handleInputChange(value: string): void {\n this.inputValue = value;\n this.emitEvent('igcInputChange', { detail: { value: this.inputValue } });\n }\n\n //#endregion\n\n //#region Public API\n\n /**\n * Adds a new chat message.\n * Emits 'igcMessageCreated' event which can be canceled to prevent adding.\n * Clears input value and attachments on success.\n * @param message Partial message object with optional id, sender, timestamp\n */\n public addMessage(message: {\n id?: string;\n text: string;\n sender?: string;\n timestamp?: Date;\n attachments?: IgcMessageAttachment[];\n }): void {\n const newMessage: IgcMessage = {\n id: message.id ?? Date.now().toString(),\n text: message.text,\n sender: message.sender ?? this.currentUserId,\n timestamp: message.timestamp ?? new Date(),\n attachments: message.attachments || [],\n };\n\n const allowed = this.emitEvent('igcMessageCreated', {\n detail: newMessage,\n cancelable: true,\n });\n\n if (allowed) {\n if (!this.messages.some((msg) => msg.id === newMessage.id)) {\n this.messages = [...this.messages, newMessage];\n }\n this.inputValue = '';\n this.inputAttachments = [];\n }\n }\n\n /**\n * Adds files as attachments to the input.\n * Emits 'igcAttachmentChange' event which can be canceled to prevent adding.\n * @param files Array of File objects to attach\n */\n public attachFiles(files: File[]) {\n const newAttachments: IgcMessageAttachment[] = [];\n let count = this.inputAttachments.length;\n files.forEach((file) => {\n if (this.inputAttachments.find((a) => a.name === file.name)) {\n return;\n }\n\n const isImage = file.type.startsWith('image/');\n newAttachments.push({\n id: Date.now().toString() + count++,\n url: URL.createObjectURL(file),\n name: file.name,\n file: file,\n thumbnail: isImage ? URL.createObjectURL(file) : undefined,\n });\n });\n\n const allowed = this.emitEvent('igcAttachmentChange', {\n detail: [...this.inputAttachments, ...newAttachments],\n cancelable: true,\n });\n if (allowed) {\n this.inputAttachments = [...this.inputAttachments, ...newAttachments];\n }\n }\n\n /**\n * Removes an attachment by index.\n * Emits 'igcAttachmentChange' event which can be canceled to prevent removal.\n * @param index Index of the attachment to remove\n */\n public removeAttachment(index: number): void {\n const allowed = this.emitEvent('igcAttachmentChange', {\n detail: this.inputAttachments.filter((_, i) => i !== index),\n cancelable: true,\n });\n if (allowed) {\n this.inputAttachments = this.inputAttachments.filter(\n (_, i) => i !== index\n );\n }\n }\n\n /**\n * Handles when a suggestion is clicked.\n * Adds the suggestion as a new message and focuses the text area.\n * @param suggestion The suggestion string clicked\n */\n public handleSuggestionClick(suggestion: string): void {\n this.addMessage({ text: suggestion });\n if (this.textArea) {\n this.textArea.focus();\n }\n }\n\n /**\n * Updates the chat options partially.\n * @param options Partial options to merge with current options\n */\n public updateOptions(options: Partial<IgcChatOptions>): void {\n this.options = { ...this.options, ...options };\n }\n\n /**\n * Updates the internal cache for accepted file types.\n * Parses the acceptedFiles string option into extensions, mimeTypes, and wildcard types.\n */\n public updateAcceptedTypesCache() {\n if (!this.options?.acceptedFiles) {\n this._acceptedTypesCache = null;\n return;\n }\n\n const types = this.options?.acceptedFiles\n .split(',')\n .map((type) => type.trim().toLowerCase());\n this._acceptedTypesCache = {\n extensions: new Set(types.filter((t) => t.startsWith('.'))),\n mimeTypes: new Set(\n types.filter((t) => !t.startsWith('.') && !t.endsWith('/*'))\n ),\n wildcardTypes: new Set(\n types.filter((t) => t.endsWith('/*')).map((t) => t.slice(0, -2))\n ),\n };\n }\n\n /**\n * Checks if a file's type or extension is accepted by the chat's acceptedFiles setting.\n * @param file File object to check\n * @param type Optional MIME type override if no file provided\n * @returns True if accepted, false otherwise\n */\n public isFileTypeAccepted(file: File, type = ''): boolean {\n if (!this._acceptedTypesCache) return true;\n\n if (file === null && type === '') return false;\n\n const fileType =\n file != null ? file.type.toLowerCase() : type.toLowerCase();\n const fileExtension =\n file != null\n ? `.${file.name.split('.').pop()?.toLowerCase()}`\n : `.${type.split('/').pop()?.toLowerCase()}`;\n\n // Check file extension\n if (this._acceptedTypesCache.extensions.has(fileExtension)) {\n return true;\n }\n\n // Check exact MIME type\n if (this._acceptedTypesCache.mimeTypes.has(fileType)) {\n return true;\n }\n\n // Check wildcard MIME types\n const [fileBaseType] = fileType.split('/');\n return this._acceptedTypesCache.wildcardTypes.has(fileBaseType);\n }\n\n /**\n * Checks if a slot is empty.\n * @param name Slot name to check\n * @returns True if the slot has content, false otherwise\n */\n public hasSlotContent(name: string): boolean {\n return (\n this._host.renderRoot.querySelector<HTMLSlotElement>(`slot[name=${name}]`)\n ?.childNodes.length !== 0\n );\n }\n //#endregion\n}\n\nexport function createChatState(host: IgcChatComponent): ChatState {\n return new ChatState(host);\n}\n"]}
|