@uzum-tech/ui 1.10.0 → 1.11.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.
Files changed (91) hide show
  1. package/dist/index.js +1269 -207
  2. package/dist/index.prod.js +3 -3
  3. package/es/_internal/index.d.ts +2 -0
  4. package/es/_internal/index.js +1 -0
  5. package/es/_internal/safe-top-scrollbar/index.d.ts +2 -0
  6. package/es/_internal/safe-top-scrollbar/index.js +1 -0
  7. package/es/_internal/safe-top-scrollbar/src/SafeTopScrollbar.d.ts +356 -0
  8. package/es/_internal/safe-top-scrollbar/src/SafeTopScrollbar.js +708 -0
  9. package/es/_internal/safe-top-scrollbar/src/styles/index.cssr.d.ts +2 -0
  10. package/es/_internal/safe-top-scrollbar/src/styles/index.cssr.js +80 -0
  11. package/es/_internal/safe-top-scrollbar/src/styles/rtl.cssr.d.ts +2 -0
  12. package/es/_internal/safe-top-scrollbar/src/styles/rtl.cssr.js +10 -0
  13. package/es/_internal/safe-top-scrollbar/styles/common.d.ts +7 -0
  14. package/es/_internal/safe-top-scrollbar/styles/common.js +7 -0
  15. package/es/_internal/safe-top-scrollbar/styles/dark.d.ts +3 -0
  16. package/es/_internal/safe-top-scrollbar/styles/dark.js +8 -0
  17. package/es/_internal/safe-top-scrollbar/styles/index.d.ts +4 -0
  18. package/es/_internal/safe-top-scrollbar/styles/index.js +3 -0
  19. package/es/_internal/safe-top-scrollbar/styles/light.d.ts +18 -0
  20. package/es/_internal/safe-top-scrollbar/styles/light.js +12 -0
  21. package/es/_internal/safe-top-scrollbar/styles/rtl.d.ts +3 -0
  22. package/es/_internal/safe-top-scrollbar/styles/rtl.js +6 -0
  23. package/es/chat/src/Chat.d.ts +14 -1
  24. package/es/chat/src/Chat.js +5 -0
  25. package/es/chat/src/ChatListItems.js +16 -1
  26. package/es/chat/src/ChatParts/ChatAttachment.js +31 -3
  27. package/es/chat/src/ChatParts/MainArea.js +68 -62
  28. package/es/chat/src/interface.d.ts +6 -2
  29. package/es/components.d.ts +1 -0
  30. package/es/components.js +1 -0
  31. package/es/config-provider/src/internal-interface.d.ts +2 -0
  32. package/es/drawer/src/Drawer.d.ts +18 -0
  33. package/es/drawer/src/Drawer.js +28 -26
  34. package/es/drawer/src/DrawerBodyWrapper.d.ts +8 -0
  35. package/es/drawer/src/DrawerBodyWrapper.js +36 -8
  36. package/es/infinite-scroll/src/InfiniteScroll.d.ts +16 -0
  37. package/es/infinite-scroll/src/InfiniteScroll.js +48 -12
  38. package/es/safe-top-scrollbar/index.d.ts +2 -0
  39. package/es/safe-top-scrollbar/index.js +1 -0
  40. package/es/safe-top-scrollbar/src/SafeTopScrollbar.d.ts +177 -0
  41. package/es/safe-top-scrollbar/src/SafeTopScrollbar.js +38 -0
  42. package/es/themes/dark.js +2 -0
  43. package/es/themes/light.js +2 -0
  44. package/es/version.d.ts +1 -1
  45. package/es/version.js +1 -1
  46. package/lib/_internal/index.d.ts +2 -0
  47. package/lib/_internal/index.js +4 -1
  48. package/lib/_internal/safe-top-scrollbar/index.d.ts +2 -0
  49. package/lib/_internal/safe-top-scrollbar/index.js +9 -0
  50. package/lib/_internal/safe-top-scrollbar/src/SafeTopScrollbar.d.ts +356 -0
  51. package/lib/_internal/safe-top-scrollbar/src/SafeTopScrollbar.js +714 -0
  52. package/lib/_internal/safe-top-scrollbar/src/styles/index.cssr.d.ts +2 -0
  53. package/lib/_internal/safe-top-scrollbar/src/styles/index.cssr.js +85 -0
  54. package/lib/_internal/safe-top-scrollbar/src/styles/rtl.cssr.d.ts +2 -0
  55. package/lib/_internal/safe-top-scrollbar/src/styles/rtl.cssr.js +15 -0
  56. package/lib/_internal/safe-top-scrollbar/styles/common.d.ts +7 -0
  57. package/lib/_internal/safe-top-scrollbar/styles/common.js +10 -0
  58. package/lib/_internal/safe-top-scrollbar/styles/dark.d.ts +3 -0
  59. package/lib/_internal/safe-top-scrollbar/styles/dark.js +10 -0
  60. package/lib/_internal/safe-top-scrollbar/styles/index.d.ts +4 -0
  61. package/lib/_internal/safe-top-scrollbar/styles/index.js +12 -0
  62. package/lib/_internal/safe-top-scrollbar/styles/light.d.ts +18 -0
  63. package/lib/_internal/safe-top-scrollbar/styles/light.js +16 -0
  64. package/lib/_internal/safe-top-scrollbar/styles/rtl.d.ts +3 -0
  65. package/lib/_internal/safe-top-scrollbar/styles/rtl.js +12 -0
  66. package/lib/chat/src/Chat.d.ts +14 -1
  67. package/lib/chat/src/Chat.js +5 -0
  68. package/lib/chat/src/ChatListItems.js +16 -1
  69. package/lib/chat/src/ChatParts/ChatAttachment.js +30 -2
  70. package/lib/chat/src/ChatParts/MainArea.js +68 -62
  71. package/lib/chat/src/interface.d.ts +6 -2
  72. package/lib/components.d.ts +1 -0
  73. package/lib/components.js +1 -0
  74. package/lib/config-provider/src/internal-interface.d.ts +2 -0
  75. package/lib/drawer/src/Drawer.d.ts +18 -0
  76. package/lib/drawer/src/Drawer.js +28 -26
  77. package/lib/drawer/src/DrawerBodyWrapper.d.ts +8 -0
  78. package/lib/drawer/src/DrawerBodyWrapper.js +36 -8
  79. package/lib/infinite-scroll/src/InfiniteScroll.d.ts +16 -0
  80. package/lib/infinite-scroll/src/InfiniteScroll.js +47 -11
  81. package/lib/safe-top-scrollbar/index.d.ts +2 -0
  82. package/lib/safe-top-scrollbar/index.js +9 -0
  83. package/lib/safe-top-scrollbar/src/SafeTopScrollbar.d.ts +177 -0
  84. package/lib/safe-top-scrollbar/src/SafeTopScrollbar.js +41 -0
  85. package/lib/themes/dark.js +5 -0
  86. package/lib/themes/light.js +5 -0
  87. package/lib/version.d.ts +1 -1
  88. package/lib/version.js +1 -1
  89. package/package.json +6 -6
  90. package/volar.d.ts +1 -0
  91. package/web-types.json +67 -2
