@webitel/ui-chats 0.0.3 → 0.0.5
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/package.json +61 -61
- package/src/locale/en/en.ts +8 -8
- package/src/ui/chat-footer/components/chat-footer-wrapper.vue +2 -4
- package/src/ui/chat-footer/modules/user-input/components/actions/attach-files-action.vue +11 -13
- package/src/ui/chat-footer/modules/user-input/components/actions/emoji-picker-action.vue +6 -8
- package/src/ui/chat-footer/modules/user-input/components/actions/send-message-action.vue +6 -8
- package/src/ui/chat-footer/modules/user-input/components/chat-input-actions-bar.vue +37 -39
- package/src/ui/chat-footer/modules/user-input/components/chat-text-field.vue +24 -19
- package/src/ui/chat-footer/modules/user-input/types/ChatAction.types.ts +5 -5
- package/src/ui/index.ts +3 -3
- package/src/ui/messaging/components/chat-date-divider.vue +34 -0
- package/src/ui/messaging/components/scroll-to-bottom-btn.vue +60 -0
- package/src/ui/messaging/components/the-chat-messages-container.vue +89 -0
- package/src/ui/messaging/composebles/useChatScroll.ts +107 -0
- package/src/ui/messaging/modules/message/components/chat-message.vue +51 -57
- package/src/ui/messaging/modules/message/components/details/chat-message-avatar.vue +21 -22
- package/src/ui/messaging/modules/message/components/details/chat-message-blocked-error.vue +1 -1
- package/src/ui/messaging/modules/message/components/details/chat-message-document.vue +22 -19
- package/src/ui/messaging/modules/message/components/details/chat-message-image.vue +8 -8
- package/src/ui/messaging/modules/message/components/details/chat-message-player.vue +14 -12
- package/src/ui/messaging/modules/message/components/details/chat-message-text.vue +12 -12
- package/src/ui/messaging/modules/message/components/details/chat-message-time.vue +11 -9
- package/src/ui/messaging/modules/message/composables/useChatMessage.ts +43 -0
- package/src/ui/messaging/modules/message/composables/useChatMessageFile.ts +26 -26
- package/src/ui/messaging/types/ChatMessage.types.ts +32 -32
- package/src/ui/the-chat-container.vue +118 -0
- package/src/ui/utils/ResultCallbacks.types.ts +1 -1
- package/src/ui/utils/emitter.ts +7 -5
- package/types/locale/en/en.d.ts +1 -1
- package/types/ui/chat-container.vue.d.ts +47 -22
- package/types/ui/chat-footer/modules/user-input/components/chat-input-actions-bar.vue.d.ts +1 -2
- package/types/ui/chat-footer/modules/user-input/components/chat-text-field.vue.d.ts +2 -2
- package/types/ui/chat-input/components/actions/attach-files-action.vue.d.ts +27 -5
- package/types/ui/chat-input/components/actions/emoji-picker-action.vue.d.ts +22 -1
- package/types/ui/chat-input/components/actions/send-message-action.vue.d.ts +27 -5
- package/types/ui/chat-input/components/chat-input-actions-bar.vue.d.ts +34 -12
- package/types/ui/chat-input/components/chat-input-actions-wrapper.vue.d.ts +26 -5
- package/types/ui/chat-input/components/chat-input.vue.d.ts +32 -10
- package/types/ui/chat-input/components/chat-text-field.vue.d.ts +28 -6
- package/types/ui/chat-input/enums/ChatAction.enum.d.ts +3 -3
- package/types/ui/index.d.ts +3 -3
- package/types/ui/messaging/components/chat-date-divider.vue.d.ts +6 -0
- package/types/ui/messaging/components/chat-messages-container.vue.d.ts +24 -3
- package/types/ui/messaging/components/scroll-to-bottom-btn.vue.d.ts +12 -0
- package/types/ui/messaging/components/the-chat-messages-container.vue.d.ts +10 -0
- package/types/ui/messaging/composebles/useChatScroll.d.ts +9 -0
- package/types/ui/messaging/modules/message/components/chat-message.vue.d.ts +4 -4
- package/types/ui/messaging/modules/message/components/details/chat-message-avatar.vue.d.ts +1 -1
- package/types/ui/messaging/modules/message/components/details/chat-message-document.vue.d.ts +1 -1
- package/types/ui/messaging/modules/message/components/details/chat-message-image.vue.d.ts +1 -1
- package/types/ui/messaging/modules/message/components/details/chat-message-player.vue.d.ts +3 -3
- package/types/ui/messaging/modules/message/composables/useChatMessage.d.ts +12 -0
- package/types/ui/messaging/modules/message/composables/useChatMessageFile.d.ts +2 -2
- package/types/ui/messaging/types/ChatMessage.types.d.ts +1 -1
- package/types/ui/the-chat-container.vue.d.ts +33 -0
- package/types/ui/utils/ResultCallbacks.types.d.ts +1 -1
- package/src/ui/chat-container.vue +0 -102
- package/src/ui/messaging/components/chat-messages-container.vue +0 -19
package/package.json
CHANGED
|
@@ -1,63 +1,63 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
2
|
+
"name": "@webitel/ui-chats",
|
|
3
|
+
"version": "0.0.5",
|
|
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:biome || 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:biome": "biome check --write --unsafe",
|
|
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
|
+
"@biomejs/biome": "2.3.8",
|
|
25
|
+
"@types/node": "^24.10.0",
|
|
26
|
+
"@vitejs/plugin-vue": "^6.0.1",
|
|
27
|
+
"@vue/tsconfig": "^0.8.1",
|
|
28
|
+
"@webitel/styleguide": "^24.12.61",
|
|
29
|
+
"@webitel/ui-sdk": "~25.12",
|
|
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
63
|
}
|
package/src/locale/en/en.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export default {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
};
|
|
2
|
+
"@webitel/ui-chats": {
|
|
3
|
+
ui: {
|
|
4
|
+
messaging: {
|
|
5
|
+
chatsFileBlocked: "TODO",
|
|
6
|
+
},
|
|
7
|
+
},
|
|
8
|
+
},
|
|
9
|
+
};
|
|
@@ -17,23 +17,21 @@
|
|
|
17
17
|
</template>
|
|
18
18
|
|
|
19
19
|
<script setup lang="ts">
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import { ChatAction } from '../../types/ChatAction.types';
|
|
20
|
+
import type { ComponentSize } from "@webitel/ui-sdk/enums";
|
|
21
|
+
import { inject, useTemplateRef } from "vue";
|
|
22
|
+
import { ChatAction } from "../../types/ChatAction.types";
|
|
24
23
|
|
|
25
|
-
const
|
|
24
|
+
const _size = inject<ComponentSize>("size");
|
|
26
25
|
|
|
27
|
-
const emit =
|
|
28
|
-
|
|
29
|
-
}>();
|
|
26
|
+
const emit =
|
|
27
|
+
defineEmits<(e: typeof ChatAction.AttachFiles, files: File[]) => void>();
|
|
30
28
|
|
|
31
|
-
const
|
|
29
|
+
const _attachFilesInputRef = useTemplateRef("attachFilesInput");
|
|
32
30
|
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
const _handleAttachmentInputChange = (event: Event) => {
|
|
32
|
+
const files = (event.target as HTMLInputElement).files;
|
|
33
|
+
if (!files) return;
|
|
34
|
+
emit(ChatAction.AttachFiles, Array.from(files) as File[]);
|
|
37
35
|
};
|
|
38
36
|
</script>
|
|
39
37
|
|
|
@@ -6,14 +6,12 @@
|
|
|
6
6
|
</template>
|
|
7
7
|
|
|
8
8
|
<script setup lang="ts">
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import type { Emitter } from 'mitt';
|
|
9
|
+
import type { ComponentSize } from "@webitel/ui-sdk/enums";
|
|
10
|
+
import type { Emitter } from "mitt";
|
|
11
|
+
import { inject } from "vue";
|
|
13
12
|
|
|
14
|
-
import type{ UiChatsEmitterEvents } from
|
|
15
|
-
|
|
16
|
-
const size = inject<ComponentSize>('size');
|
|
17
|
-
const uiChatsEmitter = inject<Emitter<UiChatsEmitterEvents>>('uiChatsEmitter');
|
|
13
|
+
import type { UiChatsEmitterEvents } from "../../../../../utils/emitter";
|
|
18
14
|
|
|
15
|
+
const _size = inject<ComponentSize>("size");
|
|
16
|
+
const _uiChatsEmitter = inject<Emitter<UiChatsEmitterEvents>>("uiChatsEmitter");
|
|
19
17
|
</script>
|
|
@@ -10,14 +10,12 @@
|
|
|
10
10
|
</template>
|
|
11
11
|
|
|
12
12
|
<script setup lang="ts">
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { ChatAction } from '../../types/ChatAction.types';
|
|
13
|
+
import type { ComponentSize } from "@webitel/ui-sdk/enums";
|
|
14
|
+
import { inject } from "vue";
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
import type { ChatAction } from "../../types/ChatAction.types";
|
|
18
17
|
|
|
19
|
-
const
|
|
20
|
-
(e: typeof ChatAction.SendMessage): void;
|
|
21
|
-
}>();
|
|
18
|
+
const _size = inject<ComponentSize>("size");
|
|
22
19
|
|
|
23
|
-
|
|
20
|
+
const _emit = defineEmits<(e: typeof ChatAction.SendMessage) => void>();
|
|
21
|
+
</script>
|
|
@@ -9,60 +9,58 @@
|
|
|
9
9
|
<component
|
|
10
10
|
:is="actionComponent"
|
|
11
11
|
:size="size"
|
|
12
|
-
@
|
|
13
|
-
@
|
|
12
|
+
@send-message="emit(ChatAction.SendMessage)"
|
|
13
|
+
@attach-files="emit(ChatAction.AttachFiles, $event)"
|
|
14
14
|
/>
|
|
15
15
|
</slot>
|
|
16
16
|
</section>
|
|
17
17
|
</template>
|
|
18
18
|
|
|
19
19
|
<script setup lang="ts">
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
20
|
+
import type { ComponentSize } from "@webitel/ui-sdk/enums";
|
|
21
|
+
import { computed, inject } from "vue";
|
|
22
22
|
|
|
23
|
-
import { ChatAction } from
|
|
24
|
-
import
|
|
25
|
-
import
|
|
26
|
-
import
|
|
27
|
-
import { SharedActionSlots } from '../types/ChatAction.types';
|
|
23
|
+
import { ChatAction, type SharedActionSlots } from "../types/ChatAction.types";
|
|
24
|
+
import AttachFilesAction from "./actions/attach-files-action.vue";
|
|
25
|
+
import EmojiPickerAction from "./actions/emoji-picker-action.vue";
|
|
26
|
+
import SendMessageAction from "./actions/send-message-action.vue";
|
|
28
27
|
|
|
29
|
-
const
|
|
28
|
+
const _size = inject<ComponentSize>("size");
|
|
30
29
|
|
|
31
30
|
const props = defineProps<{
|
|
32
|
-
|
|
31
|
+
actions: ChatAction[];
|
|
33
32
|
}>();
|
|
34
33
|
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
const _emit = defineEmits<{
|
|
35
|
+
(e: typeof ChatAction.SendMessage): void;
|
|
36
|
+
(e: typeof ChatAction.AttachFiles, files: File[]): void;
|
|
38
37
|
}>();
|
|
39
38
|
|
|
40
|
-
const
|
|
39
|
+
const _slots = defineSlots<SharedActionSlots>();
|
|
41
40
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
41
|
+
const _ShownActionComponentsList = computed(() => {
|
|
42
|
+
/**
|
|
43
|
+
* note! actions order is declared here and cannot be changed from outside
|
|
44
|
+
*/
|
|
45
|
+
return [
|
|
46
|
+
{
|
|
47
|
+
action: ChatAction.AttachFiles,
|
|
48
|
+
component: AttachFilesAction,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
action: ChatAction.EmojiPicker,
|
|
52
|
+
component: EmojiPickerAction,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
action: ChatAction.QuickReplies,
|
|
56
|
+
component: null, // has no component inside lib, should be provided by pkg-client application
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
action: ChatAction.SendMessage,
|
|
60
|
+
component: SendMessageAction,
|
|
61
|
+
},
|
|
62
|
+
].filter(({ action }) => props.actions.includes(action));
|
|
64
63
|
});
|
|
65
|
-
|
|
66
64
|
</script>
|
|
67
65
|
|
|
68
66
|
<style scoped>
|
|
@@ -74,4 +72,4 @@ const ShownActionComponentsList = computed(() => {
|
|
|
74
72
|
flex: 1;
|
|
75
73
|
}
|
|
76
74
|
}
|
|
77
|
-
</style>
|
|
75
|
+
</style>
|
|
@@ -1,39 +1,44 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<wt-textarea
|
|
3
|
-
v-model="textModel"
|
|
4
3
|
ref="chatTextFieldInput"
|
|
4
|
+
v-model="textModel"
|
|
5
5
|
:size="size"
|
|
6
6
|
autoresize
|
|
7
7
|
/>
|
|
8
8
|
</template>
|
|
9
9
|
|
|
10
10
|
<script setup lang="ts">
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import type
|
|
11
|
+
import type { WtTextarea } from "@webitel/ui-sdk/components";
|
|
12
|
+
import type { ComponentSize } from "@webitel/ui-sdk/enums";
|
|
13
|
+
import insertTextAtCursor from "insert-text-at-cursor";
|
|
14
|
+
import type { Emitter } from "mitt";
|
|
15
|
+
import { computed, inject, type MaybeRef, useTemplateRef } from "vue";
|
|
16
16
|
|
|
17
|
-
import type { UiChatsEmitterEvents } from
|
|
17
|
+
import type { UiChatsEmitterEvents } from "../../../../utils/emitter";
|
|
18
18
|
|
|
19
|
-
const
|
|
19
|
+
const _textModel = defineModel<MaybeRef<string>>("text", {
|
|
20
|
+
required: true,
|
|
21
|
+
});
|
|
20
22
|
|
|
21
|
-
const
|
|
22
|
-
const uiChatsEmitter = inject<Emitter<UiChatsEmitterEvents>>(
|
|
23
|
+
const _size = inject<ComponentSize>("size");
|
|
24
|
+
const uiChatsEmitter = inject<Emitter<UiChatsEmitterEvents>>("uiChatsEmitter");
|
|
23
25
|
|
|
24
|
-
uiChatsEmitter
|
|
25
|
-
uiChatsEmitter
|
|
26
|
+
uiChatsEmitter?.on("insertAtCursor", ({ text }) => insertAtCursor(text));
|
|
27
|
+
uiChatsEmitter?.on("focusOnTextField", focus);
|
|
26
28
|
|
|
27
|
-
const chatTextFieldInputRef =
|
|
29
|
+
const chatTextFieldInputRef =
|
|
30
|
+
useTemplateRef<typeof WtTextarea>("chatTextFieldInput");
|
|
28
31
|
|
|
29
|
-
const textareaEl = computed(() =>
|
|
32
|
+
const textareaEl = computed(() =>
|
|
33
|
+
chatTextFieldInputRef.value?.$el.querySelector("textarea"),
|
|
34
|
+
);
|
|
30
35
|
|
|
31
36
|
function focus() {
|
|
32
|
-
|
|
33
|
-
}
|
|
37
|
+
textareaEl.value?.focus();
|
|
38
|
+
}
|
|
34
39
|
|
|
35
40
|
function insertAtCursor(text: string) {
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
focus();
|
|
42
|
+
insertTextAtCursor(textareaEl.value!, text);
|
|
38
43
|
}
|
|
39
|
-
</script>
|
|
44
|
+
</script>
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
export const ChatAction = {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
SendMessage: "sendMessage",
|
|
3
|
+
AttachFiles: "attachFiles",
|
|
4
|
+
EmojiPicker: "emojiPicker",
|
|
5
|
+
QuickReplies: "quickReplies",
|
|
6
6
|
} as const;
|
|
7
7
|
|
|
8
8
|
export type ChatAction = (typeof ChatAction)[keyof typeof ChatAction];
|
|
9
9
|
|
|
10
10
|
export type SharedActionSlots = {
|
|
11
|
-
|
|
11
|
+
[key in `action:${ChatAction}`]?: () => any;
|
|
12
12
|
};
|
package/src/ui/index.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
1
|
+
export { ChatAction } from "./chat-footer/modules/user-input/types/ChatAction.types";
|
|
2
|
+
export type { ChatMessageType } from "./messaging/types/ChatMessage.types";
|
|
3
|
+
export { default as ChatContainerComponent } from "./the-chat-container.vue";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="chat-date-divider">
|
|
3
|
+
{{ formattedDate }}
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { FormatDateMode } from "@webitel/ui-sdk/enums";
|
|
9
|
+
import { formatDate } from "@webitel/ui-sdk/utils";
|
|
10
|
+
import { computed } from "vue";
|
|
11
|
+
import { useI18n } from "vue-i18n";
|
|
12
|
+
|
|
13
|
+
const props = defineProps<{
|
|
14
|
+
date: number | string;
|
|
15
|
+
}>();
|
|
16
|
+
|
|
17
|
+
const { t } = useI18n();
|
|
18
|
+
|
|
19
|
+
const _formattedDate = computed<Date>(() => {
|
|
20
|
+
const chatDate = formatDate(+props.date, FormatDateMode.DATE);
|
|
21
|
+
const today = formatDate(Date.now(), FormatDateMode.DATE);
|
|
22
|
+
|
|
23
|
+
return chatDate === today ? t("reusable.today") : chatDate;
|
|
24
|
+
});
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<style lang="scss" scoped>
|
|
28
|
+
.chat-date-divider {
|
|
29
|
+
@extend %typo-subtitle-1;
|
|
30
|
+
width: 100%;
|
|
31
|
+
display: flex;
|
|
32
|
+
justify-content: center;
|
|
33
|
+
}
|
|
34
|
+
</style>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="scroll-to-bottom-btn">
|
|
3
|
+
|
|
4
|
+
<div
|
|
5
|
+
v-show="props.newMessageCount"
|
|
6
|
+
class="scroll-to-bottom-btn__messages-counter"
|
|
7
|
+
>
|
|
8
|
+
{{ props.newMessageCount }}
|
|
9
|
+
</div>
|
|
10
|
+
<wt-rounded-action
|
|
11
|
+
icon="arrow-down"
|
|
12
|
+
rounded
|
|
13
|
+
@click="emit('scroll')"
|
|
14
|
+
/>
|
|
15
|
+
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script setup lang="ts">
|
|
20
|
+
import { defineEmits } from "vue";
|
|
21
|
+
|
|
22
|
+
const _props = withDefaults(
|
|
23
|
+
defineProps<{
|
|
24
|
+
newMessageCount?: number;
|
|
25
|
+
}>(),
|
|
26
|
+
{
|
|
27
|
+
newMessageCount: 0,
|
|
28
|
+
},
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const _emit = defineEmits<{
|
|
32
|
+
scroll: [];
|
|
33
|
+
}>();
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<style lang="scss" scoped>
|
|
37
|
+
|
|
38
|
+
.scroll-to-bottom-btn {
|
|
39
|
+
position: absolute;
|
|
40
|
+
bottom: 0;
|
|
41
|
+
right: calc(var(--scrollbar-width) + var(--spacing-2xs));
|
|
42
|
+
display: flex;
|
|
43
|
+
flex-direction: column;
|
|
44
|
+
align-items: center;
|
|
45
|
+
gap: var(--spacing-2xs);
|
|
46
|
+
|
|
47
|
+
&__messages-counter {
|
|
48
|
+
@extend %typo-body-2;
|
|
49
|
+
width: 24px;
|
|
50
|
+
height: 24px;
|
|
51
|
+
display: flex;
|
|
52
|
+
align-items: center;
|
|
53
|
+
justify-content: center;
|
|
54
|
+
background: var(--success-color);
|
|
55
|
+
border-radius: 50%;
|
|
56
|
+
color: var(--text-on-brand-color);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
</style>
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<section class="the-chat-messages-container" @click="focusOnInput">
|
|
3
|
+
<div
|
|
4
|
+
ref="messages-container"
|
|
5
|
+
class="the-chat-messages-container__wrapper"
|
|
6
|
+
@scroll="handleChatScroll"
|
|
7
|
+
@resize="handleChatResize"
|
|
8
|
+
>
|
|
9
|
+
<chat-message-component
|
|
10
|
+
v-for="(message, index) of props.messages"
|
|
11
|
+
:key="message.id"
|
|
12
|
+
:message="message"
|
|
13
|
+
:show-avatar="!props.hideAvatars && showAvatar(index)"
|
|
14
|
+
>
|
|
15
|
+
<template #before-message>
|
|
16
|
+
<chat-date
|
|
17
|
+
v-if="showChatDate(index)"
|
|
18
|
+
:date="message.createdAt"
|
|
19
|
+
/>
|
|
20
|
+
</template>
|
|
21
|
+
</chat-message-component>
|
|
22
|
+
</div>
|
|
23
|
+
<scroll-to-bottom-btn
|
|
24
|
+
v-if="showScrollToBottomBtn"
|
|
25
|
+
:new-message-count="newUnseenMessages"
|
|
26
|
+
@scroll="scrollToBottom('smooth')"
|
|
27
|
+
/>
|
|
28
|
+
</section>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script setup lang="ts">
|
|
32
|
+
import type { Emitter } from "mitt";
|
|
33
|
+
import { inject, useTemplateRef } from "vue";
|
|
34
|
+
|
|
35
|
+
import type { UiChatsEmitterEvents } from "../../utils/emitter";
|
|
36
|
+
import { useChatScroll } from "../composebles/useChatScroll";
|
|
37
|
+
import { useChatMessages } from "../modules/message/composables/useChatMessage";
|
|
38
|
+
import type { ChatMessageType } from "../types/ChatMessage.types";
|
|
39
|
+
|
|
40
|
+
const uiChatsEmitter = inject<Emitter<UiChatsEmitterEvents>>("uiChatsEmitter");
|
|
41
|
+
|
|
42
|
+
const props = withDefaults(
|
|
43
|
+
defineProps<{
|
|
44
|
+
messages: ChatMessageType[];
|
|
45
|
+
hideAvatars?: boolean;
|
|
46
|
+
}>(),
|
|
47
|
+
{
|
|
48
|
+
hideAvatars: false,
|
|
49
|
+
},
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const messagesContainer = useTemplateRef("messages-container");
|
|
53
|
+
|
|
54
|
+
const { showAvatar, showChatDate } = useChatMessages(props.messages);
|
|
55
|
+
|
|
56
|
+
const {
|
|
57
|
+
showScrollToBottomBtn,
|
|
58
|
+
newUnseenMessages,
|
|
59
|
+
scrollToBottom,
|
|
60
|
+
handleChatScroll,
|
|
61
|
+
handleChatResize,
|
|
62
|
+
} = useChatScroll(messagesContainer, props.messages);
|
|
63
|
+
|
|
64
|
+
function _focusOnInput() {
|
|
65
|
+
uiChatsEmitter?.on("focusOnTextField", focus);
|
|
66
|
+
}
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
<style scoped lang="scss">
|
|
70
|
+
.the-chat-messages-container {
|
|
71
|
+
position: relative;
|
|
72
|
+
display: flex;
|
|
73
|
+
overflow: hidden;
|
|
74
|
+
height: inherit;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.the-chat-messages-container__wrapper {
|
|
78
|
+
@extend %wt-scrollbar;
|
|
79
|
+
display: flex;
|
|
80
|
+
flex-direction: column;
|
|
81
|
+
box-sizing: border-box;
|
|
82
|
+
overflow-x: hidden;
|
|
83
|
+
overflow-y: auto;
|
|
84
|
+
width: 100%;
|
|
85
|
+
padding-right: var(--scrollbar-width); // scrollbar offset
|
|
86
|
+
scrollbar-gutter: stable both-edges;
|
|
87
|
+
gap: var(--spacing-xs);
|
|
88
|
+
}
|
|
89
|
+
</style>
|