Add ISAC Skill Chips data module

This commit is contained in:
JB
2026-07-25 16:13:34 -04:00
parent 8b7b747866
commit 60c3618173
21 changed files with 495 additions and 35 deletions
+12 -6
View File
@@ -3,6 +3,8 @@
let {
items,
selected = $bindable<string[]>([]),
value = $bindable(''),
mode = 'multiple',
excludedIds = [],
fieldName,
emptyLabel = 'NO MATCHING RECORDS',
@@ -13,6 +15,8 @@
}: {
items: Item[];
selected?: string[];
value?: string;
mode?: 'multiple' | 'single';
excludedIds?: string[];
fieldName: string;
emptyLabel?: string;
@@ -22,22 +26,24 @@
emptySelectionLabel?: string;
} = $props();
let query = $state('');
const selectedIds = $derived(mode === 'single' ? (value ? [value] : []) : selected);
const normalizedQuery = $derived(query.normalize('NFKC').trim().toLocaleLowerCase());
const available = $derived(items.filter((item) =>
!excludedIds.includes(item.id)
&& (!normalizedQuery || item.name.toLocaleLowerCase().includes(normalizedQuery))
));
const visible = $derived(available.slice(0, 30));
const selectedItems = $derived(selected.map((id) => items.find((item) => item.id === id)).filter((item): item is Item => Boolean(item)));
const selectedItems = $derived(selectedIds.map((id) => items.find((item) => item.id === id)).filter((item): item is Item => Boolean(item)));
const toggle = (id: string) => {
selected = selected.includes(id) ? selected.filter((value) => value !== id) : [...selected, id];
if (mode === 'single') value = value === id ? '' : id;
else selected = selected.includes(id) ? selected.filter((value) => value !== id) : [...selected, id];
};
</script>
<div class="assignment-picker">
{#each selected as id}<input type="hidden" name={fieldName} value={id}/>{/each}
{#each selectedIds as id}<input type="hidden" name={fieldName} value={id}/>{/each}
<div class="assignment-selected">
<div class="assignment-summary"><span>ASSIGNED</span><b>{selected.length}</b></div>
<div class="assignment-summary"><span>{mode === 'single' ? 'SELECTED' : 'ASSIGNED'}</span><b>{selectedIds.length}</b></div>
<div class="assignment-chips">
{#each selectedItems as item}
<button type="button" class:pending-reference={item.pending} onclick={() => toggle(item.id)} aria-label={`Remove ${item.name}`}><span>{item.name}{#if item.pending}<small>PENDING</small>{/if}</span><b>×</b></button>
@@ -50,8 +56,8 @@
<div class="assignment-results-head"><span>{available.length} MATCHES</span>{#if available.length > visible.length}<b>SHOWING FIRST {visible.length}</b>{/if}</div>
<div class="assignment-results" role="list">
{#each visible as item}
<button type="button" class:selected={selected.includes(item.id)} class:pending-reference={item.pending} aria-pressed={selected.includes(item.id)} onclick={() => toggle(item.id)}>
<span>{item.name}{#if item.pending}<small>PENDING · {item.proposerName ?? 'CONTRIBUTOR'}</small>{/if}</span><b>{selected.includes(item.id) ? 'ASSIGNED' : 'ADD +'}</b>
<button type="button" class:selected={selectedIds.includes(item.id)} class:pending-reference={item.pending} aria-pressed={selectedIds.includes(item.id)} onclick={() => toggle(item.id)}>
<span>{item.name}{#if item.pending}<small>PENDING · {item.proposerName ?? 'CONTRIBUTOR'}</small>{/if}</span><b>{selectedIds.includes(item.id) ? (mode === 'single' ? 'SELECTED' : 'ASSIGNED') : (mode === 'single' ? 'SELECT →' : 'ADD +')}</b>
</button>
{:else}
<p>{emptyLabel}</p>