fix
· 2.2 KiB · Text
Sin formato
<script lang="ts">
import { createEventDispatcher } from 'svelte';
export let name: string;
export let value: string[] = [];
export let placeholder: string | null = null;
export let list: any[] = [];
export let showFilter: boolean = false;
$: getSelected = (): any[] => {
return list?.filter((i) => {
const val: string = i?.value;
return value?.includes(val);
});
};
let text: string;
let open: boolean;
let query: string = '';
$: getResult = (): any[] => {
return list?.filter((e) => {
const q: string = e?.label;
return q.indexOf(query) > -1;
});
};
const dispatch = createEventDispatcher();
const doHandleSelect = (item: any) => {
open = false;
if (!value) value = [];
if (value?.includes(item.value)) return;
value.push(item.value);
value = value;
dispatch('change', item);
query = '';
};
const doHandleRemove = (item: any) => {
value = value?.filter((value) => value != item.value);
};
</script>
<details bind:open>
<summary>
{#each getSelected() as val}
<input type="hidden" name={`${name}[]`} value={val.value} />
<span class="badge"
>{val.label}
<button on:click|stopPropagation={() => doHandleRemove(val)}>x</button>
</span>
{:else}
<span>{text ?? placeholder ?? '-'}</span>
{/each}
</summary>
<ul role="listbox">
{#if showFilter}
<li class="search">
<input placeholder="search..." type="text" bind:value={query} />
</li>
{/if}
{#each getResult() as item}
<button on:click={() => doHandleSelect(item)} data-value={item?.value}>
{item?.label}
</button>
{/each}
</ul>
</details>
<style>
details {
position: relative;
}
details summary {
padding: 4px 8px;
border-radius: 4px;
border: 1px solid #eee;
background: #fff;
}
details ul {
position: absolute;
left: 0;
right: 0;
min-height: 100px;
max-height: 150px;
overflow-y: auto;
border: 1px solid #eee;
background: #fff;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.08);
padding: 4px;
}
details ul li {
padding: 2px 4px;
}
details ul li:not(.search):hover {
background: #ecec88;
}
.badge {
padding: 2px 8px;
margin: 2px;
font-size: 0.75rem;
border-radius: 4px;
text-decoration: none !important;
white-space: nowrap;
background: #eee;
}
</style>
1 | <script lang="ts"> |
2 | import { createEventDispatcher } from 'svelte'; |
3 | export let name: string; |
4 | export let value: string[] = []; |
5 | export let placeholder: string | null = null; |
6 | export let list: any[] = []; |
7 | export let showFilter: boolean = false; |
8 | $: getSelected = (): any[] => { |
9 | return list?.filter((i) => { |
10 | const val: string = i?.value; |
11 | return value?.includes(val); |
12 | }); |
13 | }; |
14 | let text: string; |
15 | let open: boolean; |
16 | let query: string = ''; |
17 | $: getResult = (): any[] => { |
18 | return list?.filter((e) => { |
19 | const q: string = e?.label; |
20 | return q.indexOf(query) > -1; |
21 | }); |
22 | }; |
23 | const dispatch = createEventDispatcher(); |
24 | const doHandleSelect = (item: any) => { |
25 | open = false; |
26 | if (!value) value = []; |
27 | if (value?.includes(item.value)) return; |
28 | value.push(item.value); |
29 | value = value; |
30 | dispatch('change', item); |
31 | query = ''; |
32 | }; |
33 | const doHandleRemove = (item: any) => { |
34 | value = value?.filter((value) => value != item.value); |
35 | }; |
36 | </script> |
37 | |
38 | <details bind:open> |
39 | <summary> |
40 | {#each getSelected() as val} |
41 | <input type="hidden" name={`${name}[]`} value={val.value} /> |
42 | <span class="badge" |
43 | >{val.label} |
44 | <button on:click|stopPropagation={() => doHandleRemove(val)}>x</button> |
45 | </span> |
46 | {:else} |
47 | <span>{text ?? placeholder ?? '-'}</span> |
48 | {/each} |
49 | </summary> |
50 | <ul role="listbox"> |
51 | {#if showFilter} |
52 | <li class="search"> |
53 | <input placeholder="search..." type="text" bind:value={query} /> |
54 | </li> |
55 | {/if} |
56 | {#each getResult() as item} |
57 | <button on:click={() => doHandleSelect(item)} data-value={item?.value}> |
58 | {item?.label} |
59 | </button> |
60 | {/each} |
61 | </ul> |
62 | </details> |
63 | |
64 | <style> |
65 | details { |
66 | position: relative; |
67 | } |
68 | details summary { |
69 | padding: 4px 8px; |
70 | border-radius: 4px; |
71 | border: 1px solid #eee; |
72 | background: #fff; |
73 | } |
74 | details ul { |
75 | position: absolute; |
76 | left: 0; |
77 | right: 0; |
78 | min-height: 100px; |
79 | max-height: 150px; |
80 | overflow-y: auto; |
81 | border: 1px solid #eee; |
82 | background: #fff; |
83 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.08); |
84 | padding: 4px; |
85 | } |
86 | details ul li { |
87 | padding: 2px 4px; |
88 | } |
89 | details ul li:not(.search):hover { |
90 | background: #ecec88; |
91 | } |
92 | .badge { |
93 | padding: 2px 8px; |
94 | margin: 2px; |
95 | font-size: 0.75rem; |
96 | border-radius: 4px; |
97 | text-decoration: none !important; |
98 | white-space: nowrap; |
99 | background: #eee; |
100 | } |
101 | </style> |
102 |