refactor(ui): extract shared IconButton from duplicated .icon-btn copies

Five files hand-rolled a near-identical 32px `.icon-btn` (same size, hover,
and primary/danger variants). Extract a single IconButton.svelte component
so the treatment lives in one place. Converts the four sites with the
standard 32px form: collections detail, manga edit, upload, and the
chapter-pages editor.

The component takes a `variant` (plain/primary/danger) and spreads any
button attributes (onclick, disabled, aria-label, title, data-testid)
straight through; `type="button"` defaults but a caller can override. The
rendered button keeps the same class, styles, and DOM position, so layout
and behaviour are unchanged — no version bump.

Three icon-button sites are intentionally left out:
- The header (+layout) and home search button are 36px / different radius —
  size outliers that need a size/radius prop before folding in.
- profile/history's copy is being removed in the shared-HistoryList change;
  touching it here would just conflict.

A component (not a global class) avoids colliding with those remaining
local `.icon-btn` definitions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-25 19:37:08 +02:00
parent bd7c3fc28c
commit 3a36796768
6 changed files with 156 additions and 154 deletions

View File

@@ -12,6 +12,7 @@
import type { Manga } from '$lib/api/client';
import { fileUrl } from '$lib/api/client';
import MangaCard from '$lib/components/MangaCard.svelte';
import IconButton from '$lib/components/IconButton.svelte';
import ArrowLeft from '@lucide/svelte/icons/arrow-left';
import Pencil from '@lucide/svelte/icons/pencil';
import Check from '@lucide/svelte/icons/check';
@@ -148,26 +149,23 @@
{:else}
<div class="title-row">
<h1 data-testid="collection-name">{collection.name}</h1>
<button
type="button"
class="icon-btn"
<IconButton
onclick={startEdit}
aria-label="Edit collection"
title="Edit"
data-testid="collection-edit-open"
>
<Pencil size={16} aria-hidden="true" />
</button>
<button
type="button"
class="icon-btn danger"
</IconButton>
<IconButton
variant="danger"
onclick={onDeleteCollection}
aria-label="Delete collection"
title="Delete"
data-testid="collection-delete"
>
<Trash2 size={16} aria-hidden="true" />
</button>
</IconButton>
</div>
{#if collection.description}
<p class="description" data-testid="collection-description">
@@ -418,26 +416,4 @@
height: 32px;
}
}
.icon-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
padding: 0;
background: transparent;
color: var(--text-muted);
border: 1px solid transparent;
border-radius: var(--radius-sm);
}
.icon-btn:hover {
background: var(--surface-elevated);
color: var(--text);
}
.icon-btn.danger:hover {
color: var(--danger);
}
</style>