2022-06-29 20:03:32 +02:00
|
|
|
<script>
|
2022-08-09 11:33:55 +02:00
|
|
|
import { Icon, Search, Layout } from "@budibase/bbui"
|
2022-08-04 14:25:44 +02:00
|
|
|
import { createEventDispatcher } from "svelte"
|
2022-06-29 20:03:32 +02:00
|
|
|
|
|
|
|
export let searchTerm = ""
|
|
|
|
export let selected
|
2022-08-04 14:25:44 +02:00
|
|
|
export let list = []
|
|
|
|
export let labelKey
|
2022-08-05 11:32:55 +02:00
|
|
|
export let iconComponent = null
|
|
|
|
export let extractIconProps = x => x
|
2022-08-04 14:25:44 +02:00
|
|
|
|
|
|
|
const dispatch = createEventDispatcher()
|
|
|
|
|
|
|
|
$: enrichedList = enrich(list, selected)
|
|
|
|
$: sortedList = sort(enrichedList)
|
|
|
|
|
|
|
|
const enrich = (list, selected) => {
|
|
|
|
return list.map(item => {
|
|
|
|
return {
|
|
|
|
...item,
|
2022-08-04 16:33:51 +02:00
|
|
|
selected: selected.find(x => x === item._id) != null,
|
2022-08-04 14:25:44 +02:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
const sort = list => {
|
|
|
|
let sortedList = list.slice()
|
|
|
|
sortedList.sort((a, b) => {
|
|
|
|
if (a.selected === b.selected) {
|
|
|
|
return a[labelKey] < b[labelKey] ? -1 : 1
|
|
|
|
} else if (a.selected) {
|
|
|
|
return -1
|
|
|
|
} else if (b.selected) {
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
})
|
|
|
|
return sortedList
|
|
|
|
}
|
2022-06-29 20:03:32 +02:00
|
|
|
</script>
|
|
|
|
|
2022-08-04 14:25:44 +02:00
|
|
|
<div class="container">
|
|
|
|
<Layout gap="S">
|
|
|
|
<div class="header">
|
|
|
|
<Search placeholder="Search" bind:value={searchTerm} />
|
2022-06-29 20:03:32 +02:00
|
|
|
</div>
|
2022-08-04 14:25:44 +02:00
|
|
|
<div class="items">
|
|
|
|
{#each sortedList as item}
|
|
|
|
<div
|
|
|
|
on:click={() => {
|
|
|
|
dispatch(item.selected ? "deselect" : "select", item._id)
|
|
|
|
}}
|
|
|
|
class="item"
|
|
|
|
>
|
2022-08-05 11:32:55 +02:00
|
|
|
{#if iconComponent}
|
|
|
|
<svelte:component
|
|
|
|
this={iconComponent}
|
|
|
|
{...extractIconProps(item)}
|
|
|
|
/>
|
|
|
|
{/if}
|
2022-08-04 14:25:44 +02:00
|
|
|
<div class="text">
|
|
|
|
{item[labelKey]}
|
2022-06-29 20:03:32 +02:00
|
|
|
</div>
|
2022-08-04 14:25:44 +02:00
|
|
|
{#if item.selected}
|
|
|
|
<div>
|
|
|
|
<Icon
|
|
|
|
color="var(--spectrum-global-color-blue-600);"
|
|
|
|
name="Checkmark"
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
{/if}
|
|
|
|
</div>
|
|
|
|
{/each}
|
|
|
|
</div>
|
|
|
|
</Layout>
|
2022-06-29 20:03:32 +02:00
|
|
|
</div>
|
|
|
|
|
|
|
|
<style>
|
2022-08-04 14:25:44 +02:00
|
|
|
.container {
|
|
|
|
width: 280px;
|
|
|
|
}
|
2022-06-29 20:03:32 +02:00
|
|
|
.header {
|
|
|
|
align-items: center;
|
2022-08-04 14:25:44 +02:00
|
|
|
display: grid;
|
|
|
|
gap: var(--spacing-m);
|
|
|
|
grid-template-columns: 1fr;
|
2022-06-29 20:03:32 +02:00
|
|
|
}
|
2022-08-04 14:25:44 +02:00
|
|
|
.items {
|
|
|
|
max-height: 242px;
|
|
|
|
overflow: auto;
|
|
|
|
overflow-x: hidden;
|
|
|
|
margin: 0 calc(-1 * var(--spacing-m));
|
|
|
|
margin-top: -8px;
|
|
|
|
}
|
|
|
|
.item {
|
2022-06-29 20:03:32 +02:00
|
|
|
display: flex;
|
|
|
|
justify-content: space-between;
|
|
|
|
cursor: pointer;
|
2022-08-04 14:25:44 +02:00
|
|
|
padding: var(--spacing-s) var(--spacing-l);
|
|
|
|
background: var(--spectrum-global-color-gray-50);
|
|
|
|
transition: background 130ms ease-out;
|
2022-08-05 11:32:55 +02:00
|
|
|
gap: var(--spacing-m);
|
|
|
|
align-items: center;
|
2022-06-29 20:03:32 +02:00
|
|
|
}
|
2022-08-04 14:25:44 +02:00
|
|
|
.item:hover {
|
|
|
|
background: var(--spectrum-global-color-gray-100);
|
|
|
|
cursor: pointer;
|
2022-06-29 20:03:32 +02:00
|
|
|
}
|
2022-08-04 14:25:44 +02:00
|
|
|
.text {
|
|
|
|
flex: 1 1 auto;
|
|
|
|
width: 0;
|
|
|
|
overflow: hidden;
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
white-space: nowrap;
|
2022-06-29 20:03:32 +02:00
|
|
|
}
|
|
|
|
</style>
|