johnandersen777.bsky.social/knotless
git that lives in your Atmosphere account
Forked from chadtmiller.com/knotless
0
0
<script lang="ts">
import type * as repo from '#lib/lexicons/dev/knotless/repo.ts';
import type { Snippet } from 'svelte';
import Icon from '#lib/atoms/Icon.svelte';
/**
* A menu of the `labels` the one looking may put on a pull or an issue,
* those it carries (`on`, by name) ticked: choosing one tells `toggle`
* to put it on, or take it off, its tick turning while it does. With
* `gear`, it opens from a gear beside a heading that names it, as a
* sidebar's does, rather than from its name; with `summary`, from what
* that draws, as a fact of a pull or issue opens from the labels it
* carries.
*/
let {
labels,
on,
toggle,
gear = false,
summary
}: {
labels: repo.Label[];
on: string[];
toggle: (name: string, removed: boolean) => Promise<unknown>;
gear?: boolean;
summary?: Snippet;
} = $props();
let busy = $state('');
async function pick(name: string, removed: boolean) {
busy = name;
await toggle(name, removed);
busy = '';
}
const hex = (color?: string) => (/^#[0-9a-fA-F]{6}$/.test(color ?? '') ? color : undefined);
</script>
<details class="popover filter-menu label-menu" data-held={busy || undefined}>
{#if summary}<summary title="Choose labels">{@render summary()}</summary>{:else if gear}<summary
aria-label="Choose labels"
title="Choose labels"><Icon name="gear" /></summary
>{:else}<summary>Labels<Icon name="caret" /></summary>{/if}
<div class="menu">
<div class="menu-head">Apply labels</div>
{#each labels as l (l.name)}
{@const ticked = on.includes(l.name)}
<button
class="menu-item"
type="button"
aria-pressed={ticked}
disabled={!!busy}
onclick={() => pick(l.name, ticked)}
><span class="tick"
>{#if busy === l.name}<span class="spinner"></span>{:else if ticked}<Icon
name="check"
/>{/if}</span
><span class="dot" style:--label={hex(l.color)}></span><span>{l.name}</span></button
>
{/each}
</div>
</details>