@webitel/ui-chats 0.0.14 → 0.0.16

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 (25) hide show
  1. package/package.json +1 -1
  2. package/src/ui/chat-footer/modules/user-input/components/actions/attach-files-action.vue +1 -1
  3. package/src/ui/chat-footer/modules/user-input/components/actions/send-message-action.vue +1 -1
  4. package/src/ui/chat-footer/modules/user-input/components/chat-input-actions-bar.vue +3 -3
  5. package/src/ui/chat-footer/modules/user-input/components/chat-text-field.vue +13 -7
  6. package/src/ui/index.ts +1 -1
  7. package/src/ui/messaging/components/dropzone.vue +70 -0
  8. package/src/ui/messaging/components/the-chat-messages-container.vue +9 -5
  9. package/src/ui/messaging/composables/useDropzoneHandlers.ts +25 -0
  10. package/src/ui/messaging/modules/message/components/chat-message.vue +6 -1
  11. package/src/ui/messaging/modules/message/composables/useChatMessage.ts +4 -2
  12. package/src/ui/the-chat-container.vue +20 -5
  13. package/types/ui/chat-footer/modules/user-input/components/chat-input-actions-bar.vue.d.ts +1 -1
  14. package/types/ui/chat-footer/modules/user-input/enums/ChatAction.enum.d.ts +10 -0
  15. package/types/ui/chat-footer/modules/user-input/types/ChatAction.types.d.ts +5 -5
  16. package/types/ui/index.d.ts +1 -1
  17. package/types/ui/messaging/components/dropzone.vue.d.ts +3 -0
  18. package/types/ui/messaging/components/the-chat-messages-container.vue.d.ts +2 -2
  19. package/types/ui/messaging/composables/useChatScroll.d.ts +9 -0
  20. package/types/ui/messaging/composables/useDropzoneHandlers.d.ts +8 -0
  21. package/types/ui/messaging/composebles/useChatScroll.d.ts +9 -6
  22. package/types/ui/messaging/modules/message/components/chat-message.vue.d.ts +2 -0
  23. package/types/ui/the-chat-container.vue.d.ts +3 -3
  24. /package/src/ui/chat-footer/modules/user-input/{types/ChatAction.types.ts → enums/ChatAction.enum.ts} +0 -0
  25. /package/src/ui/messaging/{composebles → composables}/useChatScroll.ts +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webitel/ui-chats",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "description": "Reusable Webitel Frontend Code for Chats UI",
