Skip to content

Commit 763f9fd

Browse files
committed
Resizing functionality for the widget when the screen is splitted by an iframe
1 parent 862e7fd commit 763f9fd

1 file changed

Lines changed: 117 additions & 13 deletions

File tree

widget/components/chat/Chat.vue

Lines changed: 117 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
<template>
2-
<div class="chat-wrapper" :class="{ 'dark-mode': store.darkMode, 'fit-to-parent': store.fitToParent, 'stick-input-prompt': store.stickInputPrompt, 'split-screen': store.splitScreenIframe}" @click="store.menuOpened = false">
3-
<div class="right-content">
4-
<div class="conversation-content" ref="conversationContent" :class="{'dark-mode': store.darkMode, 'fit-to-parent-conversation-content': store.fitToParent}">
5-
<div class="stacks" :class="{'merge-to-prev': getFirstLayerMergeToPrev(message)}" v-for="(message, index) in store.messages">
2+
<div class="chat-wrapper"
3+
:class="{ 'dark-mode': store.darkMode, 'fit-to-parent': store.fitToParent, 'stick-input-prompt': store.stickInputPrompt, 'split-screen': store.splitScreenIframe}"
4+
@click="store.menuOpened = false">
5+
<div class="right-content" :style="rightContentStyle">
6+
<div class="conversation-content" ref="conversationContent"
7+
:class="{'dark-mode': store.darkMode, 'fit-to-parent-conversation-content': store.fitToParent}">
8+
<div class="stacks" :class="{'merge-to-prev': getFirstLayerMergeToPrev(message)}"
9+
v-for="(message, index) in store.messages">
610
<ChatMsgManager
711
v-if="isRenderableStackType(message)"
812
:message="message"
@@ -23,7 +27,14 @@
2327
</div>
2428
<ChatPrompt @send="(msg) => sendMessage(msg)"/>
2529
</div>
26-
<div v-if="store.splitScreenIframe" class="split-screen-iframe-container">
30+
31+
<div v-if="store.splitScreenIframe"
32+
class="resizable-divider"
33+
@mousedown="startResize"
34+
:class="{'dark-mode': store.darkMode}">
35+
</div>
36+
37+
<div v-if="store.splitScreenIframe" class="split-screen-iframe-container" :style="iframeContainerStyle">
2738
<iframe
2839
class="split-screen-iframe"
2940
:src="store.splitScreenIframe"
@@ -34,12 +45,12 @@
3445
</template>
3546

