You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
108 lines
2.7 KiB
108 lines
2.7 KiB
<script lang="ts"> |
|
import { Avatar } from "$lib/components/ui"; |
|
|
|
interface Member { |
|
id: string; |
|
user_id: string; |
|
profiles: { |
|
id: string; |
|
full_name: string | null; |
|
email: string; |
|
avatar_url: string | null; |
|
}; |
|
} |
|
|
|
interface Props { |
|
value: string | null; |
|
members: Member[]; |
|
label?: string; |
|
onchange: (userId: string | null) => void; |
|
} |
|
|
|
let { value, members, label, onchange }: Props = $props(); |
|
|
|
let isOpen = $state(false); |
|
|
|
function getAssignee(id: string | null) { |
|
if (!id) return null; |
|
return members.find((m) => m.user_id === id); |
|
} |
|
|
|
const assignee = $derived(getAssignee(value)); |
|
|
|
function select(userId: string | null) { |
|
onchange(userId); |
|
isOpen = false; |
|
} |
|
</script> |
|
|
|
<div class="flex flex-col gap-3 w-full"> |
|
{#if label} |
|
<span class="px-3 font-bold font-body text-body text-white"> |
|
{label} |
|
</span> |
|
{/if} |
|
|
|
<div class="relative"> |
|
<button |
|
type="button" |
|
class="w-full p-3 bg-background text-white rounded-[32px] min-w-[192px] |
|
font-medium font-input text-body |
|
focus:outline-none focus:ring-2 focus:ring-primary |
|
transition-colors text-left flex items-center gap-3" |
|
onclick={() => (isOpen = !isOpen)} |
|
> |
|
{#if assignee} |
|
<Avatar |
|
name={assignee.profiles.full_name || |
|
assignee.profiles.email} |
|
size="sm" |
|
/> |
|
<span class="truncate"> |
|
{assignee.profiles.full_name || assignee.profiles.email} |
|
</span> |
|
{:else} |
|
<Avatar name="?" size="sm" /> |
|
<span class="text-white/40">Unassigned</span> |
|
{/if} |
|
</button> |
|
|
|
{#if isOpen} |
|
<!-- svelte-ignore a11y_no_static_element_interactions --> |
|
<!-- svelte-ignore a11y_click_events_have_key_events --> |
|
<div |
|
class="fixed inset-0 z-40" |
|
onclick={() => (isOpen = false)} |
|
></div> |
|
<div |
|
class="absolute top-full left-0 right-0 mt-2 bg-night border border-light/10 rounded-2xl shadow-xl z-50 max-h-48 overflow-y-auto py-1" |
|
> |
|
<button |
|
type="button" |
|
class="w-full px-4 py-2.5 text-left text-body-md text-white/60 hover:bg-dark transition-colors flex items-center gap-3" |
|
onclick={() => select(null)} |
|
> |
|
<Avatar name="?" size="sm" /> |
|
Unassigned |
|
</button> |
|
{#each members as member} |
|
<button |
|
type="button" |
|
class="w-full px-4 py-2.5 text-left text-body-md hover:bg-dark transition-colors flex items-center gap-3 |
|
{value === member.user_id ? 'bg-primary/10 text-primary' : 'text-white'}" |
|
onclick={() => select(member.user_id)} |
|
> |
|
<Avatar |
|
name={member.profiles.full_name || |
|
member.profiles.email} |
|
size="sm" |
|
/> |
|
<span class="truncate"> |
|
{member.profiles.full_name || member.profiles.email} |
|
</span> |
|
</button> |
|
{/each} |
|
</div> |
|
{/if} |
|
</div> |
|
</div>
|
|
|