@@ -0,0 +1,2 @@
1
+ declare const _default: import("css-render").CNode;
2
+ export default _default;
@@ -0,0 +1,80 @@
1
+ import { fadeInTransition } from '../../../../_styles/transitions/fade-in.cssr';
2
+ import { c, cB, cE, cM } from '../../../../_utils/cssr';
3
+ // vars:
4
+ // --u-scrollbar-bezier
5
+ // --u-scrollbar-color
6
+ // --u-scrollbar-color-hover
7
+ // --u-scrollbar-width
8
+ // --u-scrollbar-height
9
+ // --u-scrollbar-border-radius
10
+ // --u-scrollbar-rail-inset-horizontal
11
+ // --u-scrollbar-rail-inset-vertical
12
+ // --u-scrollbar-rail-color
13
+ export default cB('safe-top-scrollbar', `
14
+ overflow: hidden;
15
+ position: relative;
16
+ z-index: auto;
17
+ height: 100%;
18
+ width: 100%;
19
+ `, [c('>', [cB('scrollbar-container', `
20
+ width: 100%;
21
+ overflow: scroll;
22
+ height: 100%;
23
+ min-height: inherit;
24
+ max-height: inherit;
25
+ scrollbar-width: none;
26
+ `, [c('&::-webkit-scrollbar, &::-webkit-scrollbar-track-piece, &::-webkit-scrollbar-thumb', `
27
+ width: 0;
28
+ height: 0;
29
+ display: none;
30
+ `), c('>', [
31
+ // We can't set overflow hidden since it affects positioning.
32
+ cB('scrollbar-content', `
33
+ box-sizing: border-box;
34
+ min-width: 100%;
35
+ `)])])]), c('>, +', [cB('scrollbar-rail', `
36
+ position: absolute;
37
+ pointer-events: none;
38
+ user-select: none;
39
+ background: var(--u-scrollbar-rail-color);
40
+ -webkit-user-select: none;
41
+ `, [cM('horizontal', `
42
+ height: var(--u-scrollbar-height);
43
+ `, [c('>', [cE('scrollbar', `
44
+ height: var(--u-scrollbar-height);
45
+ border-radius: var(--u-scrollbar-border-radius);
46
+ right: 0;
47
+ `)])]), cM('horizontal--top', `
48
+ top: var(--u-scrollbar-rail-top-horizontal-top);
49
+ right: var(--u-scrollbar-rail-right-horizontal-top);
50
+ bottom: var(--u-scrollbar-rail-bottom-horizontal-top);
51
+ left: var(--u-scrollbar-rail-left-horizontal-top);
52
+ `), cM('horizontal--bottom', `
53
+ top: var(--u-scrollbar-rail-top-horizontal-bottom);
54
+ right: var(--u-scrollbar-rail-right-horizontal-bottom);
55
+ bottom: var(--u-scrollbar-rail-bottom-horizontal-bottom);
56
+ left: var(--u-scrollbar-rail-left-horizontal-bottom);
57
+ `), cM('vertical', `
58
+ width: var(--u-scrollbar-width);
59
+ `, [c('>', [cE('scrollbar', `
60
+ width: var(--u-scrollbar-width);
61
+ border-radius: var(--u-scrollbar-border-radius);
62
+ bottom: 0;
63
+ `)])]), cM('vertical--left', `
64
+ top: var(--u-scrollbar-rail-top-vertical-left);
65
+ right: var(--u-scrollbar-rail-right-vertical-left);
66
+ bottom: var(--u-scrollbar-rail-bottom-vertical-left);
67
+ left: var(--u-scrollbar-rail-left-vertical-left);
68
+ `), cM('vertical--right', `
69
+ top: var(--u-scrollbar-rail-top-vertical-right);
70
+ right: var(--u-scrollbar-rail-right-vertical-right);
71
+ bottom: var(--u-scrollbar-rail-bottom-vertical-right);
72
+ left: var(--u-scrollbar-rail-left-vertical-right);
73
+ `), cM('disabled', [c('>', [cE('scrollbar', 'pointer-events: none;')])]), c('>', [cE('scrollbar', `
74
+ z-index: 1;
75
+ position: absolute;
76
+ cursor: pointer;
77
+ pointer-events: all;
78
+ background-color: var(--u-scrollbar-color);
79
+ transition: background-color .2s var(--u-scrollbar-bezier);
80
+ `, [fadeInTransition(), c('&:hover', 'background-color: var(--u-scrollbar-color-hover);')])])])])]);
@@ -0,0 +1,2 @@
1
+ declare const _default: import("css-render").CNode;
2
+ export default _default;
@@ -0,0 +1,10 @@
1
+ import { cB, c, cM, cE } from '../../../../_utils/cssr';
2
+ export default cB('scrollbar', [cM('rtl', `
3
+ direction: rtl;
4
+ `, [c('>', [cB('scrollbar-rail', [cM('horizontal', [c('>', [cE('scrollbar', `
5
+ left: 0;
6
+ right: unset;
7
+ `)])]), cM('vertical', `
8
+ left: 4px;
9
+ right: unset;
10
+ `)])])])]);
@@ -0,0 +1,7 @@
1
+ export declare const commonVars: {
2
+ railInsetHorizontalBottom: string;
3
+ railInsetHorizontalTop: string;
4
+ railInsetVerticalRight: string;
5
+ railInsetVerticalLeft: string;
6
+ railColor: string;
7
+ };
@@ -0,0 +1,7 @@
1
+ export const commonVars = {
2
+ railInsetHorizontalBottom: 'auto 2px 4px 2px',
3
+ railInsetHorizontalTop: '4px 2px auto 2px',
4
+ railInsetVerticalRight: '2px 4px 2px auto',
5
+ railInsetVerticalLeft: '2px auto 2px 4px',
6
+ railColor: 'transparent'
7
+ };
@@ -0,0 +1,3 @@
1
+ import type { SafeTopScrollbarTheme } from './light';
2
+ declare const safeTopScrollbarDark: SafeTopScrollbarTheme;
3
+ export default safeTopScrollbarDark;
@@ -0,0 +1,8 @@
1
+ import { commonDark } from '../../../_styles/common';
2
+ import { self } from './light';
3
+ const safeTopScrollbarDark = {
4
+ name: 'SafeTopScrollbar',
5
+ common: commonDark,
6
+ self
7
+ };
8
+ export default safeTopScrollbarDark;
@@ -0,0 +1,4 @@
1
+ export { default as scrollbarDark } from './dark';
2
+ export { default as scrollbarLight } from './light';
3
+ export { default as scrollbarRtl } from './rtl';
4
+ export type { SafeTopScrollbarTheme, SafeTopScrollbarThemeVars } from './light';
@@ -0,0 +1,3 @@
1
+ export { default as scrollbarDark } from './dark';
2
+ export { default as scrollbarLight } from './light';
3
+ export { default as scrollbarRtl } from './rtl';
@@ -0,0 +1,18 @@
1
+ import type { ThemeCommonVars } from '../../../_styles/common';
2
+ import type { Theme } from '../../../_mixins';
3
+ export declare const self: (vars: ThemeCommonVars) => {
4
+ height: string;
5
+ width: string;
6
+ borderRadius: string;
7
+ color: string;
8
+ colorHover: string;
9
+ railInsetHorizontalBottom: string;
10
+ railInsetHorizontalTop: string;
11
+ railInsetVerticalRight: string;
12
+ railInsetVerticalLeft: string;
13
+ railColor: string;
14
+ };
15
+ export type SafeTopScrollbarThemeVars = ReturnType<typeof self>;
16
+ declare const safeTopScrollbarLight: Theme<'SafeTopScrollbar', SafeTopScrollbarThemeVars>;
17
+ export default safeTopScrollbarLight;
18
+ export type SafeTopScrollbarTheme = typeof safeTopScrollbarLight;
@@ -0,0 +1,12 @@
1
+ import { commonLight } from '../../../_styles/common';
2
+ import { commonVars } from './common';
3
+ export const self = (vars) => {
4
+ const { scrollbarColor, scrollbarColorHover, scrollbarHeight, scrollbarWidth, scrollbarBorderRadius } = vars;
5
+ return Object.assign(Object.assign({}, commonVars), { height: scrollbarHeight, width: scrollbarWidth, borderRadius: scrollbarBorderRadius, color: scrollbarColor, colorHover: scrollbarColorHover });
6
+ };
7
+ const safeTopScrollbarLight = {
8
+ name: 'SafeTopScrollbar',
9
+ common: commonLight,
10
+ self
11
+ };
12
+ export default safeTopScrollbarLight;
@@ -0,0 +1,3 @@
1
+ import type { RtlItem } from '../../../config-provider/src/internal-interface';
2
+ export declare const scrollbarRtl: RtlItem;
3
+ export default scrollbarRtl;
@@ -0,0 +1,6 @@
1
+ import rtlStyle from '../src/styles/rtl.cssr';
2
+ export const scrollbarRtl = {
3
+ name: 'SafeTopScrollbar',
4
+ style: rtlStyle
5
+ };
6
+ export default scrollbarRtl;
@@ -191,6 +191,10 @@ export declare const chatProps: {
191
191
  type: PropType<ChatPropsType["onAttachmentUpload"]>;
192
192
  default: undefined;
193
193
  };
