@acorex/components 21.0.3-next.39 → 21.0.3-next.40
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/{conversation2 → conversation}/README.md +23 -23
- package/fesm2022/acorex-components-button.mjs +4 -4
- package/fesm2022/acorex-components-button.mjs.map +1 -1
- package/fesm2022/acorex-components-conversation-acorex-components-conversation-YhVa42TC.mjs +13711 -0
- package/fesm2022/acorex-components-conversation-acorex-components-conversation-YhVa42TC.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-audio-attachment.component-CLvS3W8F.mjs +119 -0
- package/fesm2022/acorex-components-conversation-audio-attachment.component-CLvS3W8F.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-audio-composer.payload-BHQJNyXz.mjs +59 -0
- package/fesm2022/acorex-components-conversation-audio-composer.payload-BHQJNyXz.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-audio-picker.component-BD_fN2Ip.mjs +432 -0
- package/fesm2022/acorex-components-conversation-audio-picker.component-BD_fN2Ip.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-audio-renderer.component-CcxVVprx.mjs +177 -0
- package/fesm2022/acorex-components-conversation-audio-renderer.component-CcxVVprx.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-audio.payload-Bag4aTut.mjs +54 -0
- package/fesm2022/acorex-components-conversation-audio.payload-Bag4aTut.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-composer-action-choice-dialog.component-C28hl7iD.mjs +101 -0
- package/fesm2022/acorex-components-conversation-composer-action-choice-dialog.component-C28hl7iD.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-conversation-audio.file-type-CfGSVnMA.mjs +100 -0
- package/fesm2022/acorex-components-conversation-conversation-audio.file-type-CfGSVnMA.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-conversation-file.file-type-DB-2xhaC.mjs +121 -0
- package/fesm2022/acorex-components-conversation-conversation-file.file-type-DB-2xhaC.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-conversation-image.file-type-C02OzZZB.mjs +84 -0
- package/fesm2022/acorex-components-conversation-conversation-image.file-type-C02OzZZB.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-conversation-info-media-view.component-Mhk7JE6K.mjs +238 -0
- package/fesm2022/acorex-components-conversation-conversation-info-media-view.component-Mhk7JE6K.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-conversation-info-panel.component-CNaM5ajR.mjs +1080 -0
- package/fesm2022/acorex-components-conversation-conversation-info-panel.component-CNaM5ajR.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-conversation-video.file-type-Pescv4Vu.mjs +102 -0
- package/fesm2022/acorex-components-conversation-conversation-video.file-type-Pescv4Vu.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-conversation-voice.file-type-DDd47o4r.mjs +90 -0
- package/fesm2022/acorex-components-conversation-conversation-voice.file-type-DDd47o4r.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-emoji-categories-C7Gm95V0.mjs +1840 -0
- package/fesm2022/acorex-components-conversation-emoji-categories-C7Gm95V0.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-fallback-renderer.component-CYh_6O6j.mjs +44 -0
- package/fesm2022/acorex-components-conversation-fallback-renderer.component-CYh_6O6j.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-file-composer.payload-Dy7aG-EH.mjs +64 -0
- package/fesm2022/acorex-components-conversation-file-composer.payload-Dy7aG-EH.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-file-picker.component-DCVgl33l.mjs +468 -0
- package/fesm2022/acorex-components-conversation-file-picker.component-DCVgl33l.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-file-renderer.component-DgCi06Mg.mjs +125 -0
- package/fesm2022/acorex-components-conversation-file-renderer.component-DgCi06Mg.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-file.payload-DyFxTQYn.mjs +50 -0
- package/fesm2022/acorex-components-conversation-file.payload-DyFxTQYn.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-format-media-DUD4vA6F.mjs +21 -0
- package/fesm2022/acorex-components-conversation-format-media-DUD4vA6F.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-image-attachment.component-CLMGxXPl.mjs +77 -0
- package/fesm2022/acorex-components-conversation-image-attachment.component-CLMGxXPl.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-image-picker.component-CS343UDa.mjs +407 -0
- package/fesm2022/acorex-components-conversation-image-picker.component-CS343UDa.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-image-renderer.component-Cy0TA6cL.mjs +83 -0
- package/fesm2022/acorex-components-conversation-image-renderer.component-Cy0TA6cL.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-index-3V2b14I0.mjs +184 -0
- package/fesm2022/acorex-components-conversation-index-3V2b14I0.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-index-MCvfr9Zd.mjs +266 -0
- package/fesm2022/acorex-components-conversation-index-MCvfr9Zd.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-location-picker.component-DcNQzuoU.mjs +149 -0
- package/fesm2022/acorex-components-conversation-location-picker.component-DcNQzuoU.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-location-renderer.component-Cw2PB0LM.mjs +118 -0
- package/fesm2022/acorex-components-conversation-location-renderer.component-Cw2PB0LM.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-media-formatters-DV3wEEno.mjs +17 -0
- package/fesm2022/acorex-components-conversation-media-formatters-DV3wEEno.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-media-playback-info-bar-banner.component-DbWUl6M2.mjs +140 -0
- package/fesm2022/acorex-components-conversation-media-playback-info-bar-banner.component-DbWUl6M2.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-message-renderer-copy-host.component-oXTr_Lfq.mjs +33 -0
- package/fesm2022/acorex-components-conversation-message-renderer-copy-host.component-oXTr_Lfq.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-message-renderer-state.component-Dp-JdHWr.mjs +172 -0
- package/fesm2022/acorex-components-conversation-message-renderer-state.component-Dp-JdHWr.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-open-composer-picker-for-files-Du8Lj9Sp.mjs +224 -0
- package/fesm2022/acorex-components-conversation-open-composer-picker-for-files-Du8Lj9Sp.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-pagination-demo-data-oIT_YJmN.mjs +81 -0
- package/fesm2022/acorex-components-conversation-pagination-demo-data-oIT_YJmN.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-picker-header.component-BneIFjXq.mjs +91 -0
- package/fesm2022/acorex-components-conversation-picker-header.component-BneIFjXq.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-picker-toolbar.component-BthRvEeE.mjs +537 -0
- package/fesm2022/acorex-components-conversation-picker-toolbar.component-BthRvEeE.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-picker-upload-C-PlKPaU.mjs +163 -0
- package/fesm2022/acorex-components-conversation-picker-upload-C-PlKPaU.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-playback-banner-sync-D8FFKde6.mjs +68 -0
- package/fesm2022/acorex-components-conversation-playback-banner-sync-D8FFKde6.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-renderer-shared.styles-Digah3Zn.mjs +95 -0
- package/fesm2022/acorex-components-conversation-renderer-shared.styles-Digah3Zn.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-search.component-B-nc6xiA.mjs +187 -0
- package/fesm2022/acorex-components-conversation-search.component-B-nc6xiA.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-send-message-media-BRklgfV7.mjs +170 -0
- package/fesm2022/acorex-components-conversation-send-message-media-BRklgfV7.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-shared-storage.seed-J9PA4l3c.mjs +750 -0
- package/fesm2022/acorex-components-conversation-shared-storage.seed-J9PA4l3c.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-sticker-renderer.component-BmBwy7Ig.mjs +103 -0
- package/fesm2022/acorex-components-conversation-sticker-renderer.component-BmBwy7Ig.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-system-renderer.component-uie3tm5N.mjs +55 -0
- package/fesm2022/acorex-components-conversation-system-renderer.component-uie3tm5N.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-text-renderer.component-DNAquHYK.mjs +109 -0
- package/fesm2022/acorex-components-conversation-text-renderer.component-DNAquHYK.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-video-attachment.component-CGaUhnSb.mjs +105 -0
- package/fesm2022/acorex-components-conversation-video-attachment.component-CGaUhnSb.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-video-composer.payload-DMqRkafr.mjs +62 -0
- package/fesm2022/acorex-components-conversation-video-composer.payload-DMqRkafr.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-video-picker.component-DPwSBnoH.mjs +446 -0
- package/fesm2022/acorex-components-conversation-video-picker.component-DPwSBnoH.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-video-renderer.component-DKa9mUe2.mjs +181 -0
- package/fesm2022/acorex-components-conversation-video-renderer.component-DKa9mUe2.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-video.payload-C8QaqxuX.mjs +54 -0
- package/fesm2022/acorex-components-conversation-video.payload-C8QaqxuX.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-voice-composer.payload-DLVow7ex.mjs +17 -0
- package/fesm2022/acorex-components-conversation-voice-composer.payload-DLVow7ex.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-voice-recorder.component-D_rdANZN.mjs +344 -0
- package/fesm2022/acorex-components-conversation-voice-recorder.component-D_rdANZN.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation-voice-renderer.component-BY5FJcT8.mjs +270 -0
- package/fesm2022/acorex-components-conversation-voice-renderer.component-BY5FJcT8.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation.mjs +2 -0
- package/fesm2022/acorex-components-conversation.mjs.map +1 -0
- package/fesm2022/acorex-components-kbd.mjs +2 -2
- package/fesm2022/acorex-components-kbd.mjs.map +1 -1
- package/fesm2022/acorex-components-scheduler-picker.mjs +22 -14
- package/fesm2022/acorex-components-scheduler-picker.mjs.map +1 -1
- package/fesm2022/acorex-components-side-menu.mjs +4 -13
- package/fesm2022/acorex-components-side-menu.mjs.map +1 -1
- package/fesm2022/acorex-components-tree-view.mjs +2 -2
- package/fesm2022/acorex-components-tree-view.mjs.map +1 -1
- package/package.json +6 -10
- package/types/{acorex-components-conversation2.d.ts → acorex-components-conversation.d.ts} +1127 -1928
- package/types/acorex-components-scheduler-picker.d.ts +2 -0
- package/types/acorex-components-side-menu.d.ts +1 -3
- package/fesm2022/acorex-components-conversation2.mjs +0 -22347
- package/fesm2022/acorex-components-conversation2.mjs.map +0 -1
- package/fesm2022/acorex-components-sidebar.mjs +0 -275
- package/fesm2022/acorex-components-sidebar.mjs.map +0 -1
- package/sidebar/README.md +0 -5
- package/types/acorex-components-sidebar.d.ts +0 -132
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { formatFileSizeBytes } from '@acorex/core/file';
|
|
2
|
+
import { afterNextRender } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Shared upload state and helpers for composer media pickers.
|
|
6
|
+
*/
|
|
7
|
+
function getPickerCancelUploadLabel(translation) {
|
|
8
|
+
return translation.translateSync('@acorex:chat.pickers.common.cancel-upload');
|
|
9
|
+
}
|
|
10
|
+
function abortPickerUploads(items) {
|
|
11
|
+
for (const item of items) {
|
|
12
|
+
const controller = item?.abortController;
|
|
13
|
+
if (controller && !controller.signal.aborted) {
|
|
14
|
+
controller.abort();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** Upload finished; reference is {@link mediaId} and/or a non-empty session url. */
|
|
19
|
+
function isPickerItemReadyToSend(item) {
|
|
20
|
+
return !!item.uploaded && !!(item.mediaId || item.uploadedUrl?.trim());
|
|
21
|
+
}
|
|
22
|
+
/** Build uploader result shape for {@link *ItemFromUpload} after picker upload. */
|
|
23
|
+
function pickerItemToUploadResult(item) {
|
|
24
|
+
if (!isPickerItemReadyToSend(item)) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
url: item.uploadedUrl?.trim() ?? '',
|
|
29
|
+
mediaId: item.mediaId,
|
|
30
|
+
mimeType: item.file.type,
|
|
31
|
+
size: item.file.size,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function pickerItemToMediaReference(item) {
|
|
35
|
+
if (!isPickerItemReadyToSend(item)) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
url: item.uploadedUrl,
|
|
40
|
+
mediaId: item.mediaId,
|
|
41
|
+
mimeType: item.file.type,
|
|
42
|
+
size: item.file.size,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
async function deleteUploadedPickerMedia(uploader, items) {
|
|
46
|
+
const tasks = [];
|
|
47
|
+
for (const item of items) {
|
|
48
|
+
const reference = item ? pickerItemToMediaReference(item) : null;
|
|
49
|
+
if (reference) {
|
|
50
|
+
tasks.push(uploader.deleteMedia(reference).catch(() => {
|
|
51
|
+
/* ignore cleanup failures */
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
await Promise.all(tasks);
|
|
56
|
+
}
|
|
57
|
+
function cleanupPickerUploads(uploader, items, options) {
|
|
58
|
+
abortPickerUploads(items);
|
|
59
|
+
if (!options?.retainUploaded) {
|
|
60
|
+
void deleteUploadedPickerMedia(uploader, items);
|
|
61
|
+
}
|
|
62
|
+
revokePickerBlobPreviews(items);
|
|
63
|
+
}
|
|
64
|
+
function revokePickerBlobPreviews(items) {
|
|
65
|
+
if (typeof URL === 'undefined' || typeof URL.revokeObjectURL !== 'function') {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
for (const item of items) {
|
|
69
|
+
const preview = item?.preview;
|
|
70
|
+
if (preview?.startsWith('blob:')) {
|
|
71
|
+
URL.revokeObjectURL(preview);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function isUploadAborted(error, signal) {
|
|
76
|
+
if (signal?.aborted) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
const code = error?.code;
|
|
80
|
+
return code === 'UPLOAD_ABORTED' || code === 'RESOLVE_ABORTED';
|
|
81
|
+
}
|
|
82
|
+
function limitFilesToCapacity(files, currentCount, maxFiles) {
|
|
83
|
+
return files.slice(0, Math.max(0, maxFiles - currentCount));
|
|
84
|
+
}
|
|
85
|
+
async function uploadPickerFile(uploader, conversationId, file, onProgress, signal) {
|
|
86
|
+
return uploader.upload({
|
|
87
|
+
source: file,
|
|
88
|
+
fileName: file.name,
|
|
89
|
+
mimeType: file.type,
|
|
90
|
+
onProgress,
|
|
91
|
+
signal,
|
|
92
|
+
metadata: { conversationId },
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function notifyMaxFilesCapacityExceeded(toast, translation, maxFiles) {
|
|
96
|
+
toast.warning(translation.translateSync('@acorex:chat.errors.max-files-per-message', {
|
|
97
|
+
params: { count: maxFiles },
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
/** User-facing message for a file validation failure. */
|
|
101
|
+
function formatPickerValidationMessage(translation, file, error) {
|
|
102
|
+
if (!error) {
|
|
103
|
+
return translation.translateSync('@acorex:chat.errors.upload-failed');
|
|
104
|
+
}
|
|
105
|
+
const maxSize = typeof error['maxSize'] === 'number' ? error['maxSize'] : undefined;
|
|
106
|
+
const minSize = typeof error['minSize'] === 'number' ? error['minSize'] : undefined;
|
|
107
|
+
switch (error.rule) {
|
|
108
|
+
case 'fileMaxSize':
|
|
109
|
+
return translation.translateSync('@acorex:chat.errors.file-size-exceeds-limit', {
|
|
110
|
+
params: {
|
|
111
|
+
fileName: file.name,
|
|
112
|
+
fileSize: formatFileSizeBytes(file.size),
|
|
113
|
+
maxSize: formatFileSizeBytes(maxSize ?? 0),
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
case 'fileMinSize':
|
|
117
|
+
return translation.translateSync('@acorex:chat.errors.file-below-minimum-size', {
|
|
118
|
+
params: {
|
|
119
|
+
fileName: file.name,
|
|
120
|
+
minSize: formatFileSizeBytes(minSize ?? 0),
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
case 'fileMimeType':
|
|
124
|
+
return translation.translateSync('@acorex:chat.errors.file-type-not-allowed', {
|
|
125
|
+
params: {
|
|
126
|
+
fileName: file.name,
|
|
127
|
+
fileType: (file.type || 'unknown').trim() || 'unknown',
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
default:
|
|
131
|
+
return error.message?.trim()
|
|
132
|
+
? file.name
|
|
133
|
+
? `${file.name}: ${error.message}`
|
|
134
|
+
: error.message
|
|
135
|
+
: translation.translateSync('@acorex:chat.errors.upload-failed');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/** Show a warning toast for each failed validation rule result. */
|
|
139
|
+
function notifyPickerValidationErrors(toast, translation, rejected) {
|
|
140
|
+
for (const { file, errors } of rejected) {
|
|
141
|
+
toast.warning(formatPickerValidationMessage(translation, file, errors[0]));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/** Load files dropped onto the message list after the picker is mounted in a popup. */
|
|
145
|
+
function seedPickerInitialFiles(initialFiles, handleFiles) {
|
|
146
|
+
afterNextRender(() => {
|
|
147
|
+
const files = initialFiles();
|
|
148
|
+
if (files.length > 0) {
|
|
149
|
+
void handleFiles([...files]);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/** Close inline composer shell or popup host after send/cancel. */
|
|
154
|
+
function dismissComposerPickerHost(composerService, popupRef) {
|
|
155
|
+
if (popupRef) {
|
|
156
|
+
popupRef.close();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
composerService.hideComponent();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export { notifyMaxFilesCapacityExceeded as a, cleanupPickerUploads as c, dismissComposerPickerHost as d, getPickerCancelUploadLabel as g, isUploadAborted as i, limitFilesToCapacity as l, notifyPickerValidationErrors as n, pickerItemToUploadResult as p, seedPickerInitialFiles as s, uploadPickerFile as u };
|
|
163
|
+
//# sourceMappingURL=acorex-components-conversation-picker-upload-C-PlKPaU.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acorex-components-conversation-picker-upload-C-PlKPaU.mjs","sources":["../../../../packages/components/conversation/src/lib/plugins/composer/components/shared/picker-upload.ts"],"sourcesContent":["/**\n * Shared upload state and helpers for composer media pickers.\n */\n\nimport { AXUploaderService } from '@acorex/cdk/uploader';\nimport { AXToastService } from '@acorex/components/toast';\nimport type { AXPopupRef } from '@acorex/components/popup';\nimport { formatFileSizeBytes } from '@acorex/core/file';\nimport type { AXTranslationService } from '@acorex/core/translation';\nimport type { AXValidationRuleResult } from '@acorex/core/validation';\nimport { afterNextRender, type InputSignal } from '@angular/core';\nimport type { AXConversationComposerService } from '../../../../components/composer/composer.service';\nimport type { AXConversationUploaderReference } from '../../../../models/conversation-upload.types';\n\nexport interface AXConversationComposerPickerUploadItem {\n file: File;\n preview?: string;\n duration?: number;\n uploadProgress?: number;\n uploading?: boolean;\n uploaded?: boolean;\n uploadedUrl?: string;\n mediaId?: string;\n error?: string;\n abortController?: AbortController;\n}\n\nexport function getPickerCancelUploadLabel(translation: AXTranslationService): string {\n return translation.translateSync('@acorex:chat.pickers.common.cancel-upload');\n}\n\nexport function abortPickerUploads(items: Iterable<{ abortController?: AbortController } | null | undefined>): void {\n for (const item of items) {\n const controller = item?.abortController;\n if (controller && !controller.signal.aborted) {\n controller.abort();\n }\n }\n}\n\nexport interface AXConversationCleanupPickerUploadsOptions {\n retainUploaded?: boolean;\n}\n\n/** Upload finished; reference is {@link mediaId} and/or a non-empty session url. */\nexport function isPickerItemReadyToSend(\n item: Pick<AXConversationComposerPickerUploadItem, 'uploaded' | 'uploadedUrl' | 'mediaId'>,\n): boolean {\n return !!item.uploaded && !!(item.mediaId || item.uploadedUrl?.trim());\n}\n\n/** Build uploader result shape for {@link *ItemFromUpload} after picker upload. */\nexport function pickerItemToUploadResult(\n item: Pick<AXConversationComposerPickerUploadItem, 'uploaded' | 'uploadedUrl' | 'mediaId' | 'file'>,\n): { url: string; mediaId?: string; mimeType: string; size: number } | null {\n if (!isPickerItemReadyToSend(item)) {\n return null;\n }\n return {\n url: item.uploadedUrl?.trim() ?? '',\n mediaId: item.mediaId,\n mimeType: item.file.type,\n size: item.file.size,\n };\n}\n\nexport function pickerItemToMediaReference(\n item: Pick<AXConversationComposerPickerUploadItem, 'uploaded' | 'uploadedUrl' | 'mediaId' | 'file'>,\n): AXConversationUploaderReference | null {\n if (!isPickerItemReadyToSend(item)) {\n return null;\n }\n return {\n url: item.uploadedUrl,\n mediaId: item.mediaId,\n mimeType: item.file.type,\n size: item.file.size,\n };\n}\n\nexport async function deleteUploadedPickerMedia(\n uploader: AXUploaderService,\n items: Iterable<AXConversationComposerPickerUploadItem | null | undefined>,\n): Promise<void> {\n const tasks: Promise<void>[] = [];\n for (const item of items) {\n const reference = item ? pickerItemToMediaReference(item) : null;\n if (reference) {\n tasks.push(\n uploader.deleteMedia(reference).catch(() => {\n /* ignore cleanup failures */\n }),\n );\n }\n }\n await Promise.all(tasks);\n}\n\nexport function cleanupPickerUploads(\n uploader: AXUploaderService,\n items: Iterable<AXConversationComposerPickerUploadItem | null | undefined>,\n options?: AXConversationCleanupPickerUploadsOptions,\n): void {\n abortPickerUploads(items);\n if (!options?.retainUploaded) {\n void deleteUploadedPickerMedia(uploader, items);\n }\n revokePickerBlobPreviews(items);\n}\n\nexport function revokePickerBlobPreviews(items: Iterable<{ preview?: string } | null | undefined>): void {\n if (typeof URL === 'undefined' || typeof URL.revokeObjectURL !== 'function') {\n return;\n }\n for (const item of items) {\n const preview = item?.preview;\n if (preview?.startsWith('blob:')) {\n URL.revokeObjectURL(preview);\n }\n }\n}\n\nexport function isUploadAborted(error: unknown, signal?: AbortSignal): boolean {\n if (signal?.aborted) {\n return true;\n }\n const code = (error as { code?: string } | null)?.code;\n return code === 'UPLOAD_ABORTED' || code === 'RESOLVE_ABORTED';\n}\n\nexport function limitFilesToCapacity(files: File[], currentCount: number, maxFiles: number): File[] {\n return files.slice(0, Math.max(0, maxFiles - currentCount));\n}\n\nexport async function uploadPickerFile(\n uploader: AXUploaderService,\n conversationId: string,\n file: File,\n onProgress: (percent: number) => void,\n signal?: AbortSignal,\n): Promise<{ url: string; mediaId?: string; mimeType: string; size: number }> {\n return uploader.upload({\n source: file,\n fileName: file.name,\n mimeType: file.type,\n onProgress,\n signal,\n metadata: { conversationId },\n });\n}\n\nexport function notifyMaxFilesCapacityExceeded(\n toast: AXToastService,\n translation: AXTranslationService,\n maxFiles: number,\n): void {\n toast.warning(\n translation.translateSync('@acorex:chat.errors.max-files-per-message', {\n params: { count: maxFiles },\n }),\n );\n}\n\n/** User-facing message for a file validation failure. */\nexport function formatPickerValidationMessage(\n translation: AXTranslationService,\n file: File,\n error: AXValidationRuleResult | undefined,\n): string {\n if (!error) {\n return translation.translateSync('@acorex:chat.errors.upload-failed');\n }\n\n const maxSize = typeof error['maxSize'] === 'number' ? error['maxSize'] : undefined;\n const minSize = typeof error['minSize'] === 'number' ? error['minSize'] : undefined;\n\n switch (error.rule) {\n case 'fileMaxSize':\n return translation.translateSync('@acorex:chat.errors.file-size-exceeds-limit', {\n params: {\n fileName: file.name,\n fileSize: formatFileSizeBytes(file.size),\n maxSize: formatFileSizeBytes(maxSize ?? 0),\n },\n });\n case 'fileMinSize':\n return translation.translateSync('@acorex:chat.errors.file-below-minimum-size', {\n params: {\n fileName: file.name,\n minSize: formatFileSizeBytes(minSize ?? 0),\n },\n });\n case 'fileMimeType':\n return translation.translateSync('@acorex:chat.errors.file-type-not-allowed', {\n params: {\n fileName: file.name,\n fileType: (file.type || 'unknown').trim() || 'unknown',\n },\n });\n default:\n return error.message?.trim()\n ? file.name\n ? `${file.name}: ${error.message}`\n : error.message\n : translation.translateSync('@acorex:chat.errors.upload-failed');\n }\n}\n\n/** Show a warning toast for each failed validation rule result. */\nexport function notifyPickerValidationErrors(\n toast: AXToastService,\n translation: AXTranslationService,\n rejected: { file: File; errors: AXValidationRuleResult[] }[],\n): void {\n for (const { file, errors } of rejected) {\n toast.warning(formatPickerValidationMessage(translation, file, errors[0]));\n }\n}\n\n/** Load files dropped onto the message list after the picker is mounted in a popup. */\nexport function seedPickerInitialFiles(\n initialFiles: InputSignal<File[]>,\n handleFiles: (files: File[]) => void | Promise<void>,\n): void {\n afterNextRender(() => {\n const files = initialFiles();\n if (files.length > 0) {\n void handleFiles([...files]);\n }\n });\n}\n\n/** Close inline composer shell or popup host after send/cancel. */\nexport function dismissComposerPickerHost(\n composerService: AXConversationComposerService,\n popupRef?: AXPopupRef | null,\n): void {\n if (popupRef) {\n popupRef.close();\n return;\n }\n composerService.hideComponent();\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { inject, DestroyRef, effect } from '@angular/core';
|
|
2
|
+
import { i as isMessageDeliveryPending } from './acorex-components-conversation-message-renderer-state.component-Dp-JdHWr.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Keeps media renderers from showing load errors while a message is still uploading.
|
|
6
|
+
*/
|
|
7
|
+
function bindMediaRendererContentState(message, contentState, mediaVersion) {
|
|
8
|
+
const destroyRef = inject(DestroyRef);
|
|
9
|
+
const stopSendingGuard = effect(() => {
|
|
10
|
+
if (isMessageDeliveryPending(message().status)) {
|
|
11
|
+
contentState.set('ready');
|
|
12
|
+
}
|
|
13
|
+
}, ...(ngDevMode ? [{ debugName: "stopSendingGuard" }] : /* istanbul ignore next */ []));
|
|
14
|
+
const stopMediaReset = effect(() => {
|
|
15
|
+
message().id;
|
|
16
|
+
mediaVersion();
|
|
17
|
+
contentState.set('ready');
|
|
18
|
+
}, ...(ngDevMode ? [{ debugName: "stopMediaReset" }] : /* istanbul ignore next */ []));
|
|
19
|
+
destroyRef.onDestroy(() => {
|
|
20
|
+
stopSendingGuard.destroy();
|
|
21
|
+
stopMediaReset.destroy();
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function reportMediaLoadError(message, contentState) {
|
|
25
|
+
if (isMessageDeliveryPending(message.status)) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
contentState.set('error');
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function buildPlaybackBannerPayload(inputs) {
|
|
33
|
+
const payload = {
|
|
34
|
+
title: inputs.title,
|
|
35
|
+
currentTime: inputs.currentTime ?? 0,
|
|
36
|
+
duration: inputs.duration ?? 0,
|
|
37
|
+
isPlaying: inputs.isPlaying ?? false,
|
|
38
|
+
onTogglePlay: inputs.onTogglePlay,
|
|
39
|
+
onClose: inputs.onClose,
|
|
40
|
+
};
|
|
41
|
+
if (inputs.playbackSpeed !== undefined) {
|
|
42
|
+
payload['playbackSpeed'] = inputs.playbackSpeed;
|
|
43
|
+
}
|
|
44
|
+
if (inputs.onCycleSpeed) {
|
|
45
|
+
payload['onCycleSpeed'] = inputs.onCycleSpeed;
|
|
46
|
+
}
|
|
47
|
+
if (inputs.showSpeedControl !== undefined) {
|
|
48
|
+
payload['showSpeedControl'] = inputs.showSpeedControl;
|
|
49
|
+
}
|
|
50
|
+
return payload;
|
|
51
|
+
}
|
|
52
|
+
/** Push playback state to the info-bar banner for a message. */
|
|
53
|
+
function syncPlaybackInfoBarBanner(infoBar, message, inputs, options) {
|
|
54
|
+
const forceOpen = options?.forceOpen ?? false;
|
|
55
|
+
const shouldOpen = forceOpen || inputs.isPlaying || infoBar.isMessageBannerActive(message.id);
|
|
56
|
+
if (!shouldOpen) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const payload = buildPlaybackBannerPayload(inputs);
|
|
60
|
+
if (infoBar.isMessageBannerActive(message.id)) {
|
|
61
|
+
infoBar.updateMessageBannerInputs(message.id, payload);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
infoBar.showMessageBanner(message, payload);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { bindMediaRendererContentState as b, reportMediaLoadError as r, syncPlaybackInfoBarBanner as s };
|
|
68
|
+
//# sourceMappingURL=acorex-components-conversation-playback-banner-sync-D8FFKde6.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acorex-components-conversation-playback-banner-sync-D8FFKde6.mjs","sources":["../../../../packages/components/conversation/src/lib/plugins/message-list/components/shared/media-renderer-content-state.ts","../../../../packages/components/conversation/src/lib/plugins/message-list/components/shared/playback-banner-sync.ts"],"sourcesContent":["import { DestroyRef, effect, inject, type WritableSignal } from '@angular/core';\nimport type { InputSignal } from '@angular/core';\nimport type { AXConversationMessage } from '../../../../models';\nimport type { AXConversationMessageRendererContentState } from '../../../../registries/message-renderer.registry';\nimport { isMessageDeliveryPending } from './message-renderer-state.helpers';\n\n/**\n * Keeps media renderers from showing load errors while a message is still uploading.\n */\nexport function bindMediaRendererContentState(\n message: InputSignal<AXConversationMessage>,\n contentState: WritableSignal<AXConversationMessageRendererContentState>,\n mediaVersion: () => unknown,\n): void {\n const destroyRef = inject(DestroyRef);\n\n const stopSendingGuard = effect(() => {\n if (isMessageDeliveryPending(message().status)) {\n contentState.set('ready');\n }\n });\n\n const stopMediaReset = effect(() => {\n message().id;\n mediaVersion();\n contentState.set('ready');\n });\n\n destroyRef.onDestroy(() => {\n stopSendingGuard.destroy();\n stopMediaReset.destroy();\n });\n}\n\nexport function reportMediaLoadError(\n message: AXConversationMessage,\n contentState: WritableSignal<AXConversationMessageRendererContentState>,\n): boolean {\n if (isMessageDeliveryPending(message.status)) {\n return false;\n }\n contentState.set('error');\n return true;\n}\n","import type { AXConversationMessage } from '../../../../models';\nimport type { AXConversationInfoBarService } from '../../../../components/info-bar/info-bar.service';\nimport type { AXConversationPlaybackBannerInputs } from './media-playback-info-bar-banner.component';\n\nfunction buildPlaybackBannerPayload(inputs: AXConversationPlaybackBannerInputs): Record<string, unknown> {\n const payload: Record<string, unknown> = {\n title: inputs.title,\n currentTime: inputs.currentTime ?? 0,\n duration: inputs.duration ?? 0,\n isPlaying: inputs.isPlaying ?? false,\n onTogglePlay: inputs.onTogglePlay,\n onClose: inputs.onClose,\n };\n\n if (inputs.playbackSpeed !== undefined) {\n payload['playbackSpeed'] = inputs.playbackSpeed;\n }\n if (inputs.onCycleSpeed) {\n payload['onCycleSpeed'] = inputs.onCycleSpeed;\n }\n if (inputs.showSpeedControl !== undefined) {\n payload['showSpeedControl'] = inputs.showSpeedControl;\n }\n\n return payload;\n}\n\n/** Push playback state to the info-bar banner for a message. */\nexport function syncPlaybackInfoBarBanner(\n infoBar: AXConversationInfoBarService,\n message: AXConversationMessage,\n inputs: AXConversationPlaybackBannerInputs,\n options?: { forceOpen?: boolean },\n): void {\n const forceOpen = options?.forceOpen ?? false;\n const shouldOpen =\n forceOpen || inputs.isPlaying || infoBar.isMessageBannerActive(message.id);\n\n if (!shouldOpen) {\n return;\n }\n\n const payload = buildPlaybackBannerPayload(inputs);\n\n if (infoBar.isMessageBannerActive(message.id)) {\n infoBar.updateMessageBannerInputs(message.id, payload);\n return;\n }\n\n infoBar.showMessageBanner(message, payload);\n}\n"],"names":[],"mappings":";;;AAMA;;AAEG;SACa,6BAA6B,CAC3C,OAA2C,EAC3C,YAAuE,EACvE,YAA2B,EAAA;AAE3B,IAAA,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;AAErC,IAAA,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAK;QACnC,IAAI,wBAAwB,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE;AAC9C,YAAA,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC;QAC3B;AACF,IAAA,CAAC,uFAAC;AAEF,IAAA,MAAM,cAAc,GAAG,MAAM,CAAC,MAAK;QACjC,OAAO,EAAE,CAAC,EAAE;AACZ,QAAA,YAAY,EAAE;AACd,QAAA,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC;AAC3B,IAAA,CAAC,qFAAC;AAEF,IAAA,UAAU,CAAC,SAAS,CAAC,MAAK;QACxB,gBAAgB,CAAC,OAAO,EAAE;QAC1B,cAAc,CAAC,OAAO,EAAE;AAC1B,IAAA,CAAC,CAAC;AACJ;AAEM,SAAU,oBAAoB,CAClC,OAA8B,EAC9B,YAAuE,EAAA;AAEvE,IAAA,IAAI,wBAAwB,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;AAC5C,QAAA,OAAO,KAAK;IACd;AACA,IAAA,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC;AACzB,IAAA,OAAO,IAAI;AACb;;ACvCA,SAAS,0BAA0B,CAAC,MAA0C,EAAA;AAC5E,IAAA,MAAM,OAAO,GAA4B;QACvC,KAAK,EAAE,MAAM,CAAC,KAAK;AACnB,QAAA,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,CAAC;AACpC,QAAA,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,CAAC;AAC9B,QAAA,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,KAAK;QACpC,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,OAAO,EAAE,MAAM,CAAC,OAAO;KACxB;AAED,IAAA,IAAI,MAAM,CAAC,aAAa,KAAK,SAAS,EAAE;AACtC,QAAA,OAAO,CAAC,eAAe,CAAC,GAAG,MAAM,CAAC,aAAa;IACjD;AACA,IAAA,IAAI,MAAM,CAAC,YAAY,EAAE;AACvB,QAAA,OAAO,CAAC,cAAc,CAAC,GAAG,MAAM,CAAC,YAAY;IAC/C;AACA,IAAA,IAAI,MAAM,CAAC,gBAAgB,KAAK,SAAS,EAAE;AACzC,QAAA,OAAO,CAAC,kBAAkB,CAAC,GAAG,MAAM,CAAC,gBAAgB;IACvD;AAEA,IAAA,OAAO,OAAO;AAChB;AAEA;AACM,SAAU,yBAAyB,CACvC,OAAqC,EACrC,OAA8B,EAC9B,MAA0C,EAC1C,OAAiC,EAAA;AAEjC,IAAA,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,IAAI,KAAK;AAC7C,IAAA,MAAM,UAAU,GACd,SAAS,IAAI,MAAM,CAAC,SAAS,IAAI,OAAO,CAAC,qBAAqB,CAAC,OAAO,CAAC,EAAE,CAAC;IAE5E,IAAI,CAAC,UAAU,EAAE;QACf;IACF;AAEA,IAAA,MAAM,OAAO,GAAG,0BAA0B,CAAC,MAAM,CAAC;IAElD,IAAI,OAAO,CAAC,qBAAqB,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE;QAC7C,OAAO,CAAC,yBAAyB,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC;QACtD;IACF;AAEA,IAAA,OAAO,CAAC,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC;AAC7C;;;;"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared layout tokens for conversation message renderers.
|
|
3
|
+
*/
|
|
4
|
+
const AX_CONVERSATION_RENDERER_SHARED_STYLES = `
|
|
5
|
+
:host {
|
|
6
|
+
display: block;
|
|
7
|
+
max-width: 100%;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.ax-conversation-msg {
|
|
11
|
+
display: flex;
|
|
12
|
+
flex-direction: column;
|
|
13
|
+
gap: 0.375rem;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.ax-conversation-msg__stack {
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
gap: 0.375rem;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.ax-conversation-msg__grid {
|
|
23
|
+
display: grid;
|
|
24
|
+
gap: 2px;
|
|
25
|
+
width: 100%;
|
|
26
|
+
border-radius: var(--ax-sys-border-radius, 0.5rem);
|
|
27
|
+
overflow: hidden;
|
|
28
|
+
background: rgb(var(--ax-sys-color-border-light-surface));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.ax-conversation-msg__grid--single {
|
|
32
|
+
grid-template-columns: 1fr;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.ax-conversation-msg__grid--single ::ng-deep .ax-conversation-image-attachment {
|
|
36
|
+
max-height: min(50dvh, 20rem);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.ax-conversation-msg__grid--dual {
|
|
40
|
+
grid-template-columns: repeat(2, 1fr);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.ax-conversation-msg__grid--multi {
|
|
44
|
+
grid-template-columns: repeat(3, 1fr);
|
|
45
|
+
grid-auto-rows: 10rem;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@container (max-width: 380px) {
|
|
49
|
+
.ax-conversation-msg__grid--multi {
|
|
50
|
+
grid-template-columns: repeat(2, 1fr);
|
|
51
|
+
grid-auto-rows: 10rem;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.ax-conversation-msg__caption {
|
|
56
|
+
margin: 0;
|
|
57
|
+
padding: 0 0.125rem;
|
|
58
|
+
font-size: 0.8125rem;
|
|
59
|
+
line-height: 1.4;
|
|
60
|
+
word-break: break-word;
|
|
61
|
+
opacity: 0.88;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* Flush layout: media fills the message bubble edge-to-edge (image / video). */
|
|
65
|
+
.ax-conversation-msg--flush {
|
|
66
|
+
width: 100%;
|
|
67
|
+
max-width: 100%;
|
|
68
|
+
gap: 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.ax-conversation-msg--flush .ax-conversation-msg__caption {
|
|
72
|
+
padding: 0.375rem 0.625rem 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.ax-conversation-msg--flush .ax-conversation-msg__grid {
|
|
76
|
+
border-radius: 0;
|
|
77
|
+
background: transparent;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.ax-conversation-msg--flush .ax-conversation-msg__stack {
|
|
81
|
+
gap: 2px;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.ax-conversation-msg--flush ::ng-deep .ax-conversation-video {
|
|
85
|
+
border-radius: 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.ax-conversation-msg__card {
|
|
89
|
+
border-radius: var(--ax-sys-border-radius, 0.5rem);
|
|
90
|
+
border: 1px solid rgb(var(--ax-sys-color-border-light-surface));
|
|
91
|
+
background: rgb(var(--ax-sys-color-surface));
|
|
92
|
+
overflow: hidden;
|
|
93
|
+
}
|
|
94
|
+
`;
|
|
95
|
+
//# sourceMappingURL=acorex-components-conversation-renderer-shared.styles-Digah3Zn.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acorex-components-conversation-renderer-shared.styles-Digah3Zn.mjs","sources":["../../../../packages/components/conversation/src/lib/plugins/message-list/components/shared/renderer-shared.styles.ts"],"sourcesContent":["/**\n * Shared layout tokens for conversation message renderers.\n */\nexport const AX_CONVERSATION_RENDERER_SHARED_STYLES = `\n :host {\n display: block;\n max-width: 100%;\n }\n\n .ax-conversation-msg {\n display: flex;\n flex-direction: column;\n gap: 0.375rem;\n }\n\n .ax-conversation-msg__stack {\n display: flex;\n flex-direction: column;\n gap: 0.375rem;\n }\n\n .ax-conversation-msg__grid {\n display: grid;\n gap: 2px;\n width: 100%;\n border-radius: var(--ax-sys-border-radius, 0.5rem);\n overflow: hidden;\n background: rgb(var(--ax-sys-color-border-light-surface));\n }\n\n .ax-conversation-msg__grid--single {\n grid-template-columns: 1fr;\n }\n\n .ax-conversation-msg__grid--single ::ng-deep .ax-conversation-image-attachment {\n max-height: min(50dvh, 20rem);\n }\n\n .ax-conversation-msg__grid--dual {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .ax-conversation-msg__grid--multi {\n grid-template-columns: repeat(3, 1fr);\n grid-auto-rows: 10rem;\n }\n\n @container (max-width: 380px) {\n .ax-conversation-msg__grid--multi {\n grid-template-columns: repeat(2, 1fr);\n grid-auto-rows: 10rem;\n }\n }\n\n .ax-conversation-msg__caption {\n margin: 0;\n padding: 0 0.125rem;\n font-size: 0.8125rem;\n line-height: 1.4;\n word-break: break-word;\n opacity: 0.88;\n }\n\n /* Flush layout: media fills the message bubble edge-to-edge (image / video). */\n .ax-conversation-msg--flush {\n width: 100%;\n max-width: 100%;\n gap: 0;\n }\n\n .ax-conversation-msg--flush .ax-conversation-msg__caption {\n padding: 0.375rem 0.625rem 0;\n }\n\n .ax-conversation-msg--flush .ax-conversation-msg__grid {\n border-radius: 0;\n background: transparent;\n }\n\n .ax-conversation-msg--flush .ax-conversation-msg__stack {\n gap: 2px;\n }\n\n .ax-conversation-msg--flush ::ng-deep .ax-conversation-video {\n border-radius: 0;\n }\n\n .ax-conversation-msg__card {\n border-radius: var(--ax-sys-border-radius, 0.5rem);\n border: 1px solid rgb(var(--ax-sys-color-border-light-surface));\n background: rgb(var(--ax-sys-color-surface));\n overflow: hidden;\n }\n`;\n"],"names":[],"mappings":"AAAA;;AAEG;AACI,MAAM,sCAAsC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0FrD"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { AXButtonComponent } from '@acorex/components/button';
|
|
2
|
+
import { AXLabelComponent } from '@acorex/components/label';
|
|
3
|
+
import { AXSearchBoxComponent } from '@acorex/components/search-box';
|
|
4
|
+
import { isPlatformBrowser, CommonModule } from '@angular/common';
|
|
5
|
+
import * as i0 from '@angular/core';
|
|
6
|
+
import { inject, PLATFORM_ID, DestroyRef, input, computed, effect, Component } from '@angular/core';
|
|
7
|
+
import { m as AXConversationInfoBarService, A as AXConversationService } from './acorex-components-conversation-acorex-components-conversation-YhVa42TC.mjs';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Search Component for Info Bar
|
|
11
|
+
* Provides search functionality within conversations
|
|
12
|
+
*/
|
|
13
|
+
class AXConversationInfoBarSearchComponent {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.platformId = inject(PLATFORM_ID);
|
|
16
|
+
this.destroyRef = inject(DestroyRef);
|
|
17
|
+
this.infoBarService = inject(AXConversationInfoBarService);
|
|
18
|
+
this.conversationService = inject(AXConversationService);
|
|
19
|
+
/** Conversation input */
|
|
20
|
+
this.conversation = input.required(...(ngDevMode ? [{ debugName: "conversation" }] : /* istanbul ignore next */ []));
|
|
21
|
+
/** Service input */
|
|
22
|
+
this.service = input(...(ngDevMode ? [undefined, { debugName: "service" }] : /* istanbul ignore next */ []));
|
|
23
|
+
/** Search query from service */
|
|
24
|
+
this.searchQuery = this.infoBarService.searchQuery;
|
|
25
|
+
/** Results count from service */
|
|
26
|
+
this.resultsCount = this.infoBarService.searchResultsCount;
|
|
27
|
+
/** Current index from service */
|
|
28
|
+
this.currentIndex = this.infoBarService.currentSearchIndex;
|
|
29
|
+
/** Messages from conversation */
|
|
30
|
+
this.messages = computed(() => {
|
|
31
|
+
// Get messages from active conversation
|
|
32
|
+
const activeConv = this.conversationService.activeConversation();
|
|
33
|
+
if (activeConv?.id === this.conversation().id) {
|
|
34
|
+
return this.conversationService.activeMessages();
|
|
35
|
+
}
|
|
36
|
+
return [];
|
|
37
|
+
}, ...(ngDevMode ? [{ debugName: "messages" }] : /* istanbul ignore next */ []));
|
|
38
|
+
effect(() => {
|
|
39
|
+
const query = this.searchQuery();
|
|
40
|
+
if (query) {
|
|
41
|
+
this.performSearch(query);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
this.infoBarService.setSearchResults(0, 0);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
this.destroyRef.onDestroy(() => {
|
|
48
|
+
if (this.scrollTimer)
|
|
49
|
+
clearTimeout(this.scrollTimer);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/** Handle search input change */
|
|
53
|
+
onSearchChange(value) {
|
|
54
|
+
this.infoBarService.setSearchQuery(value);
|
|
55
|
+
}
|
|
56
|
+
/** Perform search in messages */
|
|
57
|
+
performSearch(query) {
|
|
58
|
+
const messages = this.messages();
|
|
59
|
+
const lowerQuery = query.toLowerCase();
|
|
60
|
+
const results = messages.filter((msg) => {
|
|
61
|
+
if (msg.type === 'text') {
|
|
62
|
+
const text = msg.payload?.text || '';
|
|
63
|
+
return text.toLowerCase().includes(lowerQuery);
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
});
|
|
67
|
+
this.infoBarService.setSearchResults(results.length, 0);
|
|
68
|
+
// Scroll to first result if exists
|
|
69
|
+
if (results.length > 0) {
|
|
70
|
+
this.scrollToResult(results[0].id);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** Navigate to next result */
|
|
74
|
+
onNext() {
|
|
75
|
+
this.infoBarService.nextSearchResult();
|
|
76
|
+
this.scrollToCurrentResult();
|
|
77
|
+
}
|
|
78
|
+
/** Navigate to previous result */
|
|
79
|
+
onPrevious() {
|
|
80
|
+
this.infoBarService.previousSearchResult();
|
|
81
|
+
this.scrollToCurrentResult();
|
|
82
|
+
}
|
|
83
|
+
/** Scroll to current search result */
|
|
84
|
+
scrollToCurrentResult() {
|
|
85
|
+
const query = this.searchQuery();
|
|
86
|
+
const currentIdx = this.currentIndex();
|
|
87
|
+
const messages = this.messages();
|
|
88
|
+
const lowerQuery = query.toLowerCase();
|
|
89
|
+
// Find the nth matching message
|
|
90
|
+
let matchCount = 0;
|
|
91
|
+
for (const msg of messages) {
|
|
92
|
+
if (msg.type === 'text') {
|
|
93
|
+
const text = msg.payload?.text || '';
|
|
94
|
+
if (text.toLowerCase().includes(lowerQuery)) {
|
|
95
|
+
if (matchCount === currentIdx) {
|
|
96
|
+
this.scrollToResult(msg.id);
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
matchCount++;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
scrollToResult(messageId) {
|
|
105
|
+
if (!isPlatformBrowser(this.platformId))
|
|
106
|
+
return;
|
|
107
|
+
if (this.scrollTimer)
|
|
108
|
+
clearTimeout(this.scrollTimer);
|
|
109
|
+
this.scrollTimer = setTimeout(() => {
|
|
110
|
+
const element = document.querySelector(`[data-message-id="${messageId}"]`);
|
|
111
|
+
if (element) {
|
|
112
|
+
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
113
|
+
}
|
|
114
|
+
}, 100);
|
|
115
|
+
}
|
|
116
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXConversationInfoBarSearchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
117
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXConversationInfoBarSearchComponent, isStandalone: true, selector: "ax-conversation-info-bar-search", inputs: { conversation: { classPropertyName: "conversation", publicName: "conversation", isSignal: true, isRequired: true, transformFunction: null }, service: { classPropertyName: "service", publicName: "service", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
118
|
+
<div class="search-wrapper">
|
|
119
|
+
<ax-search-box
|
|
120
|
+
class="search-input"
|
|
121
|
+
[placeholder]="'Search in conversation'"
|
|
122
|
+
[value]="searchQuery()"
|
|
123
|
+
(onValueChanged)="onSearchChange($event.value)"
|
|
124
|
+
></ax-search-box>
|
|
125
|
+
|
|
126
|
+
@if (searchQuery() && resultsCount() > 0) {
|
|
127
|
+
<div class="search-results">
|
|
128
|
+
<ax-label class="results-text"> {{ currentIndex() + 1 }} / {{ resultsCount() }} </ax-label>
|
|
129
|
+
<ax-button
|
|
130
|
+
class="nav-button"
|
|
131
|
+
[text]="'↑'"
|
|
132
|
+
[look]="'ghost'"
|
|
133
|
+
[size]="'sm'"
|
|
134
|
+
(onClick)="onPrevious()"
|
|
135
|
+
[disabled]="resultsCount() === 0"
|
|
136
|
+
></ax-button>
|
|
137
|
+
<ax-button
|
|
138
|
+
class="nav-button"
|
|
139
|
+
[text]="'↓'"
|
|
140
|
+
[look]="'ghost'"
|
|
141
|
+
[size]="'sm'"
|
|
142
|
+
(onClick)="onNext()"
|
|
143
|
+
[disabled]="resultsCount() === 0"
|
|
144
|
+
></ax-button>
|
|
145
|
+
</div>
|
|
146
|
+
}
|
|
147
|
+
</div>
|
|
148
|
+
`, isInline: true, styles: [":host{display:block;flex:1;min-width:0}.search-wrapper{display:flex;align-items:center;gap:.5rem;width:100%}.search-input{flex:1;min-width:0}.search-results{display:flex;align-items:center;gap:.25rem;flex-shrink:0}.results-text{font-size:.75rem;color:var(--ax-text-secondary, #6b7280);white-space:nowrap}.nav-button{min-width:28px;height:28px;padding:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: AXSearchBoxComponent, selector: "ax-search-box", inputs: ["disabled", "readonly", "tabIndex", "placeholder", "value", "state", "name", "id", "look", "class", "delayTime", "type", "autoSearch"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: AXLabelComponent, selector: "ax-label", inputs: ["required", "for"], outputs: ["requiredChange"] }] }); }
|
|
149
|
+
}
|
|
150
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXConversationInfoBarSearchComponent, decorators: [{
|
|
151
|
+
type: Component,
|
|
152
|
+
args: [{ selector: 'ax-conversation-info-bar-search', imports: [CommonModule, AXSearchBoxComponent, AXButtonComponent, AXLabelComponent], template: `
|
|
153
|
+
<div class="search-wrapper">
|
|
154
|
+
<ax-search-box
|
|
155
|
+
class="search-input"
|
|
156
|
+
[placeholder]="'Search in conversation'"
|
|
157
|
+
[value]="searchQuery()"
|
|
158
|
+
(onValueChanged)="onSearchChange($event.value)"
|
|
159
|
+
></ax-search-box>
|
|
160
|
+
|
|
161
|
+
@if (searchQuery() && resultsCount() > 0) {
|
|
162
|
+
<div class="search-results">
|
|
163
|
+
<ax-label class="results-text"> {{ currentIndex() + 1 }} / {{ resultsCount() }} </ax-label>
|
|
164
|
+
<ax-button
|
|
165
|
+
class="nav-button"
|
|
166
|
+
[text]="'↑'"
|
|
167
|
+
[look]="'ghost'"
|
|
168
|
+
[size]="'sm'"
|
|
169
|
+
(onClick)="onPrevious()"
|
|
170
|
+
[disabled]="resultsCount() === 0"
|
|
171
|
+
></ax-button>
|
|
172
|
+
<ax-button
|
|
173
|
+
class="nav-button"
|
|
174
|
+
[text]="'↓'"
|
|
175
|
+
[look]="'ghost'"
|
|
176
|
+
[size]="'sm'"
|
|
177
|
+
(onClick)="onNext()"
|
|
178
|
+
[disabled]="resultsCount() === 0"
|
|
179
|
+
></ax-button>
|
|
180
|
+
</div>
|
|
181
|
+
}
|
|
182
|
+
</div>
|
|
183
|
+
`, styles: [":host{display:block;flex:1;min-width:0}.search-wrapper{display:flex;align-items:center;gap:.5rem;width:100%}.search-input{flex:1;min-width:0}.search-results{display:flex;align-items:center;gap:.25rem;flex-shrink:0}.results-text{font-size:.75rem;color:var(--ax-text-secondary, #6b7280);white-space:nowrap}.nav-button{min-width:28px;height:28px;padding:0}\n"] }]
|
|
184
|
+
}], ctorParameters: () => [], propDecorators: { conversation: [{ type: i0.Input, args: [{ isSignal: true, alias: "conversation", required: true }] }], service: [{ type: i0.Input, args: [{ isSignal: true, alias: "service", required: false }] }] } });
|
|
185
|
+
|
|
186
|
+
export { AXConversationInfoBarSearchComponent };
|
|
187
|
+
//# sourceMappingURL=acorex-components-conversation-search.component-B-nc6xiA.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acorex-components-conversation-search.component-B-nc6xiA.mjs","sources":["../../../../packages/components/conversation/src/lib/plugins/info-bar/components/search.component.ts"],"sourcesContent":["/**\n * Search Component for Info Bar\n * Provides search functionality within conversations\n */\n\nimport { AXButtonComponent } from '@acorex/components/button';\nimport { AXLabelComponent } from '@acorex/components/label';\nimport { AXSearchBoxComponent } from '@acorex/components/search-box';\nimport { CommonModule, isPlatformBrowser } from '@angular/common';\nimport { Component, DestroyRef, PLATFORM_ID, computed, effect, inject, input } from '@angular/core';\nimport { AXConversationInfoBarService } from '../../../components/info-bar/info-bar.service';\nimport type { AXConversation, AXConversationTextPayload } from '../../../models';\nimport { AXConversationService } from '../../../services/conversation.service';\n\n@Component({\n selector: 'ax-conversation-info-bar-search',\n imports: [CommonModule, AXSearchBoxComponent, AXButtonComponent, AXLabelComponent],\n template: `\n <div class=\"search-wrapper\">\n <ax-search-box\n class=\"search-input\"\n [placeholder]=\"'Search in conversation'\"\n [value]=\"searchQuery()\"\n (onValueChanged)=\"onSearchChange($event.value)\"\n ></ax-search-box>\n\n @if (searchQuery() && resultsCount() > 0) {\n <div class=\"search-results\">\n <ax-label class=\"results-text\"> {{ currentIndex() + 1 }} / {{ resultsCount() }} </ax-label>\n <ax-button\n class=\"nav-button\"\n [text]=\"'↑'\"\n [look]=\"'ghost'\"\n [size]=\"'sm'\"\n (onClick)=\"onPrevious()\"\n [disabled]=\"resultsCount() === 0\"\n ></ax-button>\n <ax-button\n class=\"nav-button\"\n [text]=\"'↓'\"\n [look]=\"'ghost'\"\n [size]=\"'sm'\"\n (onClick)=\"onNext()\"\n [disabled]=\"resultsCount() === 0\"\n ></ax-button>\n </div>\n }\n </div>\n `,\n styles: [\n `\n :host {\n display: block;\n flex: 1;\n min-width: 0;\n }\n\n .search-wrapper {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n width: 100%;\n }\n\n .search-input {\n flex: 1;\n min-width: 0;\n }\n\n .search-results {\n display: flex;\n align-items: center;\n gap: 0.25rem;\n flex-shrink: 0;\n }\n\n .results-text {\n font-size: 0.75rem;\n color: var(--ax-text-secondary, #6b7280);\n white-space: nowrap;\n }\n\n .nav-button {\n min-width: 28px;\n height: 28px;\n padding: 0;\n }\n `,\n ],\n})\nexport class AXConversationInfoBarSearchComponent {\n private readonly platformId = inject(PLATFORM_ID);\n private readonly destroyRef = inject(DestroyRef);\n private readonly infoBarService = inject(AXConversationInfoBarService);\n private readonly conversationService = inject(AXConversationService);\n\n /** Conversation input */\n readonly conversation = input.required<AXConversation>();\n\n /** Service input */\n readonly service = input<AXConversationInfoBarService>();\n\n /** Search query from service */\n readonly searchQuery = this.infoBarService.searchQuery;\n\n /** Results count from service */\n readonly resultsCount = this.infoBarService.searchResultsCount;\n\n /** Current index from service */\n readonly currentIndex = this.infoBarService.currentSearchIndex;\n\n /** Messages from conversation */\n readonly messages = computed(() => {\n // Get messages from active conversation\n const activeConv = this.conversationService.activeConversation();\n if (activeConv?.id === this.conversation().id) {\n return this.conversationService.activeMessages();\n }\n return [];\n });\n\n private scrollTimer?: ReturnType<typeof setTimeout>;\n\n constructor() {\n effect(() => {\n const query = this.searchQuery();\n if (query) {\n this.performSearch(query);\n } else {\n this.infoBarService.setSearchResults(0, 0);\n }\n });\n\n this.destroyRef.onDestroy(() => {\n if (this.scrollTimer) clearTimeout(this.scrollTimer);\n });\n }\n\n /** Handle search input change */\n onSearchChange(value: string): void {\n this.infoBarService.setSearchQuery(value);\n }\n\n /** Perform search in messages */\n private performSearch(query: string): void {\n const messages = this.messages();\n const lowerQuery = query.toLowerCase();\n\n const results = messages.filter((msg) => {\n if (msg.type === 'text') {\n const text = (msg.payload as AXConversationTextPayload)?.text || '';\n return text.toLowerCase().includes(lowerQuery);\n }\n return false;\n });\n\n this.infoBarService.setSearchResults(results.length, 0);\n\n // Scroll to first result if exists\n if (results.length > 0) {\n this.scrollToResult(results[0].id);\n }\n }\n\n /** Navigate to next result */\n onNext(): void {\n this.infoBarService.nextSearchResult();\n this.scrollToCurrentResult();\n }\n\n /** Navigate to previous result */\n onPrevious(): void {\n this.infoBarService.previousSearchResult();\n this.scrollToCurrentResult();\n }\n\n /** Scroll to current search result */\n private scrollToCurrentResult(): void {\n const query = this.searchQuery();\n const currentIdx = this.currentIndex();\n const messages = this.messages();\n const lowerQuery = query.toLowerCase();\n\n // Find the nth matching message\n let matchCount = 0;\n for (const msg of messages) {\n if (msg.type === 'text') {\n const text = (msg.payload as AXConversationTextPayload)?.text || '';\n if (text.toLowerCase().includes(lowerQuery)) {\n if (matchCount === currentIdx) {\n this.scrollToResult(msg.id);\n break;\n }\n matchCount++;\n }\n }\n }\n }\n\n private scrollToResult(messageId: string): void {\n if (!isPlatformBrowser(this.platformId)) return;\n\n if (this.scrollTimer) clearTimeout(this.scrollTimer);\n this.scrollTimer = setTimeout(() => {\n const element = document.querySelector(`[data-message-id=\"${messageId}\"]`);\n if (element) {\n element.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }\n }, 100);\n }\n}\n"],"names":[],"mappings":"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"}
|