update: generalize button components
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { X, Pencil } from 'lucide-svelte';
|
import { X, Pencil } from 'lucide-svelte';
|
||||||
|
import IconButton from './IconButton.svelte';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
color = $bindable(null),
|
color = $bindable(null),
|
||||||
@@ -39,14 +40,14 @@
|
|||||||
|
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
{#if color}
|
{#if color}
|
||||||
<button
|
<IconButton
|
||||||
type="button"
|
|
||||||
onclick={clearColor}
|
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"
|
aria-label="Clear color"
|
||||||
>
|
>
|
||||||
<X class={iconSize} />
|
<X class={iconSize} />
|
||||||
</button>
|
</IconButton>
|
||||||
{/if}
|
{/if}
|
||||||
<label
|
<label
|
||||||
class="{buttonSize} flex items-center justify-center rounded-full border border-input hover:opacity-90 transition-opacity cursor-pointer relative overflow-hidden"
|
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>
|
</script>
|
||||||
|
|
||||||
<div class="relative dropdown-menu">
|
<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()}
|
{@render icon()}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
@@ -117,3 +124,9 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</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 { Pencil, Check, X } from "lucide-svelte";
|
||||||
import ColorPicker from "$lib/components/ui/ColorPicker.svelte";
|
import ColorPicker from "$lib/components/ui/ColorPicker.svelte";
|
||||||
import ThemePicker from "$lib/components/ui/theme-picker.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 type { Wishlist } from "$lib/server/schema";
|
||||||
import { languageStore } from '$lib/stores/language.svelte';
|
import { languageStore } from '$lib/stores/language.svelte';
|
||||||
import { getCardStyle } from '$lib/utils/colors';
|
import { getCardStyle } from '$lib/utils/colors';
|
||||||
@@ -100,8 +101,7 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<h1 class="text-3xl font-bold leading-[2.25rem]">{wishlistTitle}</h1>
|
<h1 class="text-3xl font-bold leading-[2.25rem]">{wishlistTitle}</h1>
|
||||||
{/if}
|
{/if}
|
||||||
<button
|
<IconButton
|
||||||
type="button"
|
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
if (editingTitle) {
|
if (editingTitle) {
|
||||||
saveTitle();
|
saveTitle();
|
||||||
@@ -109,7 +109,9 @@
|
|||||||
editingTitle = true;
|
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"}
|
aria-label={editingTitle ? "Save title" : "Edit title"}
|
||||||
>
|
>
|
||||||
{#if editingTitle}
|
{#if editingTitle}
|
||||||
@@ -117,7 +119,7 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<Pencil class="w-4 h-4" />
|
<Pencil class="w-4 h-4" />
|
||||||
{/if}
|
{/if}
|
||||||
</button>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2 shrink-0">
|
<div class="flex items-center gap-2 shrink-0">
|
||||||
<ThemePicker
|
<ThemePicker
|
||||||
@@ -145,8 +147,7 @@
|
|||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<div class="flex items-center justify-between gap-2">
|
<div class="flex items-center justify-between gap-2">
|
||||||
<Label for="wishlist-description">{t.form.descriptionOptional}</Label>
|
<Label for="wishlist-description">{t.form.descriptionOptional}</Label>
|
||||||
<button
|
<IconButton
|
||||||
type="button"
|
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
if (editingDescription) {
|
if (editingDescription) {
|
||||||
saveDescription();
|
saveDescription();
|
||||||
@@ -154,7 +155,9 @@
|
|||||||
editingDescription = true;
|
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"}
|
aria-label={editingDescription ? "Save description" : "Edit description"}
|
||||||
>
|
>
|
||||||
{#if editingDescription}
|
{#if editingDescription}
|
||||||
@@ -162,7 +165,7 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<Pencil class="w-4 h-4" />
|
<Pencil class="w-4 h-4" />
|
||||||
{/if}
|
{/if}
|
||||||
</button>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
{#if editingDescription}
|
{#if editingDescription}
|
||||||
<Textarea
|
<Textarea
|
||||||
@@ -190,14 +193,15 @@
|
|||||||
<Label for="wishlist-end-date">{t.form.endDateOptional}</Label>
|
<Label for="wishlist-end-date">{t.form.endDateOptional}</Label>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
{#if wishlistEndDate}
|
{#if wishlistEndDate}
|
||||||
<button
|
<IconButton
|
||||||
type="button"
|
|
||||||
onclick={clearEndDate}
|
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"
|
aria-label="Clear end date"
|
||||||
>
|
>
|
||||||
<X class="w-4 h-4" />
|
<X class="w-4 h-4" />
|
||||||
</button>
|
</IconButton>
|
||||||
{/if}
|
{/if}
|
||||||
<Input
|
<Input
|
||||||
id="wishlist-end-date"
|
id="wishlist-end-date"
|
||||||
|
|||||||
Reference in New Issue
Block a user