checkpoint ISAC registries and system monitoring
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
<script lang="ts">
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
let {
|
||||
id,
|
||||
label,
|
||||
children
|
||||
}: {
|
||||
id: string;
|
||||
label: string;
|
||||
children: Snippet;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<section class="registry-inline-editor" {id} aria-label={label}>
|
||||
{@render children()}
|
||||
</section>
|
||||
@@ -87,6 +87,6 @@
|
||||
{/each}
|
||||
<a class:disabled={pagination.page === pagination.pageCount} aria-disabled={pagination.page === pagination.pageCount} href={href(Math.min(pagination.pageCount, pagination.page + 1))}>NEXT →</a>
|
||||
</div>
|
||||
<div class="pagination-sizes"><span>ROWS</span>{#each [10, 25, 50, 100] as size}<a class:active={size === pagination.perPage} href={href(1, size)}>{size}</a>{/each}</div>
|
||||
{/if}
|
||||
<div class="pagination-sizes"><span>ROWS</span>{#each [10, 25, 50, 100] as size}<a class:active={size === pagination.perPage} href={href(1, size)}>{size}</a>{/each}</div>
|
||||
</nav>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
type Item = { id: string; name: string; pending?: boolean; proposerName?: string };
|
||||
type Item = { id: string; name: string; unit?: string; pending?: boolean; proposerName?: string };
|
||||
let {
|
||||
items,
|
||||
selected = $bindable<string[]>([]),
|
||||
@@ -33,10 +33,12 @@
|
||||
const normalizedQuery = $derived(query.normalize('NFKC').trim().toLocaleLowerCase());
|
||||
const available = $derived(items.filter((item) =>
|
||||
!excludedIds.includes(item.id)
|
||||
&& (!normalizedQuery || item.name.toLocaleLowerCase().includes(normalizedQuery))
|
||||
&& (!normalizedQuery || `${item.name} ${item.unit ?? ''}`.toLocaleLowerCase().includes(normalizedQuery))
|
||||
));
|
||||
const visible = $derived(available.slice(0, 30));
|
||||
const selectedItems = $derived(selectedIds.map((id) => items.find((item) => item.id === id)).filter((item): item is Item => Boolean(item)));
|
||||
const unitLabel = (item: Item) => item.unit === undefined ? '' : item.unit || 'FLAT';
|
||||
const accessibleName = (item: Item) => unitLabel(item) ? `${item.name}, unit ${unitLabel(item)}` : item.name;
|
||||
const toggle = (id: string) => {
|
||||
if (mode === 'single') {
|
||||
value = value === id ? '' : id;
|
||||
@@ -57,7 +59,7 @@
|
||||
<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>
|
||||
<button type="button" class:pending-reference={item.pending} onclick={() => toggle(item.id)} aria-label={`Remove ${accessibleName(item)}`}><span>{item.name}{#if unitLabel(item)}<small class="assignment-item-unit">UNIT · {unitLabel(item)}</small>{/if}{#if item.pending}<small>PENDING</small>{/if}</span><b>×</b></button>
|
||||
{:else}
|
||||
<small>{emptySelectionLabel}</small>
|
||||
{/each}
|
||||
@@ -68,7 +70,7 @@
|
||||
<div class="assignment-results" role="list">
|
||||
{#each visible as item}
|
||||
<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>
|
||||
<span>{item.name}{#if unitLabel(item)}<small class="assignment-item-unit">UNIT · {unitLabel(item)}</small>{/if}{#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>
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/state';
|
||||
import type { RegistrySort, RegistrySortDirection } from '$lib/server/pagination';
|
||||
|
||||
let {
|
||||
label,
|
||||
column,
|
||||
sort,
|
||||
namespace = '',
|
||||
defaultDirection = 'asc',
|
||||
class: className = ''
|
||||
}: {
|
||||
label: string;
|
||||
column: string;
|
||||
sort: Pick<RegistrySort, 'key' | 'direction'>;
|
||||
namespace?: string;
|
||||
defaultDirection?: RegistrySortDirection;
|
||||
class?: string;
|
||||
} = $props();
|
||||
|
||||
const active = $derived(sort.key === column);
|
||||
const nextDirection = $derived<RegistrySortDirection>(
|
||||
active ? (sort.direction === 'asc' ? 'desc' : 'asc') : defaultDirection
|
||||
);
|
||||
const sortKey = $derived(namespace ? `${namespace}Sort` : 'sort');
|
||||
const directionKey = $derived(namespace ? `${namespace}Direction` : 'direction');
|
||||
const pageKey = $derived(namespace ? `${namespace}Page` : 'page');
|
||||
const href = $derived.by(() => {
|
||||
const params = new URLSearchParams(page.url.searchParams);
|
||||
params.set(sortKey, column);
|
||||
params.set(directionKey, nextDirection);
|
||||
params.delete(pageKey);
|
||||
const query = params.toString();
|
||||
return `${page.url.pathname}${query ? `?${query}` : ''}`;
|
||||
});
|
||||
</script>
|
||||
|
||||
<a
|
||||
class="sortable-column {className}"
|
||||
class:active
|
||||
{href}
|
||||
role="columnheader"
|
||||
aria-sort={active ? (sort.direction === 'asc' ? 'ascending' : 'descending') : 'none'}
|
||||
aria-label={`${label}: ${active ? `sorted ${sort.direction}. Activate to sort ${nextDirection}` : `activate to sort ${nextDirection}`}`}
|
||||
>
|
||||
<span class="sort-direction" aria-hidden="true">{active ? (sort.direction === 'asc' ? '↑' : '↓') : '↕'}</span>
|
||||
<span>{label}</span>
|
||||
</a>
|
||||
Reference in New Issue
Block a user