Skip to content

Commit 93bc01c

Browse files
authored
Merge pull request #359 from iceljc/features/refine-chat-window
use multi select
2 parents 4562b3d + 48b315b commit 93bc01c

File tree

10 files changed

+365
-195
lines changed

10 files changed

+365
-195
lines changed

src/lib/common/MultiSelect.svelte renamed to src/lib/common/Select.svelte

Lines changed: 149 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script>
2-
import { onMount, createEventDispatcher, tick, afterUpdate } from "svelte";
2+
import { onMount, createEventDispatcher, tick } from "svelte";
33
import { Input } from "@sveltestrap/sveltestrap";
44
import { clickoutsideDirective } from "$lib/helpers/directives";
55
@@ -11,9 +11,15 @@
1111
/** @type {import('$commonTypes').LabelValuePair[]} */
1212
export let options = [];
1313
14+
/** @type {boolean} */
15+
export let multiSelect = false;
16+
1417
/** @type {boolean} */
1518
export let selectAll = true;
1619
20+
/** @type {boolean} */
21+
export let disabled = false;
22+
1723
/** @type {string} */
1824
export let searchPlaceholder = '';
1925
@@ -24,7 +30,7 @@
2430
export let selectedText = '';
2531
2632
/** @type {string[]} */
27-
export let selectedLabels;
33+
export let selectedValues = [];
2834
2935
/** @type {string} */
3036
export let containerClasses = "";
@@ -38,6 +44,9 @@
3844
/** @type {boolean} */
3945
export let searchMode = false;
4046
47+
/** @type {boolean} */
48+
export let loadMore = false;
49+
4150
/** @type {null | undefined | (() => Promise<any>)} */
4251
export let onScrollMoreOptions = null;
4352
@@ -64,6 +73,48 @@
6473
let loading = false;
6574
6675
onMount(() => {
76+
initOptions();
77+
});
78+
79+
$: {
80+
innerOptions = verifySelectedOptions(innerOptions, selectedValues);
81+
refOptions = verifySelectedOptions(innerOptions, selectedValues);
82+
changeDisplayText();
83+
}
84+
85+
$: {
86+
if (loadMore) {
87+
if (options.length > refOptions.length) {
88+
const curKeys = refOptions.map(x => x.value);
89+
const newOptions = options.filter(x => !curKeys.includes(x.value)).map(x => {
90+
return {
91+
label: x.label,
92+
value: x.value,
93+
checked: false
94+
};
95+
});
96+
97+
innerOptions = [
98+
...innerOptions,
99+
...newOptions
100+
];
101+
102+
refOptions = [
103+
...refOptions,
104+
...newOptions
105+
];
106+
107+
changeDisplayText();
108+
}
109+
} else {
110+
innerOptions = verifySelectedOptions(options, selectedValues);
111+
refOptions = verifySelectedOptions(options, selectedValues);
112+
changeDisplayText();
113+
}
114+
}
115+
116+
117+
function initOptions() {
67118
innerOptions = options.map(x => {
68119
return {
69120
label: x.label,
@@ -79,47 +130,27 @@
79130
checked: false
80131
}
81132
});
82-
});
83-
84-
$: {
85-
innerOptions = innerOptions.map(x => {
86-
x.checked = !!selectedLabels?.includes(x.label);
87-
return {...x};
88-
});
89-
refOptions = refOptions.map(x => {
90-
x.checked = !!selectedLabels?.includes(x.label);
91-
return {...x};
92-
});
93-
changeDisplayText();
94133
}
95134
96-
$: {
97-
if (options.length > refOptions.length) {
98-
const curKeys = refOptions.map(x => x.label);
99-
const newOptions = options.filter(x => !curKeys.includes(x.label)).map(x => {
100-
return {
101-
label: x.label,
102-
value: x.value,
103-
checked: false
104-
};
105-
});
106-
107-
innerOptions = [
108-
...innerOptions,
109-
...newOptions
110-
];
111-
112-
refOptions = [
113-
...refOptions,
114-
...newOptions
115-
];
116-
117-
changeDisplayText();
118-
}
135+
/**
136+
* @param {any[]} list
137+
* @param {string[]} selecteds
138+
*/
139+
function verifySelectedOptions(list, selecteds) {
140+
return list?.map(x => {
141+
const item = { ...x, checked: false };
142+
if (multiSelect) {
143+
item.checked = !!selecteds?.includes(item.value);
144+
} else {
145+
item.checked = selecteds.length > 0 && selecteds[0] === item.value;
146+
}
147+
return item;
148+
}) || [];
119149
}
120150
121-
122151
async function toggleOptionList() {
152+
if (disabled) return;
153+
123154
showOptionList = !showOptionList;
124155
if (showOptionList) {
125156
await tick();
@@ -131,8 +162,10 @@
131162
/** @param {any} e */
132163
function changeSearchValue(e) {
133164
searchValue = e.target.value || '';
165+
const innerValue = searchValue.toLowerCase();
166+
134167
if (searchValue) {
135-
innerOptions = [...refOptions.filter(x => x.value.includes(searchValue))];
168+
innerOptions = [...refOptions.filter(x => x.label.toLowerCase().includes(innerValue))];
136169
} else {
137170
innerOptions = [...refOptions];
138171
}
@@ -147,21 +180,28 @@
147180
*/
148181
function checkOption(e, option) {
149182
innerOptions = innerOptions.map(x => {
150-
if (x.label == option.label) {
151-
x.checked = e == null ? !x.checked : e.target.checked;
183+
const item = { ...x };
184+
if (item.value == option.value) {
185+
item.checked = e == null ? !item.checked : e.target.checked;
186+
} else if (!multiSelect) {
187+
item.checked = false;
152188
}
153-
return { ...x };
189+
return item;
154190
});
155191
156192
refOptions = refOptions.map(x => {
157-
if (x.label == option.label) {
158-
x.checked = e == null ? !x.checked : e.target.checked;
193+
const item = { ...x };
194+
if (item.value == option.value) {
195+
item.checked = e == null ? !item.checked : e.target.checked;
196+
} else if (!multiSelect) {
197+
item.checked = false;
159198
}
160-
return { ...x };
199+
return item;
161200
});
162201
163202
changeDisplayText();
164203
sendEvent();
204+
hideOptionList();
165205
}
166206
167207
/** @param {any} e */
@@ -178,9 +218,9 @@
178218
179219
/** @param {boolean} checked */
180220
function syncChangesToRef(checked) {
181-
const keys = innerOptions.map(x => x.label);
221+
const keys = innerOptions.map(x => x.value);
182222
refOptions = refOptions.map(x => {
183-
if (keys.includes(x.label)) {
223+
if (keys.includes(x.value)) {
184224
return {
185225
...x,
186226
checked: checked
@@ -192,20 +232,25 @@
192232
}
193233
194234
function changeDisplayText() {
195-
const count = refOptions.filter(x => x.checked).length;
196-
if (count === 0) {
197-
displayText = '';
198-
} else if (count === options.length) {
199-
displayText = `All selected ${selectedText} (${count})`;
235+
if (multiSelect) {
236+
const count = refOptions.filter(x => x.checked).length;
237+
if (count === 0) {
238+
displayText = '';
239+
} else if (count === options.length) {
240+
displayText = `All selected ${selectedText} (${count})`;
241+
} else {
242+
displayText = `Selected ${selectedText} (${count})`;
243+
}
200244
} else {
201-
displayText = `Selected ${selectedText} (${count})`;
245+
const selected = refOptions.find(x => x.checked);
246+
displayText = selected?.label || '';
202247
}
203248
204249
verifySelectAll();
205250
}
206251
207252
function verifySelectAll() {
208-
if (!selectAll) return;
253+
if (!selectAll || !multiSelect) return;
209254
210255
const innerCount = innerOptions.filter(x => x.checked).length;
211256
if (innerCount < innerOptions.length) {
@@ -266,6 +311,26 @@
266311
}
267312
}
268313
}
314+
315+
function clearSelection() {
316+
innerOptions = innerOptions.map(x => {
317+
return { ...x, checked: false }
318+
});
319+
320+
refOptions = refOptions.map(x => {
321+
return { ...x, checked: false }
322+
});
323+
324+
changeDisplayText();
325+
sendEvent();
326+
hideOptionList();
327+
}
328+
329+
function hideOptionList() {
330+
if (!multiSelect) {
331+
showOptionList = false;
332+
}
333+
}
269334
</script>
270335
271336
@@ -277,22 +342,24 @@
277342
>
278343
<!-- svelte-ignore a11y-click-events-have-key-events -->
279344
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
280-
<ul
345+
<!-- svelte-ignore a11y-no-static-element-interactions -->
346+
<div
281347
class="display-container"
282348
id={`multiselect-btn-${tag}`}
283349
on:click={() => toggleOptionList()}
284350
>
285351
<Input
286352
type="text"
287-
class='clickable'
353+
class={`clickable ${disabled ? 'disabled' : ''}`}
288354
value={displayText}
289355
placeholder={placeholder}
356+
disabled={disabled}
290357
readonly
291358
/>
292359
<div class={`display-suffix ${showOptionList ? 'show-list' : ''}`}>
293360
<i class="bx bx-chevron-down" />
294361
</div>
295-
</ul>
362+
</div>
296363
{#if showOptionList}
297364
<ul class="option-list" id={`multiselect-list-${tag}`} on:scroll={() => innerScroll()}>
298365
{#if searchMode}
@@ -309,37 +376,57 @@
309376
</div>
310377
{/if}
311378
{#if innerOptions.length > 0}
312-
{#if selectAll}
379+
{#if selectAll && multiSelect}
313380
<!-- svelte-ignore a11y-click-events-have-key-events -->
314381
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
315382
<li
316383
class="option-item clickable"
317-
on:click={() => checkSelectAll(null)}
384+
on:click|preventDefault|stopPropagation={() => {
385+
checkSelectAll(null);
386+
}}
318387
>
319388
<div class="line-align-center select-box">
320389
<Input
321390
type="checkbox"
391+
style="pointer-events: none;"
322392
checked={selectAllChecked}
323-
on:change={e => checkSelectAll(e)}
393+
readonly
324394
/>
325395
</div>
326396
<div class="line-align-center select-name fw-bold">
327397
{'Select all'}
328398
</div>
329399
</li>
330400
{/if}
401+
{#if !multiSelect}
402+
<!-- svelte-ignore a11y-click-events-have-key-events -->
403+
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
404+
<li
405+
class="option-item clickable"
406+
on:click|preventDefault|stopPropagation={() => {
407+
clearSelection();
408+
}}
409+
>
410+
<div class="line-align-center text-secondary">
411+
{`Clear selection`}
412+
</div>
413+
</li>
414+
{/if}
331415
{#each innerOptions as option, idx (idx)}
332416
<!-- svelte-ignore a11y-click-events-have-key-events -->
333417
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
334418
<li
335419
class="option-item clickable"
336-
on:click={() => checkOption(null, option)}
420+
on:click|preventDefault|stopPropagation={() => {
421+
checkOption(null, option);
422+
}}
337423
>
338424
<div class="line-align-center select-box">
339425
<Input
340426
type="checkbox"
427+
style="pointer-events: none;"
341428
checked={option.checked}
342-
on:change={e => checkOption(e, option)}
429+
readonly
343430
/>
344431
</div>
345432
<div class="line-align-center select-name">

src/lib/helpers/types/conversationTypes.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* @typedef {Object} ConversationFilter
88
* @property {import('$commonTypes').Pagination} pager - Pagination
99
* @property {string?} [agentId] - The agent id.
10+
* @property {string[]?} [agentIds] - The agent ids.
1011
* @property {string?} [channel] - The conversation channel.
1112
* @property {string?} [status] - The conversation status.
1213
* @property {string?} [taskId] - The task id.
@@ -288,6 +289,7 @@ IRichContent.prototype.quick_replies;
288289
/**
289290
* @typedef {Object} ConversationSearchOption
290291
* @property {string?} [agentId]
292+
* @property {string[]} [agentIds]
291293
* @property {string?} [channel]
292294
* @property {string?} [taskId]
293295
* @property {string?} [status]

src/lib/scss/app.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ File: Main Css File
4343
@import "custom/components/file";
4444
@import "custom/components/audio";
4545
@import "custom/components/text";
46-
@import "custom/components/multiselect";
46+
@import "custom/components/select";
4747
@import "custom/components/markdown";
4848
@import "custom/components/state";
4949

0 commit comments

Comments
 (0)