194
+ onAttachmentDownload: {
195
+ type: PropType<ChatPropsType["onAttachmentDownload"]>;
196
+ default: undefined;
197
+ };
194
198
  onFilterChange: {
195
199
  type: PropType<ChatPropsType["onFilterChange"]>;
196
200
  default: undefined;
@@ -2413,6 +2417,10 @@ declare const _default: import("vue").DefineComponent<{
2413
2417
  type: PropType<ChatPropsType["onAttachmentUpload"]>;
2414
2418
  default: undefined;
2415
2419
  };
2420
+ onAttachmentDownload: {
2421
+ type: PropType<ChatPropsType["onAttachmentDownload"]>;
2422
+ default: undefined;
2423
+ };
2416
2424
  onFilterChange: {
2417
2425
  type: PropType<ChatPropsType["onFilterChange"]>;
2418
2426
  default: undefined;
@@ -5652,6 +5660,10 @@ declare const _default: import("vue").DefineComponent<{
5652
5660
  type: PropType<ChatPropsType["onAttachmentUpload"]>;
5653
5661
  default: undefined;
5654
5662
  };
5663
+ onAttachmentDownload: {
5664
+ type: PropType<ChatPropsType["onAttachmentDownload"]>;
5665
+ default: undefined;
5666
+ };
5655
5667
  onFilterChange: {
5656
5668
  type: PropType<ChatPropsType["onFilterChange"]>;
5657
5669
  default: undefined;
@@ -7716,6 +7728,7 @@ declare const _default: import("vue").DefineComponent<{
7716
7728
  onChatItemsScrollToBottom: () => void;
7717
7729
  messages: ChatMessageData[] | undefined;
7718
7730
  onMessageRetry: ((message: ChatMessageData) => void) | undefined;
7731
+ onAttachmentUpload: import("./interface").OnAttachmentUpload | undefined;
7719
7732
  onChatClose: () => void;
7720
7733
  onChatShare: () => void;
7721
7734
  onUserProfile: () => void;
@@ -7749,7 +7762,7 @@ declare const _default: import("vue").DefineComponent<{
7749
7762
  chatItemsLoadingCount: number | undefined;
7750
7763
  messagesLoading: boolean | undefined;
7751
7764
  messagesLoadingCount: number | undefined;
7752
- onAttachmentUpload: import("./interface").OnAttachmentUpload | undefined;
7765
+ onAttachmentDownload: import("./interface").OnAttachmentDownload | undefined;
7753
7766
  onFilterChange: import("./interface").OnFilterChange | undefined;
7754
7767
  onNetworkError: import("./interface").OnNetworkError | undefined;
7755
7768
  onUploadError: import("./interface").OnUploadError | undefined;
@@ -148,6 +148,9 @@ export const chatProps = Object.assign(Object.assign({}, useTheme.props), { chat
148
148
  }, onAttachmentUpload: {
149
149
  type: Function,
150
150
  default: undefined
151
+ }, onAttachmentDownload: {
152
+ type: Function,
153
+ default: undefined
151
154
  }, onFilterChange: {
152
155
  type: Function,
153
156
  default: undefined
@@ -394,6 +397,8 @@ export default defineComponent({
394
397
  handleNetworkError,
395
398
  handleUploadError,
396
399
  handleSendError,
400
+ onAttachmentUpload: toRef(props, 'onAttachmentUpload'),
401
+ onAttachmentDownload: toRef(props, 'onAttachmentDownload'),
397
402
  onChatClose: toRef(props, 'onChatClose'),
398
403
  onChatShare: toRef(props, 'onChatShare'),
399
404
  onUserProfile: toRef(props, 'onUserProfile'),
@@ -90,11 +90,26 @@ export default defineComponent({
90
90
  var _a;
91
91
  (_a = props.onChatSelect) === null || _a === void 0 ? void 0 : _a.call(props, chatId);
92
92
  };
93
+ const toKeyString = (value) => {
94
+ if (typeof value === 'symbol')
95
+ return value.toString();
96
+ if (typeof value === 'function') {
97
+ const result = value();
98
+ return typeof result === 'string' || typeof result === 'number'
99
+ ? String(result)
100
+ : '';
101
+ }
102
+ return value != null ? String(value) : '';
103
+ };
93
104
  const renderChatItem = (item) => {
94
105
  var _a, _b;
95
106
  const isSelected = props.selectedChatId === item.id;
96
107
  const isTyping = (_b = (_a = props.typingChatIds) === null || _a === void 0 ? void 0 : _a.includes(item.id)) !== null && _b !== void 0 ? _b : false;
97
- return (h(UListItem, { key: item.id, showIcon: false, onClick: () => {
108
+ const { id, title, subtitle } = item;
109
+ const keyId = toKeyString(id);
110
+ const keyTitle = toKeyString(title);
111
+ const keySubtitle = toKeyString(subtitle);
112
+ return (h(UListItem, { key: `${keyId}-${keyTitle}-${keySubtitle}`, showIcon: false, onClick: () => {
98
113
  handleChatSelect(item.id);
99
114
  }, class: [
100
115
  `${mergedClsPrefixRef.value}-chat-sidebar__item`,
@@ -1,5 +1,15 @@
1
- import { h, defineComponent } from 'vue';
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { h, defineComponent, inject } from 'vue';
2
11
  import { UUpload, UUploadFileList } from '../../../upload';
12
+ import { chatInjectionKey } from '../interface';
3
13
  export default defineComponent({
4
14
  name: 'ChatAttachment',
5
15
  props: {
@@ -21,10 +31,28 @@ export default defineComponent({
21
31
  }
22
32
  },
23
33
  setup(props, { slots }) {
34
+ const UChat = inject(chatInjectionKey, null);
35
+ const onAttachmentDownload = UChat === null || UChat === void 0 ? void 0 : UChat.onAttachmentDownload;
24
36
  const getThumbnailUrl = (attachment) => {
25
37
  const url = [attachment.preview, attachment.thumbnail].find((value) => typeof value === 'string');
26
38
  return url !== null && url !== void 0 ? url : null;
27
39
  };
40
+ const handleDownload = (file) => __awaiter(this, void 0, void 0, function* () {
41
+ if (onAttachmentDownload === null || onAttachmentDownload === void 0 ? void 0 : onAttachmentDownload.value) {
42
+ const attachment = props.attachments.find((att) => att.name === file.name);
43
+ if (attachment) {
44
+ try {
45
+ yield onAttachmentDownload.value(attachment);
46
+ return false;
47
+ }
48
+ catch (error) {
49
+ console.error('Download failed:', error);
50
+ return false;
51
+ }
52
+ }
53
+ }
54
+ return true;
55
+ });
28
56
  const renderAttachment = () => {
29
57
  const fileList = props.attachments.map((attachment, index) => {
30
58
  var _a, _b, _c;
@@ -40,7 +68,7 @@ export default defineComponent({
40
68
  batchId: null
41
69
  });
42
70
  });
43
- const uploadComponent = (h(UUpload, Object.assign({ abstract: true, fileList: fileList, fileListStyle: props.withPadding
71
+ const uploadComponent = (h(UUpload, Object.assign({ abstract: true }, props.uploadProps, { fileList: fileList, fileListStyle: props.withPadding
44
72
  ? {
45
73
  display: 'flex',
46
74
  flexDirection: 'column',
@@ -49,7 +77,7 @@ export default defineComponent({
49
77
  }
50
78
  : undefined, showRemoveButton: false, showDownloadButton: props.attachments.some((attachment) => attachment.status === 'finished' &&
51
79
  attachment.url &&
52
- attachment.url !== '#'), showRetryButton: props.attachments.some((attachment) => attachment.status === 'error') }, props.uploadProps), {
80
+ attachment.url !== '#'), showRetryButton: props.attachments.some((attachment) => attachment.status === 'error'), onDownload: handleDownload }), {
53
81
  default: () => (h(UUploadFileList, null, {
54
82
  'upload-file-title': slots['upload-file-title']
55
83
  ? ({ file }) => { var _a; return (_a = slots['upload-file-title']) === null || _a === void 0 ? void 0 : _a.call(slots, file); }
@@ -12,7 +12,7 @@ import { MessageStatus, ChatMessageType, chatInjectionKey } from '../interface';
12
12
  import { UInput } from '../../../input';
13
13
  import { UButton } from '../../../button';
14
14
  import { UIcon } from '../../../icon';
15
- import { UScrollbar } from '../../../scrollbar';
15
+ import { USafeTopScrollbar } from '../../../safe-top-scrollbar';
16
16
  import { UTooltip } from '../../../tooltip';
17
17
  import { UUpload, UUploadTrigger } from '../../../upload';
18
18
  import { UFlex } from '../../../flex';
@@ -24,13 +24,14 @@ const SENDING_DELAY = 100;
24
24
  export default defineComponent({
25
25
  name: 'ChatMainArea',
26
26
  setup(_, { slots }) {
27
- const { mergedClsPrefixRef, mergedThemeRef, selectedChatRef, messagesRef, typingChatIdsRef, messagesLoadingRef, messagesLoadingCountRef, headerButtonPropsRef, headerIconPropsRef, headerShareButtonPropsRef, headerProfileButtonPropsRef, headerCloseButtonPropsRef, headerShareIconPropsRef, headerProfileIconPropsRef, messageUploadPropsRef, footerInputPropsRef, footerButtonPropsRef, footerUploadPropsRef, footerIconPropsRef, inputPlaceholderRef, retryTextRef, typingTextRef, closeButtonTextRef, shareButtonTooltipRef, profileButtonTooltipRef, unreadNotificationTextRef, notificationsShownSetRef, unreadCountsBeforeReadRef, markNotificationShown, handleMessageSend, handleMessageRetry, handleFooterInputChange, onChatClose, onChatShare, onUserProfile, onMessagesScrollToTop, onMessagesScrollToBottom
27
+ const { mergedClsPrefixRef, mergedThemeRef, selectedChatRef, messagesRef, typingChatIdsRef, messagesLoadingRef, messagesLoadingCountRef, headerButtonPropsRef, headerIconPropsRef, headerShareButtonPropsRef, headerProfileButtonPropsRef, headerCloseButtonPropsRef, headerShareIconPropsRef, headerProfileIconPropsRef, messageUploadPropsRef, footerInputPropsRef, footerButtonPropsRef, footerUploadPropsRef, footerIconPropsRef, inputPlaceholderRef, retryTextRef, typingTextRef, closeButtonTextRef, shareButtonTooltipRef, profileButtonTooltipRef, unreadNotificationTextRef, notificationsShownSetRef, unreadCountsBeforeReadRef, markNotificationShown, handleMessageSend, handleMessageRetry, handleFooterInputChange, onAttachmentUpload, onChatClose, onChatShare, onUserProfile, onMessagesScrollToTop, onMessagesScrollToBottom
28
28
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
29
29
  } = inject(chatInjectionKey);
30
30
  const messagesBodyRef = ref();
31
31
  const inputRef = ref();
32
32
  const inputValue = ref('');
33
33
  const attachmentFileList = ref([]);
34
+ const uploadedAttachmentsMap = ref(new Map());
34
35
  const lastScrollTop = ref(0);
35
36
  const scrollTopFired = ref(false);
36
37
  const scrollBottomFired = ref(false);
@@ -55,6 +56,8 @@ export default defineComponent({
55
56
  scrollTopFired.value = false;
56
57
  scrollBottomFired.value = false;
57
58
  shouldScrollOnLoad.value = true;
59
+ attachmentFileList.value = [];
60
+ uploadedAttachmentsMap.value.clear();
58
61
  if (unreadBeforeRead > 0 &&
59
62
  !notificationsShownSetRef.value.has(newChat.id)) {
60
63
  hasUnreadMessages.value = true;
@@ -153,22 +156,36 @@ export default defineComponent({
153
156
  el.scrollTop = el.scrollHeight;
154
157
  }
155
158
  };
159
+ const toKeyString = (value) => {
160
+ if (typeof value === 'symbol')
161
+ return value.toString();
162
+ if (typeof value === 'function') {
163
+ const result = value();
164
+ return typeof result === 'string' || typeof result === 'number'
165
+ ? String(result)
166
+ : '';
167
+ }
168
+ return value != null ? String(value) : '';
169
+ };
156
170
  const renderHeader = () => {
157
171
  return (h("div", { class: `${mergedClsPrefixRef.value}-chat-main__header` },
158
172
  h(UFlex, { justify: "space-between", align: "flex-start", wrap: false }, {
159
- default: () => (h(Fragment, null,
160
- h(UText, { variant: "heading-s-bold", class: `${mergedClsPrefixRef.value}-chat-main__header-title`, theme: mergedThemeRef.value.peers.Typography, themeOverrides: mergedThemeRef.value.peerOverrides.Typography }, {
161
- default: () => { var _a, _b; return (_b = (_a = selectedChatRef.value) === null || _a === void 0 ? void 0 : _a.title) !== null && _b !== void 0 ? _b : ''; }
162
- }),
163
- h(UFlex, { align: "center", size: "small", class: `${mergedClsPrefixRef.value}-chat-main__header-actions` }, {
164
- default: () => resolveSlot(slots.headerActions, () => {
165
- const shareButtonProps = Object.assign(Object.assign({}, headerButtonPropsRef.value), headerShareButtonPropsRef.value);
166
- const profileButtonProps = Object.assign(Object.assign({}, headerButtonPropsRef.value), headerProfileButtonPropsRef.value);
167
- const closeButtonProps = Object.assign(Object.assign({}, headerButtonPropsRef.value), headerCloseButtonPropsRef.value);
168
- const shareIconProps = Object.assign(Object.assign({}, headerIconPropsRef.value), headerShareIconPropsRef.value);
169
- const profileIconProps = Object.assign(Object.assign({}, headerIconPropsRef.value), headerProfileIconPropsRef.value);
170
- const buttons = [];
171
- if (shareButtonProps.disabled !== true) {
173
+ default: () => {
174
+ const chat = selectedChatRef.value;
175
+ const keyId = toKeyString(chat === null || chat === void 0 ? void 0 : chat.id);
176
+ const keyTitle = toKeyString(chat === null || chat === void 0 ? void 0 : chat.title);
177
+ return (h(Fragment, null,
178
+ h(UText, { key: `${keyId}-${keyTitle}`, variant: "heading-s-bold", class: `${mergedClsPrefixRef.value}-chat-main__header-title`, theme: mergedThemeRef.value.peers.Typography, themeOverrides: mergedThemeRef.value.peerOverrides.Typography }, {
179
+ default: () => { var _a, _b; return (_b = (_a = selectedChatRef.value) === null || _a === void 0 ? void 0 : _a.title) !== null && _b !== void 0 ? _b : ''; }
180
+ }),
181
+ h(UFlex, { align: "center", size: "small", class: `${mergedClsPrefixRef.value}-chat-main__header-actions` }, {
182
+ default: () => resolveSlot(slots.headerActions, () => {
183
+ const shareButtonProps = Object.assign(Object.assign({}, headerButtonPropsRef.value), headerShareButtonPropsRef.value);
184
+ const profileButtonProps = Object.assign(Object.assign({}, headerButtonPropsRef.value), headerProfileButtonPropsRef.value);
185
+ const closeButtonProps = Object.assign(Object.assign({}, headerButtonPropsRef.value), headerCloseButtonPropsRef.value);
186
+ const shareIconProps = Object.assign(Object.assign({}, headerIconPropsRef.value), headerShareIconPropsRef.value);
187
+ const profileIconProps = Object.assign(Object.assign({}, headerIconPropsRef.value), headerProfileIconPropsRef.value);
188
+ const buttons = [];
172
189
  buttons.push(h(UTooltip, null, {
173
190
  trigger: () => (h(UButton, Object.assign({ secondary: true, circle: true, size: "large" }, shareButtonProps, { theme: mergedThemeRef.value.peers.Button, themeOverrides: mergedThemeRef.value.peerOverrides
174
191
  .Button, onClick: () => { var _a; return (_a = onChatShare === null || onChatShare === void 0 ? void 0 : onChatShare.value) === null || _a === void 0 ? void 0 : _a.call(onChatShare); } }), {
@@ -179,8 +196,6 @@ export default defineComponent({
179
196
  })),
180
197
  default: () => shareButtonTooltipRef.value
181
198
  }));
182
- }
183
- if (profileButtonProps.disabled !== true) {
184
199
  buttons.push(h(UTooltip, null, {
185
200
  trigger: () => (h(UButton, Object.assign({ secondary: true, circle: true, size: "large" }, profileButtonProps, { theme: mergedThemeRef.value.peers.Button, themeOverrides: mergedThemeRef.value.peerOverrides
186
201
  .Button, onClick: () => { var _a; return (_a = onUserProfile === null || onUserProfile === void 0 ? void 0 : onUserProfile.value) === null || _a === void 0 ? void 0 : _a.call(onUserProfile); } }), {
@@ -191,15 +206,13 @@ export default defineComponent({
191
206
  })),
192
207
  default: () => profileButtonTooltipRef.value
193
208
  }));
194
- }
195
- if (closeButtonProps.disabled !== true) {
196
209
  buttons.push(h(UButton, Object.assign({ type: "primary", size: "large", round: true }, closeButtonProps, { theme: mergedThemeRef.value.peers.Button, themeOverrides: mergedThemeRef.value.peerOverrides.Button, onClick: () => { var _a; return (_a = onChatClose === null || onChatClose === void 0 ? void 0 : onChatClose.value) === null || _a === void 0 ? void 0 : _a.call(onChatClose); } }), {
197
210
  default: () => closeButtonTextRef.value
198
211
  }));
199
- }
200
- return buttons;
201
- })
202
- })))
212
+ return buttons;
213
+ })
214
+ })));
215
+ }
203
216
  })));
204
217
  };
205
218
  const renderMessages = () => {
@@ -226,7 +239,11 @@ export default defineComponent({
226
239
  const attachments = attachmentFileList.value.length > 0
227
240
  ? attachmentFileList.value.map((file) => {
228
241
  var _a, _b, _c;
229
- return ({
242
+ const uploadedAttachment = uploadedAttachmentsMap.value.get(file.id);
243
+ if (uploadedAttachment) {
244
+ return uploadedAttachment;
245
+ }
246
+ return {
230
247
  id: file.id,
231
248
  type: ChatMessageType.FILE,
232
249
  name: file.name,
@@ -236,13 +253,14 @@ export default defineComponent({
236
253
  thumbnail: (_b = file.thumbnailUrl) !== null && _b !== void 0 ? _b : undefined,
237
254
  status: file.status,
238
255
  percentage: (_c = file.percentage) !== null && _c !== void 0 ? _c : undefined
239
- });
256
+ };
240
257
  })
241
258
  : undefined;
242
259
  handleMessageSend(inputValue.value.trim(), attachments);
243
260
  inputValue.value = '';
244
261
  attachmentFileList.value = [];
245
262
  chatInputs.value[selectedChatRef.value.id] = '';
263
+ uploadedAttachmentsMap.value.clear();
246
264
  hasUnreadMessages.value = false;
247
265
  showNotificationManually.value = false;
248
266
  }
@@ -253,39 +271,24 @@ export default defineComponent({
253
271
  }
254
272
  }
255
273
  });
256
- let fileUploadTimer = null;
257
- const pendingFiles = ref([]);
258
274
  const handleFileListUpdate = (fileList) => {
259
- const newFiles = fileList.filter((file) => !attachmentFileList.value.some((existing) => existing.id === file.id) &&
260
- !pendingFiles.value.some((existing) => existing.id === file.id));
261
- if (newFiles.length > 0) {
262
- pendingFiles.value = [...pendingFiles.value, ...newFiles];
263
- if (fileUploadTimer) {
264
- clearTimeout(fileUploadTimer);
265
- }
266
- fileUploadTimer = setTimeout(() => {
267
- if (pendingFiles.value.length > 0 && handleMessageSend) {
268
- const hasError = pendingFiles.value.some((file) => file.status === 'error');
269
- const batchStatus = hasError ? 'error' : 'finished';
270
- const attachments = pendingFiles.value.map((file) => {
271
- var _a, _b, _c;
272
- return ({
273
- id: file.id,
274
- type: ChatMessageType.FILE,
275
- name: file.name,
276
- url: file.url ||
277
- (file.file ? URL.createObjectURL(file.file) : '#'),
278
- size: (_a = file.file) === null || _a === void 0 ? void 0 : _a.size,
279
- thumbnail: (_b = file.thumbnailUrl) !== null && _b !== void 0 ? _b : undefined,
280
- status: batchStatus,
281
- percentage: (_c = file.percentage) !== null && _c !== void 0 ? _c : undefined
282
- });
283
- });
284
- handleMessageSend('', attachments);
285
- pendingFiles.value = [];
286
- attachmentFileList.value = [];
275
+ attachmentFileList.value = fileList;
276
+ const uploadCallback = onAttachmentUpload === null || onAttachmentUpload === void 0 ? void 0 : onAttachmentUpload.value;
277
+ if (uploadCallback) {
278
+ const newFiles = fileList.filter((file) => file.file && !uploadedAttachmentsMap.value.has(file.id));
279
+ void (() => __awaiter(this, void 0, void 0, function* () {
280
+ for (const uploadFile of newFiles) {
281
+ if (uploadFile.file) {
282
+ try {
283
+ const attachment = yield uploadCallback(uploadFile.file);
284
+ uploadedAttachmentsMap.value.set(uploadFile.id, attachment);
285
+ }
286
+ catch (error) {
287
+ console.error('File upload failed:', error);
288
+ }
289
+ }
287
290
  }
288
- }, SENDING_DELAY);
291
+ }))();
289
292
  }
290
293
  };
291
294
  const renderFooter = () => {
@@ -294,11 +297,14 @@ export default defineComponent({
294
297
  default: () => (h(UFlex, { align: "center", size: "small", class: `${mergedClsPrefixRef.value}-chat-main__input-container` }, {
295
298
  default: () => (h(Fragment, null,
296
299
  h(UUploadTrigger, { abstract: true }, {
297
- default: ({ handleClick }) => (h(UButton, Object.assign({ secondary: true, size: "large", class: `${mergedClsPrefixRef.value}-chat-main__attach-btn` }, footerButtonPropsRef.value, { theme: mergedThemeRef.value.peers.Button, themeOverrides: mergedThemeRef.value.peerOverrides.Button, onClick: handleClick }), {
298
- default: () => (h(UIcon, Object.assign({ size: 24 }, footerIconPropsRef.value, { theme: mergedThemeRef.value.peers.Icon, themeOverrides: mergedThemeRef.value.peerOverrides.Icon }), {
299
- default: () => h(AttachIcon, null)
300
- }))
301
- }))
300
+ default: ({ handleClick }) => {
301
+ var _a;
302
+ return (h(UButton, Object.assign({ secondary: true, size: "large", class: `${mergedClsPrefixRef.value}-chat-main__attach-btn` }, footerButtonPropsRef.value, { disabled: (_a = footerInputPropsRef.value) === null || _a === void 0 ? void 0 : _a.disabled, theme: mergedThemeRef.value.peers.Button, themeOverrides: mergedThemeRef.value.peerOverrides.Button, onClick: handleClick }), {
303
+ default: () => (h(UIcon, Object.assign({ size: 24 }, footerIconPropsRef.value, { theme: mergedThemeRef.value.peers.Icon, themeOverrides: mergedThemeRef.value.peerOverrides.Icon }), {
304
+ default: () => h(AttachIcon, null)
305
+ }))
306
+ }));
307
+ }
302
308
  }),
303
309
  h(UInput, Object.assign({ ref: "inputRef", value: inputValue.value, placeholder: inputPlaceholderRef.value, class: `${mergedClsPrefixRef.value}-chat-main__input` }, footerInputPropsRef.value, { theme: mergedThemeRef.value.peers.Input, themeOverrides: mergedThemeRef.value.peerOverrides.Input, onUpdateValue: (value) => {
304
310
  inputValue.value = value;
@@ -332,7 +338,7 @@ export default defineComponent({
332
338
  const { mergedClsPrefixRef } = inject(chatInjectionKey);
333
339
  return (h("div", { class: `${mergedClsPrefixRef.value}-chat-main` },
334
340
  this.renderHeader(),
335
- h(UScrollbar, { ref: "messagesBodyRef", class: `${mergedClsPrefixRef.value}-chat-main__body`, onScroll: this.handleMessagesScroll }, {
341
+ h(USafeTopScrollbar, { ref: "messagesBodyRef", class: `${mergedClsPrefixRef.value}-chat-main__body`, onScroll: this.handleMessagesScroll }, {
336
342
  default: () => this.renderMessages()
337
343
  }),
338
344
  this.renderFooter()));
@@ -67,7 +67,7 @@ export interface ChatMessageData {
67
67
  timestamp?: string | number | Date | (() => VNodeChild);
68
68
  status?: MessageStatus;
69
69
  date?: string;
70
- attachment?: ChatAttachment | ChatAttachment[];
70
+ attachment?: ChatAttachment[];
71
71
  uploadProps?: Partial<UploadProps>;
72
72
  senderAvatar?: string | (() => VNodeChild);
73
73
  senderName?: string | (() => VNodeChild);
@@ -80,7 +80,7 @@ export interface ChatMessageProps {
80
80
  content?: string;
81
81
  timestamp?: string | number | Date | (() => VNodeChild);
82
82
  status?: MessageStatus;
83
- attachment?: ChatAttachment | ChatAttachment[];
83
+ attachment?: ChatAttachment[];
84
84
  uploadProps?: Partial<UploadProps>;
85
85
  senderAvatar?: string | (() => VNodeChild);
86
86
  senderName?: string | (() => VNodeChild);
@@ -165,6 +165,7 @@ export interface ChatProps {
165
165
  onMessageSend?: OnMessageSend;
166
166
  onMessageRetry?: (message: ChatMessageData) => void;
167
167
  onAttachmentUpload?: OnAttachmentUpload;
168
+ onAttachmentDownload?: OnAttachmentDownload;
168
169
  onFilterChange?: OnFilterChange;
169
170
  onFooterInputChange?: (value: string, chatId: ChatId) => void;
170
171
  onNetworkError?: OnNetworkError;
@@ -225,6 +226,7 @@ export interface ChatFooterProps {
225
226
  export type OnChatSelect = (key: ChatId, item: ChatListItemData) => void;
226
227
  export type OnMessageSend = (content: string, attachments?: ChatAttachment[]) => void;
227
228
  export type OnAttachmentUpload = (file: File) => Promise<ChatAttachment>;
229
+ export type OnAttachmentDownload = (attachment: ChatAttachment) => Promise<void>;
228
230
  export type OnFilterChange = (filter: {
229
231
  type: string;
230
232
  value: any;
@@ -311,6 +313,8 @@ export declare const chatInjectionKey: InjectionKey<{
311
313
  message: string;
312
314
  content: string;
313
315
  }) => void;
316
+ onAttachmentUpload?: Ref<OnAttachmentUpload | undefined>;
317
+ onAttachmentDownload?: Ref<OnAttachmentDownload | undefined>;
314
318
  onChatClose?: Ref<(() => void) | undefined>;
315
319
  onChatShare?: Ref<(() => void) | undefined>;
316
320
  onUserProfile?: Ref<(() => void) | undefined>;
@@ -69,6 +69,7 @@ export * from './radio';
69
69
  export * from './rate';
70
70
  export * from './result';
71
71
  export * from './scrollbar';
72
+ export * from './safe-top-scrollbar';
72
73
  export * from './select';
73
74
  export * from './skeleton';
74
75
  export * from './slider';