Skip to content

Commit b60ca93

Browse files
authored
Merge pull request #350 from iceljc/features/refine-chat-window
add utility description
2 parents 1282bc5 + 4e5b00c commit b60ca93

File tree

10 files changed

+403
-32
lines changed

10 files changed

+403
-32
lines changed

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"@fullcalendar/common": "^5.11.5",
4242
"@microsoft/signalr": "^8.0.0",
4343
"@pgrabovets/json-view": "^2.7.5",
44+
"@popperjs/core": "^2.11.8",
4445
"@sveltejs/adapter-static": "^3.0.0",
4546
"@sveltestrap/sveltestrap": "^6.2.3",
4647
"@twilio/voice-sdk": "^2.9.0",

src/lib/common/markdown/Markdown.svelte

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
<script>
2+
import { onMount } from 'svelte';
23
import { marked } from 'marked';
34
import { replaceMarkdown, replaceNewLine } from '$lib/helpers/http';
5+
import 'overlayscrollbars/overlayscrollbars.css';
6+
import { OverlayScrollbars } from 'overlayscrollbars';
7+
import { v4 as uuidv4 } from 'uuid';
48
59
/** @type {string} */
610
export let text;
@@ -14,6 +18,32 @@
1418
/** @type {boolean} */
1519
export let rawText = false;
1620
21+
const scrollbarId = uuidv4();
22+
23+
const options = {
24+
scrollbars: {
25+
visibility: 'auto',
26+
autoHide: 'move',
27+
autoHideDelay: 100,
28+
dragScroll: true,
29+
clickScroll: false,
30+
theme: 'os-theme-light',
31+
pointers: ['mouse', 'touch', 'pen']
32+
}
33+
};
34+
35+
onMount(() => {
36+
initScrollbar();
37+
});
38+
39+
function initScrollbar() {
40+
const elem = document.querySelector(`#markdown-scrollbar-${scrollbarId}`);
41+
if (elem) {
42+
// @ts-ignore
43+
const scrollbar = OverlayScrollbars(elem, options);
44+
}
45+
}
46+
1747
let innerText = '';
1848
$: {
1949
if (typeof text !== 'string') {
@@ -30,6 +60,7 @@
3060
</script>
3161
3262
<div
63+
id={`markdown-scrollbar-${scrollbarId}`}
3364
class={`markdown-container markdown-lite ${containerClasses || 'text-white'}`}
3465
style={`${containerStyles}`}
3566
>
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
<script>
2+
import { Portal } from '@sveltestrap/sveltestrap';
3+
import { onMount, onDestroy } from 'svelte';
4+
import { createPopper } from '@popperjs/core';
5+
import { v4 as uuidv4 } from 'uuid';
6+
import { classnames } from '$lib/helpers/utils/common';
7+
import { clickoutsideDirective } from "$lib/helpers/directives";
8+
9+
/**
10+
* Additional CSS class names for the tooltip.
11+
* @type {string}
12+
*/
13+
export let containerClasses = '';
14+
15+
/**
16+
* Flag to enable animation for the tooltip.
17+
* @type {boolean}
18+
*/
19+
export let animation = true;
20+
21+
/**
22+
* Unique identifier for the tooltip.
23+
* @type {string}
24+
*/
25+
export let id = `tooltip_${uuidv4()}`;
26+
27+
/**
28+
* Controls the visibility of the tooltip.
29+
* @type {boolean}
30+
*/
31+
export let isOpen = false;
32+
33+
/**
34+
* Controls the visibility of the tooltip after hover the attached element.
35+
* @type {boolean}
36+
*/
37+
export let persist = false;
38+
39+
/**
40+
* The preferred placement of the tooltip.
41+
* @type {string}
42+
*/
43+
export let placement = 'top';
44+
45+
/**
46+
* The target element to which the tooltip is attached.
47+
* @type {string | HTMLElement}
48+
*/
49+
export let target = '';
50+
51+
/**
52+
* The theme name override to apply to this component instance.
53+
* @type {string | null}
54+
*/
55+
export let theme = null;
56+
57+
/**
58+
* The delay for showing the tooltip (in milliseconds).
59+
* @type {number}
60+
*/
61+
export let delay = 0;
62+
63+
/** @type {string} */
64+
let bsPlacement = 'start';
65+
/** @type {object} */
66+
let popperInstance;
67+
/** @type {string} */
68+
let popperPlacement = placement;
69+
/** @type {HTMLDivElement | null} */
70+
let targetEl;
71+
/** @type {HTMLDivElement | null} */
72+
let tooltipEl;
73+
/** @type {number} */
74+
let showTimer;
75+
76+
const checkPopperPlacement = {
77+
name: 'checkPopperPlacement',
78+
enabled: true,
79+
phase: 'main',
80+
// @ts-ignore
81+
fn(args) {
82+
popperPlacement = args.state.placement;
83+
}
84+
};
85+
86+
87+
onMount(() => {
88+
registerEventListeners();
89+
});
90+
91+
onDestroy(() => {
92+
unregisterEventListeners();
93+
clearTimeout(showTimer);
94+
});
95+
96+
const open = () => {
97+
clearTimeout(showTimer);
98+
showTimer = setTimeout(() => (isOpen = true), delay);
99+
};
100+
101+
const close = () => {
102+
clearTimeout(showTimer);
103+
isOpen = false;
104+
};
105+
106+
function registerEventListeners() {
107+
// eslint-disable-next-line eqeqeq
108+
if (target == null || !target) {
109+
targetEl = null;
110+
return;
111+
}
112+
113+
try {
114+
if (target instanceof HTMLElement) {
115+
// @ts-ignore
116+
targetEl = target;
117+
}
118+
} catch (e) {}
119+
120+
// eslint-disable-next-line eqeqeq
121+
if (targetEl == null) {
122+
try {
123+
targetEl = document.querySelector(`#${target}`);
124+
} catch (e) {}
125+
}
126+
127+
if (targetEl) {
128+
targetEl.addEventListener('mouseover', open);
129+
if (!persist) {
130+
targetEl.addEventListener('mouseleave', close);
131+
}
132+
}
133+
}
134+
135+
function unregisterEventListeners() {
136+
if (targetEl) {
137+
targetEl.removeEventListener('mouseover', open);
138+
targetEl.removeEventListener('mouseleave', close);
139+
targetEl.removeAttribute('aria-describedby');
140+
}
141+
142+
if (tooltipEl && persist) {
143+
tooltipEl.removeEventListener("mouseleave", close);
144+
}
145+
}
146+
147+
/** @param {any} e */
148+
function handleClickOutside(e) {
149+
e.preventDefault();
150+
151+
if (!persist) return;
152+
153+
const curNode = e.detail.currentNode;
154+
const targetNode = e.detail.targetNode;
155+
156+
if (!curNode?.contains(targetNode)) {
157+
isOpen = false;
158+
}
159+
}
160+
161+
$: classes = classnames(
162+
containerClasses,
163+
'tooltip',
164+
`bs-tooltip-${bsPlacement}`,
165+
animation ? 'fade' : null,
166+
isOpen ? 'show' : null
167+
);
168+
169+
$: {
170+
if (isOpen && tooltipEl) {
171+
// @ts-ignore
172+
popperInstance = createPopper(targetEl, tooltipEl, {
173+
placement,
174+
modifiers: [checkPopperPlacement]
175+
});
176+
} else if (popperInstance) {
177+
// @ts-ignore
178+
popperInstance.destroy();
179+
// @ts-ignore
180+
popperInstance = undefined;
181+
}
182+
}
183+
184+
$: if (target) {
185+
unregisterEventListeners();
186+
registerEventListeners();
187+
}
188+
189+
$: if (targetEl) {
190+
if (isOpen) {
191+
targetEl.setAttribute('aria-describedby', id);
192+
} else {
193+
targetEl.removeAttribute('aria-describedby');
194+
}
195+
}
196+
197+
$: if (persist && tooltipEl) {
198+
tooltipEl.addEventListener("mouseleave", close);
199+
}
200+
201+
$: {
202+
if (popperPlacement === 'left') {
203+
bsPlacement = 'start';
204+
} else if (popperPlacement === 'right') {
205+
bsPlacement = 'end';
206+
} else {
207+
bsPlacement = popperPlacement;
208+
}
209+
}
210+
</script>
211+
212+
{#if isOpen}
213+
<svelte:component this={Portal}>
214+
<div
215+
bind:this={tooltipEl}
216+
use:clickoutsideDirective
217+
on:clickoutside={handleClickOutside}
218+
{...$$restProps}
219+
class={classes}
220+
{id}
221+
role="tooltip"
222+
data-bs-theme={theme}
223+
data-bs-delay={delay}
224+
x-placement={popperPlacement}
225+
>
226+
<div class="tooltip-arrow" data-popper-arrow></div>
227+
<div class="tooltip-inner">
228+
<slot />
229+
</div>
230+
</div>
231+
</svelte:component>
232+
{/if}

src/lib/helpers/types/agentTypes.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@
145145
* @property {string?} [template_name]
146146
* @property {string?} [template_display_name]
147147
* @property {string?} [visibility_expression]
148+
* @property {string?} [description]
148149
*/
149150

150151
/**

src/lib/helpers/utils/common.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,9 @@ export function truncateByPrefix(str, prefix) {
7676
*/
7777
export function removeDuplicates(arr, key) {
7878
return [...new Map(arr.map(item => [item[key], item])).values()];
79-
}
79+
}
80+
81+
/**
82+
* @param {(string | null)[]} args
83+
*/
84+
export const classnames = (...args) => args.filter(Boolean).join(' ');

src/lib/scss/custom/pages/_agent.scss

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,4 +249,21 @@
249249
gap: 5px;
250250
flex-wrap: wrap;
251251
margin: 5px 0px;
252+
}
253+
254+
.agent-utility-desc {
255+
.tooltip-inner {
256+
text-align: start;
257+
max-width: fit-content;
258+
padding: 20px;
259+
}
260+
261+
.markdown-div {
262+
max-height: 500px;
263+
font-size: 15px;
264+
}
265+
266+
&.show {
267+
opacity: 1 !important;
268+
}
252269
}

src/lib/services/signalr-service.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export const signalr = {
4848
// create a new connection object with the hub URL and some options
4949
let user = getUserStore();
5050
connection = new HubConnectionBuilder()
51-
.withUrl(endpoints.chatHubUrl + `?conversationId=${conversationId}&access_token=${user.token}`) // the hub URL, change it according to your server
51+
.withUrl(endpoints.chatHubUrl + `?conversation-id=${conversationId}&access_token=${user.token}`) // the hub URL, change it according to your server
5252
.withAutomaticReconnect() // enable automatic reconnection
5353
.configureLogging(LogLevel.Information) // configure the logging level
5454
.build();

0 commit comments

Comments
 (0)