3647
<script setup>
37-
import {ref, watch, nextTick, onMounted} from "vue";
48+
import {ref, watch, nextTick, onMounted, computed, onUnmounted} from "vue";
3849
import {useGlobalStore} from "~/store";
3950
import LoaderMsg from "~/components/chat/LoaderMsg.vue";
4051
import ChatMsgManager from "~/components/chat/msgs/ChatMsgManager.vue";
4152
import ChatPrompt from "~/components/chat/ChatPrompt.vue";
42-
import { createTextMessage, createMessage } from "~/utils";
53+
import {createTextMessage, createMessage} from "~/utils";
4354
4455
const store = useGlobalStore();
4556
@@ -50,6 +61,31 @@ let notRenderableStackTypes = ["gtm_tag", "close_conversation", undefined]
5061
notRenderableStackTypes = notRenderableStackTypes.concat(store.notRenderableStackTypes)
5162
let ws = undefined
5263
64+
// --- Resizing functionality ---
65+
const rightContentWidth = ref( '100%' );
66+
const iframeContainerWidth = ref('0');
67+
const isResizing = ref(false);
68+
const startX = ref(0);
69+
const startRightWidth = ref(0);
70+
// Computed styles
71+
const rightContentStyle = computed(() => ({
72+
width: rightContentWidth.value
73+
}));
74+
75+
const iframeContainerStyle = computed(() => ({
76+
width: iframeContainerWidth.value,
77+
pointerEvents: isResizing.value ? 'none' : 'auto'
78+
}));
79+
80+
watch(() => store.splitScreenIframe, () => {
81+
if (store.splitScreenIframe) {
82+
rightContentWidth.value = '50%';
83+
iframeContainerWidth.value = '50%';
84+
} else {
85+
rightContentWidth.value = '100%';
86+
iframeContainerWidth.value = '0';
87+
}
88+
})
5389
watch(() => store.scrollToBottom, scrollConversationDown)
5490
watch(() => store.selectedPlConversationId, createConnection)
5591
watch(() => store.feedbackSent, animateFeedbackSent)
@@ -66,9 +102,11 @@ onMounted(async () => {
66102
function isLastOfType(index) {
67103
return index === store.messages.length - 1 || store.messages[index + 1].sender.type !== store.messages[index].sender.type
68104
}
105+
69106
function getFirstLayerMergeToPrev(message) {
70107
return message?.stack[0]?.payload.merge_to_prev;
71108
}
109+
72110
function scrollConversationDown() {
73111
nextTick(() => {
74112
conversationContent.value.scroll({top: conversationContent.value.scrollHeight, behavior: "smooth"})
@@ -85,12 +123,13 @@ function animateFeedbackSent() {
85123
feedbackSentDisabled.value = true
86124
}, 1500)
87125
}
126+
88127
function isRenderableStackType(message) {
89128
return !notRenderableStackTypes.includes(message.stack[0]?.type)
90129
}
91130
92131
function createConnection() {
93-
if(store.previewMode)
132+
if (store.previewMode)
94133
return
95134
96135
if (ws)
@@ -141,7 +180,7 @@ function createConnection() {
141180
142181
sendToGTM(msg)
143182
store.addMessage(msg);
144-
if(store.messages.length === 1)
183+
if (store.messages.length === 1)
145184
await store.gatherConversations()
146185
};
147186
ws.onopen = async function () {
@@ -166,7 +205,7 @@ function createConnection() {
166205
167206
168207
async function initializeConversation() {
169-
if(store.previewMode)
208+
if (store.previewMode)
170209
return
171210
172211
if (store.initialSelectedPlConversationId) {
@@ -194,7 +233,7 @@ function sendMessage(message) {
194233
}
195234
196235
function sendMessagesToBeSent() {
197-
if(!store.canSendMsg) {
236+
if (!store.canSendMsg) {
198237
setTimeout(() => sendMessagesToBeSent(), 1000)
199238
return
200239
}
@@ -221,9 +260,52 @@ function sendToGTM(msg) {
221260
console.warn("GTM tag received but no dataLayer found")
222261
}
223262
}
263+
224264
document.addEventListener("request-text-message-send", (ev) => sendMessage(createTextMessage("human", ev.detail)))
225265
document.addEventListener("request-message-send", (ev) => sendMessage(createMessage("human", ev.detail)))
226266
267+
// --- Resizing functionality ---
268+
// Resize methods
269+
function startResize(e) {
270+
isResizing.value = true;
271+
startX.value = e.clientX;
272+
startRightWidth.value = parseInt(rightContentWidth.value);
273+
274+
// Add event listeners
275+
document.addEventListener('mousemove', resize);
276+
document.addEventListener('mouseup', stopResize);
277+
278+
// Prevent text selection during resize
279+
// e.preventDefault();
280+
}
281+
282+
function resize(e) {
283+
if (!isResizing.value) return;
284+
285+
const chatWrapper = document.querySelector('.chat-wrapper');
286+
const totalWidth = chatWrapper.offsetWidth;
287+
288+
// Calculate the new width based on mouse movement
289+
const dx = e.clientX - startX.value;
290+
const newRightWidth = Math.max(20, Math.min(80, (startRightWidth.value + dx / totalWidth * 100)));
291+
292+
// Update the widths
293+
rightContentWidth.value = `${newRightWidth}%`;
294+
iframeContainerWidth.value = `${100 - newRightWidth}%`;
295+
}
296+
297+
function stopResize() {
298+
isResizing.value = false;
299+
document.removeEventListener('mousemove', resize);
300+
document.removeEventListener('mouseup', stopResize);
301+
}
302+
303+
// Clean up event listeners when component is unmounted
304+
onUnmounted(() => {
305+
document.removeEventListener('mousemove', resize);
306+
document.removeEventListener('mouseup', stopResize);
307+
});
308+
227309
</script>
228310
<style scoped lang="scss">
229311
.chat-wrapper {
@@ -235,31 +317,52 @@ document.addEventListener("request-message-send", (ev) => sendMessage(createMess
235317
display: flex;
236318
flex-direction: column;
237319
background-color: $chatfaq-color-chat-background-light;
320+
238321
&.split-screen {
239322
flex-direction: row;
240323
overflow: hidden !important;
241324
}
325+
242326
.right-content {
243327
display: flex;
244328
flex-direction: column;
245329
justify-content: space-between;
246330
height: 100%;
247-
width: 100%;
331+
// width: 100%;
332+
// transition: width 0.05s ease;
248333
249334
}
335+
336+
337+
.resizable-divider {
338+
width: 8px;
339+
opacity: 0;
340+
height: 100%;
341+
background-color: #f0f0f0;
342+
cursor: col-resize;
343+
display: flex;
344+
align-items: center;
345+
justify-content: center;
346+
z-index: 10;
347+
}
348+
250349
.split-screen-iframe-container {
251350
height: 100%;
252-
width: 100%;
351+
// width: 100%;
253352
box-shadow: 0px 2px 18px 0px #0000001A;
353+
// transition: width 0.05s ease;
254354
}
355+
255356
.split-screen-iframe {
256357
height: 100%;
257358
width: 100%;
258359
border: 0;
259360
}
361+
260362
&.dark-mode {
261363
background-color: $chatfaq-color-chat-background-dark;
262364
}
365+
263366
&.fit-to-parent {
264367
border: unset !important;
265368
border-radius: inherit !important;
@@ -316,6 +419,7 @@ document.addEventListener("request-message-send", (ev) => sendMessage(createMess
316419
&.dark-mode {
317420
@include scroll-style($chatfaq-color-scrollBar-dark);
318421
}
422+
319423
&.fit-to-parent-conversation-content {
320424
min-height: inherit;
321425
}

0 commit comments

Comments
 (0)