update: generalize button components
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { X, Pencil } from 'lucide-svelte';
|
||||
import IconButton from './IconButton.svelte';
|
||||
|
||||
let {
|
||||
color = $bindable(null),
|
||||
@@ -39,14 +40,14 @@
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
{#if color}
|
||||
<button
|
||||
type="button"
|
||||
<IconButton
|
||||
onclick={clearColor}
|
||||
class="{buttonSize} flex items-center justify-center rounded-full border border-input hover:bg-accent transition-colors"
|
||||
{color}
|
||||
{size}
|
||||
aria-label="Clear color"
|
||||
>
|
||||
<X class={iconSize} />
|
||||
</button>
|
||||
</IconButton>
|
||||
{/if}
|
||||
<label
|
||||
class="{buttonSize} flex items-center justify-center rounded-full border border-input hover:opacity-90 transition-opacity cursor-pointer relative overflow-hidden"
|
||||
|
||||
@@ -79,7 +79,14 @@
|
||||
</script>
|
||||
|
||||
<div class="relative dropdown-menu">
|
||||
<Button variant="outline" size="icon" onclick={toggleMenu} aria-label={ariaLabel}>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onclick={toggleMenu}
|
||||
aria-label={ariaLabel}
|
||||
class={color ? 'hover-themed' : ''}
|
||||
style={color ? `--hover-bg: ${color}20;` : ''}
|
||||
>
|
||||
{@render icon()}
|
||||
</Button>
|
||||
|
||||
@@ -117,3 +124,9 @@
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
:global(.hover-themed:hover) {
|
||||
background-color: var(--hover-bg) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
52
src/lib/components/ui/IconButton.svelte
Normal file
52
src/lib/components/ui/IconButton.svelte
Normal file
@@ -0,0 +1,52 @@
|
||||
<script lang="ts">
|
||||
import type { Snippet } from 'svelte';
|
||||
import type { HTMLButtonAttributes } from 'svelte/elements';
|
||||
|
||||
interface Props extends HTMLButtonAttributes {
|
||||
color?: string | null;
|
||||
rounded?: 'full' | 'md' | 'lg';
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
children: Snippet;
|
||||
}
|
||||
|
||||
let {
|
||||
color = null,
|
||||
rounded = 'full',
|
||||
size = 'md',
|
||||
class: className = '',
|
||||
children,
|
||||
...restProps
|
||||
}: Props = $props();
|
||||
|
||||
const sizeClasses = {
|
||||
sm: 'w-8 h-8',
|
||||
md: 'w-10 h-10',
|
||||
lg: 'w-12 h-12'
|
||||
};
|
||||
|
||||
const roundedClasses = {
|
||||
full: 'rounded-full',
|
||||
md: 'rounded-md',
|
||||
lg: 'rounded-lg'
|
||||
};
|
||||
|
||||
const baseClasses = 'flex items-center justify-center border border-input transition-colors';
|
||||
const sizeClass = sizeClasses[size];
|
||||
const roundedClass = roundedClasses[rounded];
|
||||
</script>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="{baseClasses} {sizeClass} {roundedClass} {className}"
|
||||
class:hover:bg-accent={!color}
|
||||
style={color ? `--hover-bg: ${color}20;` : ''}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children()}
|
||||
</button>
|
||||
|
||||
<style>
|
||||
button[style*='--hover-bg']:hover {
|
||||
background-color: var(--hover-bg);
|
||||
}
|
||||
</style>
|
||||
@@ -6,6 +6,7 @@
|
||||
import { Pencil, Check, X } from "lucide-svelte";
|
||||
import ColorPicker from "$lib/components/ui/ColorPicker.svelte";
|
||||
import ThemePicker from "$lib/components/ui/theme-picker.svelte";
|
||||
import IconButton from "$lib/components/ui/IconButton.svelte";
|
||||
import type { Wishlist } from "$lib/server/schema";
|
||||
import { languageStore } from '$lib/stores/language.svelte';
|
||||
import { getCardStyle } from '$lib/utils/colors';
|
||||
@@ -100,8 +101,7 @@
|
||||
{:else}
|
||||
<h1 class="text-3xl font-bold leading-[2.25rem]">{wishlistTitle}</h1>
|
||||
{/if}
|
||||
<button
|
||||
type="button"
|
||||
<IconButton
|
||||
onclick={() => {
|
||||
if (editingTitle) {
|
||||
saveTitle();
|
||||
@@ -109,7 +109,9 @@
|
||||
editingTitle = true;
|
||||
}
|
||||
}}
|
||||
class="shrink-0 w-8 h-8 flex items-center justify-center rounded-full border border-input hover:bg-accent transition-colors"
|
||||
color={wishlistColor}
|
||||
size="sm"
|
||||
class="shrink-0"
|
||||
aria-label={editingTitle ? "Save title" : "Edit title"}
|
||||
>
|
||||
{#if editingTitle}
|
||||
@@ -117,7 +119,7 @@
|
||||
{:else}
|
||||
<Pencil class="w-4 h-4" />
|
||||
{/if}
|
||||
</button>
|
||||
</IconButton>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 shrink-0">
|
||||
<ThemePicker
|
||||
@@ -145,8 +147,7 @@
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<Label for="wishlist-description">{t.form.descriptionOptional}</Label>
|
||||
<button
|
||||
type="button"
|
||||
<IconButton
|
||||
onclick={() => {
|
||||
if (editingDescription) {
|
||||
saveDescription();
|
||||
@@ -154,7 +155,9 @@
|
||||
editingDescription = true;
|
||||
}
|
||||
}}
|
||||
class="flex-shrink-0 w-8 h-8 flex items-center justify-center rounded-full border border-input hover:bg-accent transition-colors"
|
||||
color={wishlistColor}
|
||||
size="sm"
|
||||
class="flex-shrink-0"
|
||||
aria-label={editingDescription ? "Save description" : "Edit description"}
|
||||
>
|
||||
{#if editingDescription}
|
||||
@@ -162,7 +165,7 @@
|
||||
{:else}
|
||||
<Pencil class="w-4 h-4" />
|
||||
{/if}
|
||||
</button>
|
||||
</IconButton>
|
||||
</div>
|
||||
{#if editingDescription}
|
||||
<Textarea
|
||||
@@ -190,14 +193,15 @@
|
||||
<Label for="wishlist-end-date">{t.form.endDateOptional}</Label>
|
||||
<div class="flex items-center gap-2">
|
||||
{#if wishlistEndDate}
|
||||
<button
|
||||
type="button"
|
||||
<IconButton
|
||||
onclick={clearEndDate}
|
||||
class="flex-shrink-0 w-8 h-8 flex items-center justify-center rounded-full border border-input hover:bg-accent transition-colors"
|
||||
color={wishlistColor}
|
||||
size="sm"
|
||||
class="flex-shrink-0"
|
||||
aria-label="Clear end date"
|
||||
>
|
||||
<X class="w-4 h-4" />
|
||||
</button>
|
||||
</IconButton>
|
||||
{/if}
|
||||
<Input
|
||||
id="wishlist-end-date"
|
||||
|
||||
Reference in New Issue
Block a user