@webitel/ui-chats 0.0.2

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.
Files changed (59) hide show
  1. package/README.md +3 -0
  2. package/package.json +63 -0
  3. package/src/assets/icons/bot.svg +6 -0
  4. package/src/index.ts +0 -0
  5. package/src/locale/en/en.ts +9 -0
  6. package/src/ui/chat-container.vue +102 -0
  7. package/src/ui/chat-footer/components/chat-footer-wrapper.vue +21 -0
  8. package/src/ui/chat-footer/modules/user-input/components/actions/attach-files-action.vue +40 -0
  9. package/src/ui/chat-footer/modules/user-input/components/actions/emoji-picker-action.vue +19 -0
  10. package/src/ui/chat-footer/modules/user-input/components/actions/send-message-action.vue +23 -0
  11. package/src/ui/chat-footer/modules/user-input/components/chat-input-actions-bar.vue +77 -0
  12. package/src/ui/chat-footer/modules/user-input/components/chat-text-field.vue +39 -0
  13. package/src/ui/chat-footer/modules/user-input/types/ChatAction.types.ts +12 -0
  14. package/src/ui/index.ts +2 -0
  15. package/src/ui/messaging/components/chat-messages-container.vue +19 -0
  16. package/src/ui/messaging/modules/message/components/chat-message.vue +155 -0
  17. package/src/ui/messaging/modules/message/components/details/chat-message-avatar.vue +44 -0
  18. package/src/ui/messaging/modules/message/components/details/chat-message-blocked-error.vue +38 -0
  19. package/src/ui/messaging/modules/message/components/details/chat-message-document.vue +93 -0
  20. package/src/ui/messaging/modules/message/components/details/chat-message-image.vue +40 -0
  21. package/src/ui/messaging/modules/message/components/details/chat-message-player.vue +55 -0
  22. package/src/ui/messaging/modules/message/components/details/chat-message-text.vue +46 -0
  23. package/src/ui/messaging/modules/message/components/details/chat-message-time.vue +37 -0
  24. package/src/ui/messaging/modules/message/composables/useChatMessageFile.ts +38 -0
  25. package/src/ui/messaging/types/ChatMessage.types.ts +12 -0
  26. package/src/ui/utils/ResultCallbacks.types.ts +1 -0
  27. package/src/ui/utils/emitter.ts +12 -0
  28. package/types/index.d.ts +1 -0
  29. package/types/locale/en/en.d.ts +10 -0
  30. package/types/ui/chat-container.vue.d.ts +31 -0
  31. package/types/ui/chat-footer/components/chat-footer-wrapper.vue.d.ts +12 -0
  32. package/types/ui/chat-footer/modules/user-input/components/actions/attach-files-action.vue.d.ts +7 -0
  33. package/types/ui/chat-footer/modules/user-input/components/actions/emoji-picker-action.vue.d.ts +3 -0
  34. package/types/ui/chat-footer/modules/user-input/components/actions/send-message-action.vue.d.ts +7 -0
  35. package/types/ui/chat-footer/modules/user-input/components/chat-input-actions-bar.vue.d.ts +21 -0
  36. package/types/ui/chat-footer/modules/user-input/components/chat-text-field.vue.d.ts +11 -0
  37. package/types/ui/chat-footer/modules/user-input/types/ChatAction.types.d.ts +10 -0
  38. package/types/ui/chat-input/components/actions/attach-files-action.vue.d.ts +7 -0
  39. package/types/ui/chat-input/components/actions/emoji-picker-action.vue.d.ts +3 -0
  40. package/types/ui/chat-input/components/actions/send-message-action.vue.d.ts +7 -0
  41. package/types/ui/chat-input/components/chat-input-actions-bar.vue.d.ts +21 -0
  42. package/types/ui/chat-input/components/chat-input-actions-wrapper.vue.d.ts +13 -0
  43. package/types/ui/chat-input/components/chat-input.vue.d.ts +20 -0
  44. package/types/ui/chat-input/components/chat-text-field.vue.d.ts +10 -0
  45. package/types/ui/chat-input/enums/ChatAction.enum.d.ts +6 -0
  46. package/types/ui/index.d.ts +2 -0
  47. package/types/ui/messaging/components/chat-messages-container.vue.d.ts +7 -0
  48. package/types/ui/messaging/modules/message/components/chat-message.vue.d.ts +29 -0
  49. package/types/ui/messaging/modules/message/components/details/chat-message-avatar.vue.d.ts +10 -0
  50. package/types/ui/messaging/modules/message/components/details/chat-message-blocked-error.vue.d.ts +3 -0
  51. package/types/ui/messaging/modules/message/components/details/chat-message-document.vue.d.ts +10 -0
  52. package/types/ui/messaging/modules/message/components/details/chat-message-image.vue.d.ts +11 -0
  53. package/types/ui/messaging/modules/message/components/details/chat-message-player.vue.d.ts +14 -0
  54. package/types/ui/messaging/modules/message/components/details/chat-message-text.vue.d.ts +6 -0
  55. package/types/ui/messaging/modules/message/components/details/chat-message-time.vue.d.ts +8 -0
  56. package/types/ui/messaging/modules/message/composables/useChatMessageFile.d.ts +21 -0
  57. package/types/ui/messaging/types/ChatMessage.types.d.ts +11 -0
  58. package/types/ui/utils/ResultCallbacks.types.d.ts +1 -0
  59. package/types/ui/utils/emitter.d.ts +7 -0
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # @webitel/ui-chats
2
+
3
+ Reusable Webitel Frontend Code for Chats UI
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@webitel/ui-chats",
3
+ "version": "0.0.2",
4
+ "description": "Reusable Webitel Frontend Code for Chats UI",
5
+ "workspaces": [
6
+ "../../",
7
+ "../api-services"
8
+ ],
9
+ "scripts": {
10
+ "dev": "vite",
11
+ "make-all": "npm version patch --git-tag-version false && (npm run lint:oxlint || true) && (npm run build:types || true) && npm run utils:publish",
12
+ "build": "(npm run build:types || true)",
13
+ "build:types": "vue-tsc -p ./tsconfig.build.json",
14
+ "lint:oxlint": "npx oxlint . --fix --type-aware",
15
+ "utils:publish": "npm publish --access public"
16
+ },
17
+ "dependencies": {
18
+ "autolinker": "^4.1.5",
19
+ "insert-text-at-cursor": "^0.3.0",
20
+ "mitt": "^3.0.1",
21
+ "vue": "^3.5.24"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^24.10.0",
25
+ "@vitejs/plugin-vue": "^6.0.1",
26
+ "@vue/tsconfig": "^0.8.1",
27
+ "@webitel/styleguide": "^24.12.61",
28
+ "@webitel/ui-sdk": "~25.12",
29
+ "oxlint-tsgolint": "^0.5.1",
30
+ "typescript": "~5.9.3",
31
+ "vite": "npm:rolldown-vite@7.2.2",
32
+ "vue-tsc": "^3.1.3"
33
+ },
34
+ "type": "module",
35
+ "main": "index.ts",
36
+ "types": "index.d.ts",
37
+ "files": [
38
+ "src/*",
39
+ "types/*",
40
+ "Readme.md",
41
+ "CHANGELOG.md"
42
+ ],
43
+ "exports": {
44
+ "./ui": {
45
+ "types": "./types/ui/index.d.ts",
46
+ "import": "./src/ui/index.ts"
47
+ }
48
+ },
49
+ "author": "webitel",
50
+ "license": "ISC",
51
+ "keywords": [
52
+ "webitel",
53
+ "webitel-ui"
54
+ ],
55
+ "repository": {
56
+ "type": "git",
57
+ "url": "github.com/webitel/webitel-ui-sdk"
58
+ },
59
+ "engines": {
60
+ "npm": ">=10",
61
+ "node": ">=v22"
62
+ }
63
+ }
@@ -0,0 +1,6 @@
1
+ <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill-rule="evenodd" clip-rule="evenodd"
3
+ d="M11.959 2.00018L12.0017 2L12.0445 2.00018L12.1008 2C14.9905 2 17.3597 3.41811 17.5835 5.22036C17.624 5.30176 17.6617 5.38476 17.6967 5.46927C17.8657 5.87713 17.9409 6.34558 17.9744 7.00018L18.0012 7C19.1058 7 20.0012 7.89543 20.0012 9C20.0012 9.45025 19.8524 9.86576 19.6013 10.2C19.8442 10.3825 20.0012 10.6729 20.0012 11V14C20.0012 14.5523 19.5535 15 19.0012 15C18.4489 15 18.0012 14.5523 18.0012 14C18.0012 15.8638 18.0012 16.7956 17.6967 17.5307C17.2908 18.5108 16.5121 19.2895 15.532 19.6955C14.7969 20 13.8651 20 12.0017 20C10.1383 20 9.20648 20 8.47146 19.6955C7.49135 19.2895 6.71266 18.5108 6.30668 17.5307C6.0022 16.7956 6.0022 15.8638 6.0022 14C6.0022 14.5523 5.55448 15 5.0022 15C4.44991 15 4.0022 14.5523 4.0022 14V11C4.0022 10.6729 4.15925 10.3825 4.40207 10.2C4.15098 9.86576 4.0022 9.45025 4.0022 9C4.0022 7.89543 4.89763 7 6.0022 7L6.02906 7.00018C6.06253 6.34558 6.13774 5.87713 6.30668 5.46927C6.34168 5.38476 6.37945 5.30176 6.41989 5.22036C6.64377 3.41811 9.01294 2 11.9026 2L11.959 2.00018ZM12.0017 18C12.9607 18 13.5798 17.9989 14.0537 17.9666C14.5097 17.9355 14.6834 17.8822 14.7666 17.8478C15.2566 17.6448 15.646 17.2554 15.849 16.7654C15.8835 16.6821 15.9367 16.5085 15.9678 16.0525C16.0001 15.5785 16.0012 14.9592 16.0012 14V10C16.0012 10 15.0013 9.00011 12.0017 9C9.00209 9.00011 8.0022 10 8.0022 10V14C8.0022 14.9592 8.00328 15.5785 8.03562 16.0525C8.06673 16.5085 8.11996 16.6821 8.15444 16.7654C8.35743 17.2554 8.74677 17.6448 9.23683 17.8478C9.32006 17.8822 9.49372 17.9355 9.94972 17.9666C10.4236 17.9989 11.0427 18 12.0017 18ZM15.6008 5.5H15.6083V6.5C15.6083 7.05228 15.1606 7.5 14.6083 7.5H9.40112C8.84884 7.5 8.40112 7.05228 8.40112 6.5V5.5H8.40259C8.40259 5.45447 8.43533 5.12731 9.08725 4.71245C9.71545 4.31269 10.7054 4 11.9026 4C11.9358 4 11.9688 4.00024 12.0017 4.00072C12.0346 4.00024 12.0676 4 12.1008 4C13.298 4 14.288 4.31269 14.9162 4.71245C15.5681 5.12731 15.6008 5.45447 15.6008 5.5Z"/>
4
+ <path d="M9.0022 21C9.0022 20.4477 9.44991 20 10.0022 20H10.006C10.5566 20 11.0022 20 11.0022 21C11.0022 21.5523 10.5545 22 10.0022 22C9.5022 22 9.2522 22 9.1272 21.875C9.0022 21.75 9.0022 21.5 9.0022 21Z"/>
5
+ <path d="M14.0012 20C14.5535 20 15.0012 20.4477 15.0012 21C15.0012 21.5 15.0012 21.75 14.8762 21.875C14.7512 22 14.5012 22 14.0012 22C13.4489 22 13.0012 21.5523 13.0012 21C13.0012 20 13.4469 20 13.9974 20H14.0012Z"/>
6
+ </svg>
package/src/index.ts ADDED
File without changes
@@ -0,0 +1,9 @@
1
+ export default {
2
+ '@webitel/ui-chats': {
3
+ ui: {
4
+ messaging: {
5
+ chatsFileBlocked: 'TODO',
6
+ },
7
+ },
8
+ },
9
+ };
@@ -0,0 +1,102 @@
1
+ <template>
2
+ <section class="chat-container">
3
+ <!-- <slot name="header">
4
+ header goes here
5
+ </slot> -->
6
+ <slot name="main">
7
+ <chat-messages-container :messages="messages" />
8
+ </slot>
9
+ <slot name="footer">
10
+ <chat-footer-wrapper>
11
+ <template #default>
12
+ <chat-text-field
13
+ v-model:text="draft"
14
+ />
15
+ <chat-input-actions-bar
16
+ :actions="chatActions"
17
+ @[ChatAction.SendMessage]="sendMessage"
18
+ @[ChatAction.AttachFiles]="sendFile"
19
+ >
20
+ <template
21
+ v-for="action in slottedChatActions"
22
+ :key="action"
23
+ #[action]="{ size }"
24
+ >
25
+ <slot
26
+ :name="`action:${action}`"
27
+ v-bind="{ size }"
28
+ />
29
+ </template>
30
+ </chat-input-actions-bar>
31
+ </template>
32
+ </chat-footer-wrapper>
33
+ </slot>
34
+ </section>
35
+ </template>
36
+
37
+ <script setup lang="ts">
38
+ import { provide, ref, computed } from 'vue';
39
+ import { ComponentSize } from '@webitel/ui-sdk/enums';
40
+ import ChatMessagesContainer from './messaging/components/chat-messages-container.vue';
41
+
42
+ import { ResultCallbacks } from './utils/ResultCallbacks.types';
43
+ import ChatFooterWrapper from './chat-footer/components/chat-footer-wrapper.vue';
44
+ import ChatTextField from './chat-footer/modules/user-input/components/chat-text-field.vue';
45
+ import ChatInputActionsBar from './chat-footer/modules/user-input/components/chat-input-actions-bar.vue';
46
+ import { createUiChatsEmitter } from './utils/emitter';
47
+ import { ChatMessageType } from './messaging/types/ChatMessage.types';
48
+ import { ChatAction, SharedActionSlots } from './chat-footer/modules/user-input/types/ChatAction.types';
49
+
50
+ const props = withDefaults(defineProps<{
51
+ messages: ChatMessageType[];
52
+ chatActions?: ChatAction[];
53
+ size?: ComponentSize;
54
+ }>(), {
55
+ size: ComponentSize.MD,
56
+ chatActions: () => [ChatAction.SendMessage],
57
+ });
58
+
59
+ const emit = defineEmits<{
60
+ (e: `action:${typeof ChatAction.SendMessage}`, text: string, options: ResultCallbacks): void;
61
+ (e: `action:${typeof ChatAction.AttachFiles}`, files: File[], options: ResultCallbacks): void;
62
+ }>();
63
+
64
+ const slots = defineSlots<{
65
+ main: () => any;
66
+ footer: () => any;
67
+ } & SharedActionSlots>();
68
+
69
+ const uiChatsEmitter = createUiChatsEmitter();
70
+
71
+ provide('size', props.size);
72
+ provide('uiChatsEmitter', uiChatsEmitter);
73
+
74
+ const draft = ref<string>('');
75
+
76
+ const slottedChatActions = computed(() => {
77
+ return Object.keys(slots)
78
+ .filter((key) => key.startsWith('action:'))
79
+ .map((key) => key.replace('action:', ''));
80
+ });
81
+
82
+ function sendMessage() {
83
+ emit(`action:${ChatAction.SendMessage}`,
84
+ draft.value,
85
+ {
86
+ onSuccess: () => draft.value = '',
87
+ });
88
+ }
89
+
90
+ function sendFile(files: File[]) {
91
+ emit(`action:${ChatAction.AttachFiles}`, files, {
92
+ });
93
+ }
94
+
95
+ </script>
96
+
97
+ <style scoped>
98
+ .chat-container {
99
+ display: flex;
100
+ flex-direction: column;
101
+ }
102
+ </style>
@@ -0,0 +1,21 @@
1
+ <template>
2
+ <section class="chat-footer-wrapper">
3
+ <slot />
4
+ </section>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+
9
+ const slots = defineSlots<{
10
+ default: () => any;
11
+ }>();
12
+
13
+ </script>
14
+
15
+ <style scoped>
16
+ .chat-footer-wrapper {
17
+ display: flex;
18
+ flex-direction: column;
19
+ gap: var(--spacing-2xs);
20
+ }
21
+ </style>
@@ -0,0 +1,40 @@
1
+ <template>
2
+ <wt-rounded-action
3
+ icon="attach"
4
+ color="secondary"
5
+ :size="size"
6
+ rounded
7
+ wide
8
+ @click="attachFilesInputRef!.click()"
9
+ />
10
+ <input
11
+ ref="attachFilesInput"
12
+ style="display: none;"
13
+ type="file"
14
+ multiple
15
+ @change="handleAttachmentInputChange"
16
+ />
17
+ </template>
18
+
19
+ <script setup lang="ts">
20
+ import { inject, useTemplateRef } from 'vue';
21
+ import { WtRoundedAction } from '@webitel/ui-sdk/components';
22
+ import { ComponentSize } from '@webitel/ui-sdk/enums';
23
+ import { ChatAction } from '../../types/ChatAction.types';
24
+
25
+ const size = inject<ComponentSize>('size');
26
+
27
+ const emit = defineEmits<{
28
+ (e: typeof ChatAction.AttachFiles, files: File[]): void;
29
+ }>();
30
+
31
+ const attachFilesInputRef = useTemplateRef('attachFilesInput');
32
+
33
+ const handleAttachmentInputChange = (event: Event) => {
34
+ const files = (event.target as HTMLInputElement).files;
35
+ if (!files) return;
36
+ emit(ChatAction.AttachFiles, Array.from(files) as File[]);
37
+ };
38
+ </script>
39
+
40
+ <style scoped></style>
@@ -0,0 +1,19 @@
1
+ <template>
2
+ <wt-chat-emoji
3
+ :size="size"
4
+ @insert-emoji="uiChatsEmitter!.emit('insertAtCursor', { text: $event })"
5
+ />
6
+ </template>
7
+
8
+ <script setup lang="ts">
9
+ import { inject } from 'vue';
10
+ import { WtChatEmoji } from '@webitel/ui-sdk/components';
11
+ import { ComponentSize } from '@webitel/ui-sdk/enums';
12
+ import type { Emitter } from 'mitt';
13
+
14
+ import type{ UiChatsEmitterEvents } from '../../../../../utils/emitter';
15
+
16
+ const size = inject<ComponentSize>('size');
17
+ const uiChatsEmitter = inject<Emitter<UiChatsEmitterEvents>>('uiChatsEmitter');
18
+
19
+ </script>
@@ -0,0 +1,23 @@
1
+ <template>
2
+ <wt-rounded-action
3
+ icon="chat-send"
4
+ color="accent"
5
+ :size="size"
6
+ rounded
7
+ wide
8
+ @click="emit(ChatAction.SendMessage)"
9
+ />
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ import { inject } from 'vue';
14
+ import { ComponentSize } from '@webitel/ui-sdk/enums';
15
+ import { ChatAction } from '../../types/ChatAction.types';
16
+
17
+ const size = inject<ComponentSize>('size');
18
+
19
+ const emit = defineEmits<{
20
+ (e: typeof ChatAction.SendMessage): void;
21
+ }>();
22
+
23
+ </script>
@@ -0,0 +1,77 @@
1
+ <template>
2
+ <section class="chat-input-actions-bar">
3
+ <slot
4
+ v-for="{ action, component: actionComponent } in ShownActionComponentsList"
5
+ :key="action"
6
+ :name="action"
7
+ v-bind="{ size }"
8
+ >
9
+ <component
10
+ :is="actionComponent"
11
+ :size="size"
12
+ @sendMessage="emit(ChatAction.SendMessage)"
13
+ @attachFiles="emit(ChatAction.AttachFiles, $event)"
14
+ />
15
+ </slot>
16
+ </section>
17
+ </template>
18
+
19
+ <script setup lang="ts">
20
+ import { computed, inject } from 'vue';
21
+ import { ComponentSize } from '@webitel/ui-sdk/enums';
22
+
23
+ import { ChatAction } from '../types/ChatAction.types';
24
+ import SendMessageAction from './actions/send-message-action.vue';
25
+ import AttachFilesAction from './actions/attach-files-action.vue';
26
+ import EmojiPickerAction from './actions/emoji-picker-action.vue';
27
+ import { SharedActionSlots } from '../types/ChatAction.types';
28
+
29
+ const size = inject<ComponentSize>('size');
30
+
31
+ const props = defineProps<{
32
+ actions: ChatAction[];
33
+ }>();
34
+
35
+ const emit = defineEmits<{
36
+ (e: typeof ChatAction.SendMessage): void;
37
+ (e: typeof ChatAction.AttachFiles, files: File[]): void;
38
+ }>();
39
+
40
+ const slots = defineSlots<SharedActionSlots>();
41
+
42
+ const ShownActionComponentsList = computed(() => {
43
+ /**
44
+ * note! actions order is declared here and cannot be changed from outside
45
+ */
46
+ return [
47
+ {
48
+ action: ChatAction.AttachFiles,
49
+ component: AttachFilesAction,
50
+ },
51
+ {
52
+ action: ChatAction.EmojiPicker,
53
+ component: EmojiPickerAction,
54
+ },
55
+ {
56
+ action: ChatAction.QuickReplies,
57
+ component: null, // has no component inside lib, should be provided by pkg-client application
58
+ },
59
+ {
60
+ action: ChatAction.SendMessage,
61
+ component: SendMessageAction,
62
+ },
63
+ ].filter(({ action }) => props.actions.includes(action));
64
+ });
65
+
66
+ </script>
67
+
68
+ <style scoped>
69
+ .chat-input-actions-bar {
70
+ display: flex;
71
+ gap: var(--spacing-2xs);
72
+
73
+ > * {
74
+ flex: 1;
75
+ }
76
+ }
77
+ </style>
@@ -0,0 +1,39 @@
1
+ <template>
2
+ <wt-textarea
3
+ v-model="textModel"
4
+ ref="chatTextFieldInput"
5
+ :size="size"
6
+ autoresize
7
+ />
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import { computed, inject, useTemplateRef, MaybeRef } from 'vue';
12
+ import { WtTextarea } from '@webitel/ui-sdk/components';
13
+ import { ComponentSize } from '@webitel/ui-sdk/enums';
14
+ import insertTextAtCursor from 'insert-text-at-cursor';
15
+ import type { Emitter } from 'mitt';
16
+
17
+ import type { UiChatsEmitterEvents } from '../../../../utils/emitter';
18
+
19
+ const textModel = defineModel<MaybeRef<string>>('text', { required: true });
20
+
21
+ const size = inject<ComponentSize>('size');
22
+ const uiChatsEmitter = inject<Emitter<UiChatsEmitterEvents>>('uiChatsEmitter');
23
+
24
+ uiChatsEmitter!.on('insertAtCursor', ({ text }) => insertAtCursor(text));
25
+ uiChatsEmitter!.on('focusOnTextField', focus);
26
+
27
+ const chatTextFieldInputRef = useTemplateRef<typeof WtTextarea>('chatTextFieldInput');
28
+
29
+ const textareaEl = computed(() => chatTextFieldInputRef.value?.$el.querySelector('textarea'));
30
+
31
+ function focus() {
32
+ textareaEl.value!.focus();
33
+ };
34
+
35
+ function insertAtCursor(text: string) {
36
+ focus();
37
+ insertTextAtCursor(textareaEl.value!, text);
38
+ }
39
+ </script>
@@ -0,0 +1,12 @@
1
+ export const ChatAction = {
2
+ SendMessage: 'sendMessage',
3
+ AttachFiles: 'attachFiles',
4
+ EmojiPicker: 'emojiPicker',
5
+ QuickReplies: 'quickReplies',
6
+ } as const;
7
+
8
+ export type ChatAction = (typeof ChatAction)[keyof typeof ChatAction];
9
+
10
+ export type SharedActionSlots = {
11
+ [key in `action:${ChatAction}`]?: () => any;
12
+ };
@@ -0,0 +1,2 @@
1
+ export { default as ChatContainerComponent } from './chat-container.vue';
2
+ export { ChatAction } from './chat-footer/modules/user-input/types/ChatAction.types';
@@ -0,0 +1,19 @@
1
+ <template>
2
+ <section class="chat-messages-container">
3
+ <!-- <chat-message-component
4
+ v-for="message in messages"
5
+ :key="message.id"
6
+ :message="message"
7
+ /> -->
8
+ </section>
9
+ </template>
10
+
11
+ <script setup lang="ts">
12
+ import { ChatMessageType } from '../types/ChatMessage.types';
13
+ // import ChatMessageComponent from '../modules/message/components/chat-message.vue';
14
+
15
+
16
+ const props = defineProps<{
17
+ messages: ChatMessageType[];
18
+ }>();
19
+ </script>
@@ -0,0 +1,155 @@
1
+ <template>
2
+ <div
3
+ :class="{
4
+ 'chat-message--right' : isSelfSide,
5
+ 'chat-message--md': size === ComponentSize.MD
6
+ }"
7
+ class="chat-message"
8
+ >
9
+ <slot name="before-message" />
10
+
11
+ <div class="chat-message__content">
12
+ <div class="chat-message__avatar-wrapper">
13
+ <message-avatar
14
+ v-if="props.showAvatar"
15
+ :bot="isBot"
16
+ :username="getClientUsername"
17
+ />
18
+ </div>
19
+ <!-- click.stop prevents focus on textarea and allows to select the message-new text -->
20
+ <message-blocked-error v-if="message.file?.malware" @click.stop />
21
+ <div v-else @click.stop>
22
+ <message-player
23
+ v-if="media"
24
+ :file="media"
25
+ :type="media.mime"
26
+ :size="size"
27
+ @initialized="handlePlayerInitialize"
28
+ />
29
+ <message-image
30
+ v-else-if="image"
31
+ :file="image"
32
+ @open="emit('open-image')"
33
+ />
34
+ <message-document
35
+ v-else-if="document"
36
+ :file="document"
37
+ />
38
+ <message-text
39
+ v-if="props.message?.text"
40
+ :text="props.message.text"
41
+ />
42
+ </div>
43
+ <message-time
44
+ :date="props.message.createdAt"
45
+ />
46
+ </div>
47
+
48
+ <slot name="after-message" />
49
+ </div>
50
+ </template>
51
+
52
+ <script setup lang="ts">
53
+
54
+ import { ComponentSize } from '@webitel/ui-sdk/enums';
55
+ import {computed, defineEmits, defineProps, inject} from 'vue';
56
+
57
+ import { useChatMessageFile } from '../composables/useChatMessageFile'
58
+ import MessageAvatar from './details/chat-message-avatar.vue';
59
+ import MessageBlockedError from './details/chat-message-blocked-error.vue';
60
+ import MessageDocument from './details/chat-message-document.vue';
61
+ import MessageImage from './details/chat-message-image.vue';
62
+ import MessagePlayer from './details/chat-message-player.vue';
63
+ import MessageText from './details/chat-message-text.vue';
64
+ import MessageTime from './details/chat-message-time.vue';
65
+
66
+ const props = withDefaults(defineProps<{
67
+ message: object,
68
+ showAvatar?: boolean,
69
+ username?: string,
70
+ }>(), {
71
+ showAvatar: false,
72
+ username: '',
73
+ });
74
+
75
+ const emit = defineEmits<{
76
+ 'open-image': [],
77
+ 'initialized-player': [object]
78
+ }>();
79
+
80
+ const size = inject<ComponentSize>('size');
81
+
82
+ const {
83
+ image,
84
+ media,
85
+ document,
86
+ } = useChatMessageFile(props.message.file);
87
+
88
+ const isSelfMessage = computed(() =>
89
+ props.message.member?.self
90
+ || props.message.member?.type === 'webitel'
91
+ );
92
+
93
+ const isBot = computed(() =>
94
+ props.message.member?.type === 'bot'
95
+ || (!props.message.member?.type && !props.message.channelId)
96
+ );
97
+
98
+ const isSelfSide = computed(() => isSelfMessage.value || isBot.value);
99
+
100
+ const getClientUsername = computed(() => {
101
+ return !isSelfSide.value ? props.username : ''; // need to show username avatar only for client
102
+ });
103
+
104
+ function handlePlayerInitialize(player) {
105
+ emit('initialized-player', { player });
106
+ };
107
+
108
+ </script>
109
+
110
+ <style scoped>
111
+
112
+ .chat-message {
113
+ position: relative;
114
+ display: flex;
115
+ flex-direction: column;
116
+ max-width: 100%;
117
+ gap: var(--spacing-2xs);
118
+ }
119
+
120
+ .chat-message:last-child {
121
+ margin-bottom: var(--spacing-xs);
122
+ }
123
+
124
+ .chat-message__content {
125
+ display: flex;
126
+ flex: 1;
127
+ min-width: 0;
128
+ /** prevents height difference from its content */
129
+ line-height: 0;
130
+ gap: var(--spacing-xs);
131
+ margin: 0 var(--spacing-md) 0 var(--spacing-2xs);
132
+ }
133
+
134
+ .chat-message__avatar-wrapper {
135
+ flex: 0 0 var(--spacing-lg);
136
+ width: var(--wt-avatar-size--size-sm);
137
+ }
138
+
139
+ .chat-message-avatar {
140
+ width: 100%;
141
+ flex: 0 0 var(--icon-lg-size);
142
+ }
143
+
144
+ .chat-message--right .chat-message__content {
145
+ flex-direction: row-reverse;
146
+ margin: 0 var(--spacing-2xs) 0 var(--spacing-md);
147
+ }
148
+
149
+ .chat-message--right .chat-message-text {
150
+ background: var(--secondary-light-color);
151
+ color: var(--secondary-on-color);
152
+ place-self: flex-end;
153
+ }
154
+
155
+ </style>
@@ -0,0 +1,44 @@
1
+ <template>
2
+ <div
3
+ class="chat-message-avatar"
4
+ >
5
+ <wt-avatar
6
+ :username="props.username"
7
+ :size="size"
8
+ :src="src"
9
+ />
10
+ </div>
11
+ </template>
12
+
13
+ <script setup lang="ts">
14
+ import { WtAvatar } from '@webitel/ui-sdk/components';
15
+ import { ComponentSize } from '@webitel/ui-sdk/enums';
16
+ import { computed, defineProps, inject } from 'vue';
17
+
18
+ import botIcon from '../../../../../../assets/icons/bot.svg'
19
+
20
+ const props = withDefaults(defineProps<{
21
+ bot?: boolean,
22
+ username?: string,
23
+ }>(), {
24
+ bot: false,
25
+ username: '',
26
+ });
27
+
28
+ const injectedSize = inject<ComponentSize>('size');
29
+
30
+ const size = computed(() => {
31
+ return ComponentSize.SM || injectedSize; // todo: should injected size be considered?
32
+ });
33
+
34
+ const src = computed(() =>
35
+ props.bot && botIcon
36
+ )
37
+
38
+ </script>
39
+
40
+ <style lang="scss" scoped>
41
+ .chat-message-avatar {
42
+ pointer-events: none; // prevents dragging to upload file area
43
+ }
44
+ </style>