@webitel/ui-chats 0.0.3 → 0.0.4
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-container.vue +51 -42
- 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 +5 -8
- package/src/ui/chat-footer/modules/user-input/components/chat-input-actions-bar.vue +34 -37
- package/src/ui/chat-footer/modules/user-input/components/chat-text-field.vue +22 -17
- 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-messages-container.vue +4 -4
- package/src/ui/messaging/composebles/useChatScroll.ts +102 -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/useChatMessageFile.ts +26 -26
- package/src/ui/messaging/types/ChatMessage.types.ts +32 -32
- 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 +5 -5
- 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-messages-container.vue.d.ts +1 -1
- 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/useChatMessageFile.d.ts +2 -2
- package/types/ui/messaging/types/ChatMessage.types.d.ts +1 -1
- package/types/ui/utils/ResultCallbacks.types.d.ts +1 -1
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.4",
|
|
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
|
+
};
|
|
@@ -35,63 +35,72 @@
|
|
|
35
35
|
</template>
|
|
36
36
|
|
|
37
37
|
<script setup lang="ts">
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
import
|
|
38
|
+
import { ComponentSize } from "@webitel/ui-sdk/enums";
|
|
39
|
+
import { computed, provide, ref } from "vue";
|
|
40
|
+
import {
|
|
41
|
+
ChatAction,
|
|
42
|
+
type SharedActionSlots,
|
|
43
|
+
} from "./chat-footer/modules/user-input/types/ChatAction.types";
|
|
44
|
+
import type { ChatMessageType } from "./messaging/types/ChatMessage.types";
|
|
45
|
+
import { createUiChatsEmitter } from "./utils/emitter";
|
|
46
|
+
import type { ResultCallbacks } from "./utils/ResultCallbacks.types";
|
|
41
47
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
size: ComponentSize.MD,
|
|
56
|
-
chatActions: () => [ChatAction.SendMessage],
|
|
57
|
-
});
|
|
48
|
+
const props = withDefaults(
|
|
49
|
+
defineProps<{
|
|
50
|
+
messages: ChatMessageType[];
|
|
51
|
+
chatActions?: ChatAction[];
|
|
52
|
+
size?: ComponentSize;
|
|
53
|
+
}>(),
|
|
54
|
+
{
|
|
55
|
+
size: ComponentSize.MD,
|
|
56
|
+
chatActions: () => [
|
|
57
|
+
ChatAction.SendMessage,
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
);
|
|
58
61
|
|
|
59
62
|
const emit = defineEmits<{
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
(
|
|
64
|
+
e: `action:${typeof ChatAction.SendMessage}`,
|
|
65
|
+
text: string,
|
|
66
|
+
options: ResultCallbacks,
|
|
67
|
+
): void;
|
|
68
|
+
(
|
|
69
|
+
e: `action:${typeof ChatAction.AttachFiles}`,
|
|
70
|
+
files: File[],
|
|
71
|
+
options: ResultCallbacks,
|
|
72
|
+
): void;
|
|
62
73
|
}>();
|
|
63
74
|
|
|
64
|
-
const slots = defineSlots<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
75
|
+
const slots = defineSlots<
|
|
76
|
+
{
|
|
77
|
+
main: () => any;
|
|
78
|
+
footer: () => any;
|
|
79
|
+
} & SharedActionSlots
|
|
80
|
+
>();
|
|
68
81
|
|
|
69
82
|
const uiChatsEmitter = createUiChatsEmitter();
|
|
70
83
|
|
|
71
|
-
provide(
|
|
72
|
-
provide(
|
|
84
|
+
provide("size", props.size);
|
|
85
|
+
provide("uiChatsEmitter", uiChatsEmitter);
|
|
73
86
|
|
|
74
|
-
const draft = ref<string>(
|
|
87
|
+
const draft = ref<string>("");
|
|
75
88
|
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
89
|
+
const _slottedChatActions = computed(() => {
|
|
90
|
+
return Object.keys(slots)
|
|
91
|
+
.filter((key) => key.startsWith("action:"))
|
|
92
|
+
.map((key) => key.replace("action:", ""));
|
|
80
93
|
});
|
|
81
94
|
|
|
82
|
-
function
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
onSuccess: () => draft.value = '',
|
|
87
|
-
});
|
|
95
|
+
function _sendMessage() {
|
|
96
|
+
emit(`action:${ChatAction.SendMessage}`, draft.value, {
|
|
97
|
+
onSuccess: () => (draft.value = ""),
|
|
98
|
+
});
|
|
88
99
|
}
|
|
89
100
|
|
|
90
|
-
function
|
|
91
|
-
|
|
92
|
-
});
|
|
101
|
+
function _sendFile(files: File[]) {
|
|
102
|
+
emit(`action:${ChatAction.AttachFiles}`, files, {});
|
|
93
103
|
}
|
|
94
|
-
|
|
95
104
|
</script>
|
|
96
105
|
|
|
97
106
|
<style scoped>
|
|
@@ -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,11 @@
|
|
|
10
10
|
</template>
|
|
11
11
|
|
|
12
12
|
<script setup lang="ts">
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { ChatAction } from
|
|
13
|
+
import type { ComponentSize } from "@webitel/ui-sdk/enums";
|
|
14
|
+
import { inject } from "vue";
|
|
15
|
+
import type { ChatAction } from "../../types/ChatAction.types";
|
|
16
16
|
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
const emit = defineEmits<{
|
|
20
|
-
(e: typeof ChatAction.SendMessage): void;
|
|
21
|
-
}>();
|
|
17
|
+
const _size = inject<ComponentSize>("size");
|
|
22
18
|
|
|
19
|
+
const _emit = defineEmits<(e: typeof ChatAction.SendMessage) => void>();
|
|
23
20
|
</script>
|
|
@@ -17,52 +17,49 @@
|
|
|
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
|
+
import { ChatAction, type SharedActionSlots } from "../types/ChatAction.types";
|
|
23
|
+
import AttachFilesAction from "./actions/attach-files-action.vue";
|
|
24
|
+
import EmojiPickerAction from "./actions/emoji-picker-action.vue";
|
|
25
|
+
import SendMessageAction from "./actions/send-message-action.vue";
|
|
22
26
|
|
|
23
|
-
|
|
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');
|
|
27
|
+
const _size = inject<ComponentSize>("size");
|
|
30
28
|
|
|
31
29
|
const props = defineProps<{
|
|
32
|
-
|
|
30
|
+
actions: ChatAction[];
|
|
33
31
|
}>();
|
|
34
32
|
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
const _emit = defineEmits<{
|
|
34
|
+
(e: typeof ChatAction.SendMessage): void;
|
|
35
|
+
(e: typeof ChatAction.AttachFiles, files: File[]): void;
|
|
38
36
|
}>();
|
|
39
37
|
|
|
40
|
-
const
|
|
38
|
+
const _slots = defineSlots<SharedActionSlots>();
|
|
41
39
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
40
|
+
const _ShownActionComponentsList = computed(() => {
|
|
41
|
+
/**
|
|
42
|
+
* note! actions order is declared here and cannot be changed from outside
|
|
43
|
+
*/
|
|
44
|
+
return [
|
|
45
|
+
{
|
|
46
|
+
action: ChatAction.AttachFiles,
|
|
47
|
+
component: AttachFilesAction,
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
action: ChatAction.EmojiPicker,
|
|
51
|
+
component: EmojiPickerAction,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
action: ChatAction.QuickReplies,
|
|
55
|
+
component: null, // has no component inside lib, should be provided by pkg-client application
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
action: ChatAction.SendMessage,
|
|
59
|
+
component: SendMessageAction,
|
|
60
|
+
},
|
|
61
|
+
].filter(({ action }) => props.actions.includes(action));
|
|
64
62
|
});
|
|
65
|
-
|
|
66
63
|
</script>
|
|
67
64
|
|
|
68
65
|
<style scoped>
|
|
@@ -8,32 +8,37 @@
|
|
|
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
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 { default as ChatContainerComponent } from
|
|
2
|
-
export { ChatAction } from
|
|
3
|
-
export {
|
|
1
|
+
export { default as ChatContainerComponent } from "./chat-container.vue";
|
|
2
|
+
export { ChatAction } from "./chat-footer/modules/user-input/types/ChatAction.types";
|
|
3
|
+
export type { ChatMessageType } from "./messaging/types/ChatMessage.types";
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
</template>
|
|
10
10
|
|
|
11
11
|
<script setup lang="ts">
|
|
12
|
-
import { ChatMessageType } from
|
|
13
|
-
// import ChatMessageComponent from '../modules/message/components/chat-message.vue';
|
|
12
|
+
import type { ChatMessageType } from "../types/ChatMessage.types";
|
|
14
13
|
|
|
14
|
+
// import ChatMessageComponent from '../modules/message/components/chat-message.vue';
|
|
15
15
|
|
|
16
|
-
const
|
|
17
|
-
|
|
16
|
+
const _props = defineProps<{
|
|
17
|
+
messages: ChatMessageType[];
|
|
18
18
|
}>();
|
|
19
19
|
</script>
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { useScroll } from "@vueuse/core";
|
|
2
|
+
import { computed, type Ref, ref, watch } from "vue";
|
|
3
|
+
|
|
4
|
+
import type { ChatMessageType } from "../types/ChatMessage.types";
|
|
5
|
+
|
|
6
|
+
export const useChatScroll = (
|
|
7
|
+
element: Ref<HTMLElement | null> = null,
|
|
8
|
+
chatMessages: ChatMessageType[] = [],
|
|
9
|
+
) => {
|
|
10
|
+
const defaultThreshold = 136;
|
|
11
|
+
const { arrivedState } = useScroll(element.value);
|
|
12
|
+
|
|
13
|
+
const newUnseenMessages = ref<number>(0);
|
|
14
|
+
const showScrollToBottomBtn = ref<boolean>(false);
|
|
15
|
+
/* @author ye.pohranichna
|
|
16
|
+
the distance where the scrollToBottomBtn must be shown/hide.
|
|
17
|
+
why defaultThreshold=136px? because: https://webitel.atlassian.net/browse/WTEL-7136 */
|
|
18
|
+
const threshold = ref<number>(defaultThreshold);
|
|
19
|
+
const messages = ref<ChatMessageType[]>(chatMessages);
|
|
20
|
+
|
|
21
|
+
const lastMessage = computed<ChatMessageType>(
|
|
22
|
+
() => messages.value[messages.value?.length - 1],
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const isLastMessageIsMy = computed<boolean>(() =>
|
|
26
|
+
Boolean(lastMessage.value?.member?.self),
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const scrollToBottom = (behavior: ScrollBehavior = "instant") => {
|
|
30
|
+
element.value?.scrollTo({
|
|
31
|
+
top: element.value?.scrollHeight,
|
|
32
|
+
behavior: behavior === "instant" ? "auto" : behavior,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
newUnseenMessages.value = 0;
|
|
36
|
+
showScrollToBottomBtn.value = false;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const handleShowScrollToBottomBtn = (el: HTMLElement) => {
|
|
40
|
+
if (arrivedState.bottom && newUnseenMessages.value) {
|
|
41
|
+
/* @author ye.pohranichna
|
|
42
|
+
hide the btn and reset new messages count, when we scroll to the bottom without btn */
|
|
43
|
+
newUnseenMessages.value = 0;
|
|
44
|
+
showScrollToBottomBtn.value = false;
|
|
45
|
+
/* @author ye.pohranichna
|
|
46
|
+
quit the function because we are already at the bottom */
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const { scrollTop, scrollHeight, clientHeight } = el;
|
|
51
|
+
const distanceFromBottom = scrollHeight - (scrollTop + clientHeight);
|
|
52
|
+
showScrollToBottomBtn.value = distanceFromBottom > threshold.value;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const updateThreshold = (el: HTMLElement) => {
|
|
56
|
+
/* @author ye.pohranichna
|
|
57
|
+
need to update if clientHeight was changed
|
|
58
|
+
https://webitel.atlassian.net/browse/WTEL-7136 */
|
|
59
|
+
threshold.value = Math.max(defaultThreshold, el.clientHeight * 0.3);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const scrollToBottomAfterNewMessage = () => {
|
|
63
|
+
if (arrivedState.bottom || isLastMessageIsMy.value) {
|
|
64
|
+
scrollToBottom("smooth");
|
|
65
|
+
} else {
|
|
66
|
+
newUnseenMessages.value += 1;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const handleChatScroll = () => {
|
|
70
|
+
const wrapper = element.value;
|
|
71
|
+
if (!wrapper) return;
|
|
72
|
+
|
|
73
|
+
handleShowScrollToBottomBtn(wrapper);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const handleChatResize = () => {
|
|
77
|
+
const wrapper = element.value;
|
|
78
|
+
if (!wrapper) return;
|
|
79
|
+
|
|
80
|
+
updateThreshold(wrapper);
|
|
81
|
+
handleShowScrollToBottomBtn(wrapper);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
watch(
|
|
85
|
+
() => messages.value?.length,
|
|
86
|
+
(newValue, oldValue) => {
|
|
87
|
+
const newMessageReceived = newValue - oldValue === 1; // when chat have just 1 new message @author ye.pohranichna
|
|
88
|
+
if (newMessageReceived) scrollToBottomAfterNewMessage();
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
flush: "post",
|
|
92
|
+
},
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
showScrollToBottomBtn,
|
|
97
|
+
newUnseenMessages,
|
|
98
|
+
scrollToBottom,
|
|
99
|
+
handleChatScroll,
|
|
100
|
+
handleChatResize,
|
|
101
|
+
};
|
|
102
|
+
};
|