~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/knotless

git that lives in your Atmosphere account

Forked from chadtmiller.com/knotless

0
0
88 lines · 2.7 KB Raw History
<script lang="ts">
	import type { ActorView } from '#lib/lexicons/dev/knotless/defs.ts';
	import Avatar from '#lib/atoms/Avatar.svelte';
	import BotMark from '#lib/atoms/BotMark.svelte';
	import Icon from '#lib/atoms/Icon.svelte';
	/**
	 * Whom a pull or an issue is assigned to, as a fact of it: their faces
	 * and who they are, opening to a row each. With `people` and `assign`,
	 * as the owner or a collaborator, it also assigns it to more of them,
	 * or takes one off.
	 */
	let {
		assignees,
		people = [],
		assign
	}: {
		assignees: ActorView[];
		people?: ActorView[];
		assign?: (did: ActorView['did'], removed: boolean) => Promise<unknown>;
	} = $props();
	/** Those it may yet be assigned to. */
	const more = $derived(people.filter((p) => !assignees.some((a) => a.did === p.did)));
	const SHOWN = 5;
	/** Who, in words: the first by name, then how many more. */
	const who = $derived(
		assignees.length > 1
			? `${assignees[0].handle} and ${assignees.length - 1} more`
			: (assignees[0]?.handle ?? '')
	);
	let busy = $state('');
	async function pick(did: ActorView['did'], removed: boolean) {
		busy = did;
		await assign?.(did, removed);
		busy = '';
	}
</script>

{#if assignees.length}
	<details class="popover filter-menu assignees-fact" data-held={busy || undefined}>
		<summary aria-label="Assigned to {assignees.map((a) => a.handle).join(', ')}">
			<span class="avs">
				{#each assignees.slice(0, SHOWN).reverse() as a (a.did)}<span class="face"
						><Avatar name={a.handle} did={a.did} src={a.avatar} bot={a.bot} /></span
					>{/each}
			</span>
			<span class="said">Assigned to <b>{who}</b></span>
		</summary>
		<div class="menu">
			<div class="menu-head">Assignees</div>
			{#each assignees as a (a.did)}
				<div class="menu-item reviewer">
					<Avatar name={a.handle} did={a.did} src={a.avatar} />
					<span class="who"
						>{a.handle}{#if a.bot}<BotMark />{/if}</span
					>
					{#if assign}<button
							class="stop"
							type="button"
							aria-label="Take {a.handle} off"
							title="Take off"
							disabled={!!busy}
							aria-busy={busy === a.did}
							onclick={() => pick(a.did, true)}
							>{#if busy === a.did}<span class="spinner"></span>{:else}<Icon
									name="x"
								/>{/if}</button
						>{/if}
				</div>
			{/each}
			{#if assign && more.length}
				<div class="menu-head sub">Assign</div>
				{#each more as p (p.did)}
					<button
						class="menu-item"
						type="button"
						disabled={!!busy}
						onclick={() => pick(p.did, false)}
						><span class="tick"
							>{#if busy === p.did}<span class="spinner"></span>{:else}<Icon
									name="plus"
								/>{/if}</span
						><Avatar name={p.handle} did={p.did} src={p.avatar} /><span>{p.handle}</span></button
					>
				{/each}
			{/if}
		</div>
	</details>
{/if}