5
5
  "workspaces": [
6
6
  "../../",
@@ -21,7 +21,7 @@ import { WtRoundedAction } from "@webitel/ui-sdk/components";
21
21
  import { ComponentSize } from "@webitel/ui-sdk/enums";
22
22
  import { inject, useTemplateRef } from "vue";
23
23
 
24
- import { ChatAction } from "../../types/ChatAction.types";
24
+ import { ChatAction } from "../../enums/ChatAction.enum";
25
25
 
26
26
  const size = inject<ComponentSize>("size");
27
27
 
@@ -13,7 +13,7 @@
13
13
  import { ComponentSize } from "@webitel/ui-sdk/enums";
14
14
  import { inject } from "vue";
15
15
 
16
- import { ChatAction } from "../../types/ChatAction.types";
16
+ import { ChatAction } from "../../enums/ChatAction.enum";
17
17
 
18
18
  const size = inject<ComponentSize>("size");
19
19
 
@@ -20,7 +20,7 @@
20
20
  import { ComponentSize } from "@webitel/ui-sdk/enums";
21
21
  import { computed, inject } from "vue";
22
22
 
23
- import { ChatAction, type SharedActionSlots } from "../types/ChatAction.types";
23
+ import { ChatAction, type SharedActionSlots } from "../enums/ChatAction.enum";
24
24
  import AttachFilesAction from "./actions/attach-files-action.vue";
25
25
  import EmojiPickerAction from "./actions/emoji-picker-action.vue";
26
26
  import SendMessageAction from "./actions/send-message-action.vue";
@@ -63,12 +63,12 @@ const ShownActionComponentsList = computed(() => {
63
63
  });
64
64
  </script>
65
65
 
66
- <style scoped>
66
+ <style scoped lang="scss">
67
67
  .chat-input-actions-bar {
68
68
  display: flex;
69
69
  gap: var(--spacing-2xs);
70
70
 
71
- > * {
71
+ & > * {
72
72
  flex: 1;
73
73
  }
74
74
  }
@@ -1,14 +1,15 @@
1
1
  <template>
2
- <wt-textarea
3
- ref="chatTextFieldInput"
4
- v-model="textModel"
5
- :size="size"
6
- autoresize
7
- />
2
+ <wt-textarea
3
+ ref="chatTextFieldInput"
4
+ :model-value="textModel"
5
+ :size="size"
6
+ autoresize
7
+ @update:model-value="send"
8
+ />
8
9
  </template>
9
10
 
10
11
  <script setup lang="ts">
11
- import type { WtTextarea } from "@webitel/ui-sdk/components";
12
+ import { WtTextarea } from "@webitel/ui-sdk/components";
12
13
  import { ComponentSize } from "@webitel/ui-sdk/enums";
13
14
  import insertTextAtCursor from "insert-text-at-cursor";
14
15
  import type { Emitter } from "mitt";
@@ -33,6 +34,10 @@ const textareaEl = computed(() =>
33
34
  chatTextFieldInputRef.value?.$el.querySelector("textarea"),
34
35
  );
35
36
 
37
+ function send(text: string) {
38
+ textModel.value = text;
39
+ }
40
+
36
41
  function focus() {
37
42
  textareaEl.value!.focus();
38
43
  }
@@ -42,3 +47,4 @@ function insertAtCursor(text: string) {
42
47
  insertTextAtCursor(textareaEl.value!, text);
43
48
  }
44
49
  </script>
50
+
package/src/ui/index.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { ChatAction } from "./chat-footer/modules/user-input/types/ChatAction.types";
1
+ export { ChatAction } from "./chat-footer/modules/user-input/enums/ChatAction.enum";
2
2
  export type { ChatMessageType } from "./messaging/types/ChatMessage.types";
3
3
  export { default as ChatContainer } from "./the-chat-container.vue";
@@ -0,0 +1,70 @@
1
+ <template>
2
+ <aside class="dropzone dropzone--animated" v-on="$listeners">
3
+ <div class="dropzone__border-animation"></div>
4
+ <article class="dropzone__text-wrapper">
5
+ <h3 class="dropzone__title">{{ $t('workspaceSec.chat.dropzone.title') }}</h3>
6
+ <p class="dropzone__description">{{ $t('workspaceSec.chat.dropzone.description') }}</p>
7
+ </article>
8
+ </aside>
9
+ </template>
10
+
11
+ <script>
12
+ </script>
13
+
14
+ <style lang="scss" scoped>
15
+ @use '@webitel/styleguide/typography' as *;
16
+
17
+ .dropzone {
18
+ position: absolute;
19
+ top: 0;
20
+ right: 0;
21
+ bottom: 0;
22
+ left: 0;
23
+ display: flex;
24
+ align-items: center;
25
+ justify-content: center;
26
+ background: var(--content-wrapper-color);
27
+ box-shadow: var(--box-shadow);
28
+ border-radius: var(--border-radius);
29
+ z-index: 10;
30
+
31
+ &--animated :before {
32
+ content: '';
33
+ position: absolute;
34
+ top: 0;
35
+ right: 0;
36
+ bottom: 0;
37
+ left: 0;
38
+ background: linear-gradient(90deg, var(--primary-color) 50%, transparent 50%),
39
+ linear-gradient(90deg, var(--primary-color) 50%, transparent 50%),
40
+ linear-gradient(0deg, var(--primary-color) 50%, transparent 50%),
41
+ linear-gradient(0deg, var(--primary-color) 50%, transparent 50%);
42
+ background-repeat: repeat-x, repeat-x, repeat-y, repeat-y;
43
+ background-size: 15px 4px, 15px 4px, 4px 15px, 4px 15px;
44
+ background-position: 0 0, 100% 100%;
45
+ animation: border-dance 6s infinite linear;
46
+ @keyframes border-dance {
47
+ 0% {
48
+ background-position: 0 0, 100% 100%, 0 100%, 100% 0;
49
+ }
50
+ 100% {
51
+ background-position: 100% 0, 0 100%, 0 0, 100% 100%;
52
+ }
53
+ }
54
+ }
55
+ }
56
+
57
+ .dropzone__text-wrapper {
58
+ max-width: 80%;
59
+ }
60
+
61
+ .dropzone__title {
62
+ @extend %typo-heading-2;
63
+ text-align: center;
64
+ }
65
+
66
+ .dropzone__description {
67
+ @extend %typo-body-2;
68
+ text-align: center;
69
+ }
70
+ </style>
@@ -1,5 +1,8 @@
1
1
  <template>
2
- <section class="the-chat-messages-container" @click="focusOnInput">
2
+ <section
3
+ class="the-chat-messages-container"
4
+ @click="focusOnInput"
5
+ >
3
6
  <div
4
7
  ref="messages-container"
5
8
  class="the-chat-messages-container__wrapper"
@@ -10,7 +13,8 @@
10
13
  v-for="(message, index) of props.messages"
11
14
  :key="message.id"
12
15
  :message="message"
13
- :show-avatar="!props.hideAvatars && showAvatar(index)"
16
+ :show-avatar="showAvatar(index)"
17
+ :without-avatars="props.withoutAvatars"
14
18
  >
15
19
  <template #before-message>
16
20
  <chat-date-divider
@@ -33,7 +37,7 @@ import type { Emitter } from "mitt";
33
37
  import { inject, useTemplateRef } from "vue";
34
38
 
35
39
  import type { UiChatsEmitterEvents } from "../../utils/emitter";
36
- import { useChatScroll } from "../composebles/useChatScroll";
40
+ import { useChatScroll } from "../composables/useChatScroll";
37
41
  import ChatMessage from "../modules/message/components/chat-message.vue";
38
42
  import { useChatMessages } from "../modules/message/composables/useChatMessage";
39
43
  import type { ChatMessageType } from "../types/ChatMessage.types";
@@ -45,10 +49,10 @@ const uiChatsEmitter = inject<Emitter<UiChatsEmitterEvents>>("uiChatsEmitter");
45
49
  const props = withDefaults(
46
50
  defineProps<{
47
51
  messages: ChatMessageType[];
48
- hideAvatars?: boolean;
52
+ withoutAvatars?: boolean;
49
53
  }>(),
50
54
  {
51
- hideAvatars: false,
55
+ withoutAvatars: false,
52
56
  },
53
57
  );
54
58
 
@@ -0,0 +1,25 @@
1
+ import { Ref, ref } from "vue";
2
+
3
+ interface UseDropzoneHandlers {
4
+ isDropzoneVisible: Ref<boolean>;
5
+ handleDragEnter: () => void;
6
+ handleDragLeave: () => void;
7
+ }
8
+
9
+ export const useDropzoneHandlers = (): UseDropzoneHandlers => {
10
+ const isDropzoneVisible = ref<boolean>(false);
11
+
12
+ const handleDragEnter = (): void => {
13
+ isDropzoneVisible.value = true;
14
+ };
15
+
16
+ const handleDragLeave = (): void => {
17
+ isDropzoneVisible.value = false;
18
+ };
19
+
20
+ return {
21
+ isDropzoneVisible,
22
+ handleDragEnter,
23
+ handleDragLeave,
24
+ };
25
+ };
@@ -9,7 +9,10 @@
9
9
  <slot name="before-message" />
10
10
 
11
11
  <div class="chat-message__content">
12
- <div class="chat-message__avatar-wrapper">
12
+ <div
13
+ v-if="withoutAvatars"
14
+ class="chat-message__avatar-wrapper"
15
+ >
13
16
  <message-avatar
14
17
  v-if="props.showAvatar"
15
18
  :bot="isBot"
@@ -67,10 +70,12 @@ const props = withDefaults(
67
70
  defineProps<{
68
71
  message: ChatMessageType;
69
72
  showAvatar?: boolean;
73
+ withoutAvatars?: boolean;
70
74
  username?: string;
71
75
  }>(),
72
76
  {
73
77
  showAvatar: false,
78
+ withoutAvatars: false,
74
79
  username: "",
75
80
  },
76
81
  );
@@ -15,10 +15,12 @@ export const useChatMessages = (chatMessages: ChatMessageType[]) => {
15
15
 
16
16
  function showChatDate(index: number): boolean {
17
17
  const { prevMessage, message } = getMessage(index);
18
+ const prevDate = +prevMessage?.createdAt || 0;
19
+ const currentDate = +message?.createdAt || 0;
18
20
  return (
19
21
  !!prevMessage &&
20
- formatDate(+prevMessage?.createdAt, FormatDateMode.DATE) !==
21
- formatDate(+message?.createdAt, FormatDateMode.DATE)
22
+ formatDate(prevDate, FormatDateMode.DATE) !==
23
+ formatDate(currentDate, FormatDateMode.DATE)
22
24
  );
23
25
  }
24
26
 
@@ -4,9 +4,16 @@
4
4
  header goes here
5
5
  </slot> -->
6
6
  <slot name="main">
7
+ <dropzone
8
+ v-if="!isDropzoneDisabled && isDropzoneVisible"
9
+ @dragenter.prevent
10
+ @dragleave.prevent="handleDragLeave"
11
+ @drop="sendFile"
12
+ />
7
13
  <chat-messages-container
8
14
  :messages="props.messages"
9
- :hide-avatars="props.hideAvatars"
15
+ :without-avatars="props.withoutAvatars"
16
+ @[ChatAction.AttachFiles]="sendFile"
10
17
  />
11
18
  </slot>
12
19
  <slot name="footer">
@@ -16,7 +23,7 @@
16
23
  v-model:text="draft"
17
24
  />
18
25
  <chat-input-actions-bar
19
- :actions="chatActions"
26
+ :actions="props.chatActions"
20
27
  @[ChatAction.SendMessage]="sendMessage"
21
28
  @[ChatAction.AttachFiles]="sendFile"
22
29
  >
@@ -47,8 +54,10 @@ import ChatTextField from "./chat-footer/modules/user-input/components/chat-text
47
54
  import {
48
55
  ChatAction,
49
56
  type SharedActionSlots,
50
- } from "./chat-footer/modules/user-input/types/ChatAction.types";
57
+ } from "./chat-footer/modules/user-input/enums/ChatAction.enum";
58
+ import Dropzone from "./messaging/components/dropzone.vue";
51
59
  import ChatMessagesContainer from "./messaging/components/the-chat-messages-container.vue";
60
+ import { useDropzoneHandlers } from "./messaging/composables/useDropzoneHandlers";
52
61
  import type { ChatMessageType } from "./messaging/types/ChatMessage.types";
53
62
  import { createUiChatsEmitter } from "./utils/emitter";
54
63
  import type { ResultCallbacks } from "./utils/ResultCallbacks.types";
@@ -58,11 +67,11 @@ const props = withDefaults(
58
67
  messages: ChatMessageType[];
59
68
  chatActions?: ChatAction[];
60
69
  size?: ComponentSize;
61
- hideAvatars?: boolean;
70
+ withoutAvatars?: boolean;
62
71
  }>(),
63
72
  {
64
73
  size: ComponentSize.MD,
65
- hideAvatars: false,
74
+ withoutAvatars: false,
66
75
  chatActions: () => [
67
76
  ChatAction.SendMessage,
68
77
  ],
@@ -94,6 +103,8 @@ const uiChatsEmitter = createUiChatsEmitter();
94
103
  provide("size", props.size);
95
104
  provide("uiChatsEmitter", uiChatsEmitter);
96
105
 
106
+ const { isDropzoneVisible, handleDragLeave } = useDropzoneHandlers();
107
+
97
108
  const draft = ref<string>("");
98
109
 
99
110
  const slottedChatActions = computed(() => {
@@ -101,6 +112,9 @@ const slottedChatActions = computed(() => {
101
112
  .filter((key) => key.startsWith("action:"))
102
113
  .map((key) => key.replace("action:", ""));
103
114
  });
115
+ const isDropzoneDisabled = computed(
116
+ () => !props.chatActions.includes(ChatAction.AttachFiles),
117
+ );
104
118
 
105
119
  function sendMessage() {
106
120
  emit(`action:${ChatAction.SendMessage}`, draft.value, {
@@ -118,5 +132,6 @@ function sendFile(files: File[]) {
118
132
  display: flex;
119
133
  flex-direction: column;
120
134
  height: 100%;
135
+ width: 100%;
121
136
  }
122
137
  </style>
@@ -1,4 +1,4 @@
1
- import { ChatAction, type SharedActionSlots } from "../types/ChatAction.types";
1
+ import { ChatAction, type SharedActionSlots } from "../enums/ChatAction.enum";
2
2
  type __VLS_Props = {
3
3
  actions: ChatAction[];
4
4
  };
@@ -0,0 +1,10 @@
1
+ export declare const ChatAction: {
2
+ readonly SendMessage: "sendMessage";
3
+ readonly AttachFiles: "attachFiles";
4
+ readonly EmojiPicker: "emojiPicker";
5
+ readonly QuickReplies: "quickReplies";
6
+ };
7
+ export type ChatAction = (typeof ChatAction)[keyof typeof ChatAction];
8
+ export type SharedActionSlots = {
9
+ [key in `action:${ChatAction}`]?: () => any;
10
+ };
@@ -1,10 +1,10 @@
1
1
  export declare const ChatAction: {
2
- readonly SendMessage: "sendMessage";
3
- readonly AttachFiles: "attachFiles";
4
- readonly EmojiPicker: "emojiPicker";
5
- readonly QuickReplies: "quickReplies";
2
+ readonly SendMessage: "sendMessage";
3
+ readonly AttachFiles: "attachFiles";
4
+ readonly EmojiPicker: "emojiPicker";
5
+ readonly QuickReplies: "quickReplies";
6
6
  };
7
7
  export type ChatAction = (typeof ChatAction)[keyof typeof ChatAction];
8
8
  export type SharedActionSlots = {
9
- [key in `action:${ChatAction}`]?: () => any;
9
+ [key in `action:${ChatAction}`]?: () => any;
10
10
  };
@@ -1,3 +1,3 @@
1
- export { ChatAction } from "./chat-footer/modules/user-input/types/ChatAction.types";
1
+ export { ChatAction } from "./chat-footer/modules/user-input/enums/ChatAction.enum";
2
2
  export type { ChatMessageType } from "./messaging/types/ChatMessage.types";
3
3
  export { default as ChatContainer } from "./the-chat-container.vue";
@@ -0,0 +1,3 @@
1
+ declare const _default: typeof __VLS_export;
2
+ export default _default;
3
+ declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -1,10 +1,10 @@
1
1
  import type { ChatMessageType } from "../types/ChatMessage.types";
2
2
  type __VLS_Props = {
3
3
  messages: ChatMessageType[];
4
- hideAvatars?: boolean;
4
+ withoutAvatars?: boolean;
5
5
  };
6
6
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
7
- hideAvatars: boolean;
7
+ withoutAvatars: boolean;
8
8
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
9
  declare const _default: typeof __VLS_export;
10
10
  export default _default;
@@ -0,0 +1,9 @@
1
+ import { type Ref } from "vue";
2
+ import type { ChatMessageType } from "../types/ChatMessage.types";
3
+ export declare const useChatScroll: (element?: Ref<HTMLElement | null>, chatMessages?: ChatMessageType[]) => {
4
+ showScrollToBottomBtn: Ref<boolean, boolean>;
5
+ newUnseenMessages: Ref<number, number>;
6
+ scrollToBottom: (behavior?: ScrollBehavior) => void;
7
+ handleChatScroll: () => void;
8
+ handleChatResize: () => void;
9
+ };
@@ -0,0 +1,8 @@
1
+ import { Ref } from "vue";
2
+ interface UseDropzoneHandlers {
3
+ isDropzoneVisible: Ref<boolean>;
4
+ handleDragEnter: () => void;
5
+ handleDragLeave: () => void;
6
+ }
7
+ export declare const useDropzoneHandlers: () => UseDropzoneHandlers;
8
+ export {};
@@ -1,9 +1,12 @@
1
1
  import { type Ref } from "vue";
2
2
  import type { ChatMessageType } from "../types/ChatMessage.types";
3
- export declare const useChatScroll: (element?: Ref<HTMLElement | null>, chatMessages?: ChatMessageType[]) => {
4
- showScrollToBottomBtn: Ref<boolean, boolean>;
5
- newUnseenMessages: Ref<number, number>;
6
- scrollToBottom: (behavior?: ScrollBehavior) => void;
7
- handleChatScroll: () => void;
8
- handleChatResize: () => void;
3
+ export declare const useChatScroll: (
4
+ element?: Ref<HTMLElement | null>,
5
+ chatMessages?: ChatMessageType[],
6
+ ) => {
7
+ showScrollToBottomBtn: Ref<boolean, boolean>;
8
+ newUnseenMessages: Ref<number, number>;
9
+ scrollToBottom: (behavior?: ScrollBehavior) => void;
10
+ handleChatScroll: () => void;
11
+ handleChatResize: () => void;
9
12
  };
@@ -2,6 +2,7 @@ import type { ChatMessageType } from "../../../types/ChatMessage.types";
2
2
  type __VLS_Props = {
3
3
  message: ChatMessageType;
4
4
  showAvatar?: boolean;
5
+ withoutAvatars?: boolean;
5
6
  username?: string;
6
7
  };
7
8
  declare var __VLS_1: {}, __VLS_44: {};
@@ -19,6 +20,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
19
20
  }>, {
20
21
  username: string;
21
22
  showAvatar: boolean;
23
+ withoutAvatars: boolean;
22
24
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
23
25
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
24
26
  declare const _default: typeof __VLS_export;
@@ -1,12 +1,12 @@
1
1
  import { ComponentSize } from "@webitel/ui-sdk/enums";
2
- import { ChatAction, type SharedActionSlots } from "./chat-footer/modules/user-input/types/ChatAction.types";
2
+ import { ChatAction, type SharedActionSlots } from "./chat-footer/modules/user-input/enums/ChatAction.enum";
3
3
  import type { ChatMessageType } from "./messaging/types/ChatMessage.types";
4
4
  import type { ResultCallbacks } from "./utils/ResultCallbacks.types";
5
5
  type __VLS_Props = {
6
6
  messages: ChatMessageType[];
7
7
  chatActions?: ChatAction[];
8
8
  size?: ComponentSize;
9
- hideAvatars?: boolean;
9
+ withoutAvatars?: boolean;
10
10
  };
11
11
  type __VLS_Slots = {
12
12
  main: () => any;
@@ -20,7 +20,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
20
20
  "onAction:attachFiles"?: (files: File[], options: ResultCallbacks) => any;
21
21
  }>, {
22
22
  size: ComponentSize;
23
- hideAvatars: boolean;
23
+ withoutAvatars: boolean;
24
24
  chatActions: ChatAction[];
25
25
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
26
26
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;