feat: create reusable CloseButton icon component

- Create CloseButton.svelte with configurable size, color, strokeWidth props
- Replace inline close button SVGs in 7 components
- Update Modal, Lightbox, MediaDetailsModal, MediaInput, GalleryManager, AlbumSelectorModal, UnifiedMediaModal

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Justin Edmund 2025-06-26 08:40:32 -04:00
parent 5370ae020d
commit df3dd1abd9
8 changed files with 47 additions and 101 deletions

View file

@ -1,6 +1,7 @@
<script lang="ts">
import { onMount } from 'svelte'
import { fade, scale } from 'svelte/transition'
import CloseButton from '$components/icons/CloseButton.svelte'
// Convert CSS transition durations to milliseconds for Svelte transitions
const TRANSITION_NORMAL_MS = 200 // $transition-normal: 0.2s
@ -111,20 +112,7 @@
</div>
<button class="lightbox-close" onclick={close} aria-label="Close lightbox">
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M18 6L6 18M6 6l12 12"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
/>
</svg>
<CloseButton />
</button>
</div>
{/if}

View file

@ -2,6 +2,7 @@
import Modal from './Modal.svelte'
import AlbumSelector from './AlbumSelector.svelte'
import Button from './Button.svelte'
import CloseButton from '../icons/CloseButton.svelte'
import LoadingSpinner from './LoadingSpinner.svelte'
import type { Album } from '@prisma/client'
@ -86,20 +87,7 @@
<div class="header-top">
<h2>Add to Album</h2>
<button class="close-button" onclick={handleClose} aria-label="Close modal">
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 6L18 18M6 18L18 6"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
/>
</svg>
<CloseButton size={20} />
</button>
</div>
<p class="modal-subtitle">

View file

@ -1,5 +1,6 @@
<script lang="ts">
import Button from './Button.svelte'
import CloseButton from '../icons/CloseButton.svelte'
import UnifiedMediaModal from './UnifiedMediaModal.svelte'
import type { Media } from '@prisma/client'
@ -221,20 +222,7 @@
onclick={() => removeImage(index)}
aria-label="Remove image"
>
<svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 6L18 18M6 18L18 6"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
/>
</svg>
<CloseButton size={14} color="currentColor" />
</button>
<!-- Order Indicator -->

View file

@ -6,6 +6,7 @@
import SmartImage from '../SmartImage.svelte'
import AlbumSelector from './AlbumSelector.svelte'
import AlbumIcon from '$icons/album.svg?component'
import CloseButton from '$components/icons/CloseButton.svelte'
import { authenticatedFetch } from '$lib/admin-auth'
import { toast } from '$lib/stores/toast'
import type { Media } from '@prisma/client'
@ -296,21 +297,7 @@
</svg>
</Button>
<Button variant="ghost" onclick={handleClose} iconOnly aria-label="Close modal">
<svg
slot="icon"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 6L18 18M6 18L18 6"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
/>
</svg>
<CloseButton slot="icon" />
</Button>
{/if}
</div>

View file

@ -1,5 +1,6 @@
<script lang="ts">
import Button from './Button.svelte'
import CloseButton from '../icons/CloseButton.svelte'
import UnifiedMediaModal from './UnifiedMediaModal.svelte'
import type { Media } from '@prisma/client'
@ -209,21 +210,7 @@
<Button variant="ghost" onclick={openModal}>Browse</Button>
{#if hasValue}
<Button variant="ghost" onclick={handleClear} aria-label="Clear selection">
<svg
slot="icon"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 6L18 18M6 18L18 6"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
/>
</svg>
<CloseButton slot="icon" size={16} />
</Button>
{/if}
</div>

View file

@ -1,6 +1,7 @@
<script lang="ts">
import BaseModal from './BaseModal.svelte'
import Button from './Button.svelte'
import CloseButton from '$components/icons/CloseButton.svelte'
interface Props {
isOpen: boolean
@ -41,21 +42,7 @@
aria-label="Close modal"
class="close-button"
>
<svg
slot="icon"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 6L18 18M6 18L18 6"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
/>
</svg>
<CloseButton slot="icon" />
</Button>
{/if}

View file

@ -4,6 +4,7 @@
import Select from './Select.svelte'
import Input from './Input.svelte'
import Button from './Button.svelte'
import CloseButton from '../icons/CloseButton.svelte'
import LoadingSpinner from './LoadingSpinner.svelte'
import SmartImage from '../SmartImage.svelte'
import { InfiniteLoader, LoaderState } from 'svelte-infinite'
@ -307,20 +308,7 @@
<div class="header-top">
<h2>{computedTitle}</h2>
<button class="close-button" onclick={handleClose} aria-label="Close modal">
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 6L18 18M6 18L18 6"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
/>
</svg>
<CloseButton size={20} />
</button>
</div>

View file

@ -0,0 +1,33 @@
<script lang="ts">
interface Props {
size?: number | string
color?: string
strokeWidth?: number
class?: string
}
let {
size = 24,
color = 'currentColor',
strokeWidth = 2,
class: className = ''
}: Props = $props()
</script>
<svg
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
class={className}
aria-hidden="true"
>
<path
d="M18 6L6 18M6 6l12 12"
stroke={color}
stroke-width={strokeWidth}
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>