Enhance Mihomo proxy groups component with improved data handling and UI updates
- Introduced a stable order for selectable proxy groups to minimize DOM changes during JSON updates. - Added a refresh icon to the update button for better user feedback during loading states. - Enhanced error handling to prevent UI flickering when fetching proxy data. - Updated layout in the Mihomo page to utilize a grid system for better responsiveness and organization.
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
import DataQueryState from '$lib/components/fleet/data-query-state.svelte';
|
||||
import { Skeleton } from '$lib/components/ui/skeleton/index.js';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import RefreshCwIcon from '@lucide/svelte/icons/refresh-cw';
|
||||
|
||||
let {
|
||||
alias,
|
||||
@@ -39,8 +40,8 @@
|
||||
try {
|
||||
proxiesData = await fetchMihomoJson<MihomoProxiesResponse>(a, 'proxies');
|
||||
} catch (e) {
|
||||
proxiesData = null;
|
||||
proxiesErr = e instanceof ApiError ? e.message : String(e);
|
||||
// не затираем успешный снимок при сбое обновления — без мерцания UI
|
||||
} finally {
|
||||
proxiesLoading = false;
|
||||
}
|
||||
@@ -59,6 +60,15 @@
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Стабильный порядок групп — меньше скачков DOM при обновлении JSON. */
|
||||
let selectableGroups = $derived.by((): [string, MihomoProxyEntry][] => {
|
||||
const p = proxiesData?.proxies;
|
||||
if (!p) return [];
|
||||
return Object.entries(p)
|
||||
.filter(([_, v]) => isSelectableGroup(v))
|
||||
.sort(([a], [b]) => a.localeCompare(b));
|
||||
});
|
||||
|
||||
function lastDelay(p: MihomoProxyEntry): number | null {
|
||||
const h = p.history;
|
||||
if (!h?.length) return null;
|
||||
@@ -139,11 +149,20 @@
|
||||
onclick={() => loadProxies()}
|
||||
disabled={proxiesLoading || !alias}
|
||||
>
|
||||
<RefreshCwIcon
|
||||
class={cn('mr-1 size-3.5 shrink-0', proxiesLoading && 'animate-spin')}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Обновить
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<DataQueryState err={proxiesErr} showSkeleton={proxiesLoading} isEmpty={false}>
|
||||
<DataQueryState
|
||||
err={proxiesErr}
|
||||
hasStaleData={proxiesData != null && proxiesErr != null}
|
||||
showSkeleton={proxiesLoading && proxiesData === null}
|
||||
isEmpty={false}
|
||||
>
|
||||
{#snippet skeleton()}
|
||||
{#if compact}
|
||||
<div class="flex flex-wrap gap-1">
|
||||
@@ -162,7 +181,7 @@
|
||||
{#snippet children()}
|
||||
{#if proxiesData?.proxies}
|
||||
<div class={cn('flex flex-col', compact ? 'gap-3' : 'gap-8')}>
|
||||
{#each Object.entries(proxiesData.proxies).filter(([_, v]) => isSelectableGroup(v)) as [gName, group] (gName)}
|
||||
{#each selectableGroups as [gName, group] (gName)}
|
||||
{#if compact}
|
||||
<div class="bg-muted/20 rounded-lg border px-2 py-2 sm:px-3">
|
||||
<div class="mb-1.5 flex flex-wrap items-center justify-between gap-x-2 gap-y-1">
|
||||
|
||||
@@ -329,12 +329,14 @@
|
||||
Список групп подгружается автоматически. Клик по ячейке — выбор узла; «ping» — задержка.
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3">
|
||||
<div
|
||||
class="grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-3"
|
||||
>
|
||||
{#each rows.filter((r) => r.metaStatus === 'ok') as r (r.alias)}
|
||||
<Card.Root
|
||||
id="mihomo-proxies-{encodeURIComponent(r.alias)}"
|
||||
data-size="sm"
|
||||
class="border-l-4 border-l-primary/30 scroll-mt-20 shadow-sm"
|
||||
class="border-l-4 border-l-primary/30 min-h-0 min-w-0 scroll-mt-20 shadow-sm"
|
||||
>
|
||||
<Card.Header class="space-y-0 pb-1 pt-3">
|
||||
<Card.Title class="text-sm font-semibold">{r.alias}</Card.Title>
|
||||
|
||||
Reference in New Issue
Block a user