chore(web): migrate CircleIconButton to @immich/ui IconButton (#18486)

* remove import and referenced file

* first pass at replacing all CircleIconButtons

* fix linting issues

* fix combobox formatting issues

* fix button context menu coloring

* remove circle icon button from search history box

* use theme switcher from UI lib

* dark mode force the asset viewer icons

* fix forced dark mode icons

* dark mode memory viewer icons

* fix: back button in memory viewer

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
Brandon Wees 2025-06-02 09:47:23 -05:00 committed by GitHub
parent d544053c67
commit a02e1f5e7c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
75 changed files with 822 additions and 556 deletions

View file

@ -1,29 +0,0 @@
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
import { render, screen } from '@testing-library/svelte';
describe('CircleIconButton component', () => {
it('should render as a button', () => {
render(CircleIconButton, { icon: '', title: 'test' });
const button = screen.getByRole('button');
expect(button).toBeInTheDocument();
expect(button).toHaveAttribute('type', 'button');
expect(button).not.toHaveAttribute('href');
expect(button).toHaveAttribute('title', 'test');
});
it('should render as a link if href prop is set', () => {
render(CircleIconButton, { props: { href: '/test', icon: '', title: 'test' } });
const link = screen.getByRole('link');
expect(link).toBeInTheDocument();
expect(link).toHaveAttribute('href', '/test');
expect(link).not.toHaveAttribute('type');
});
it('should render icon inside button', () => {
render(CircleIconButton, { icon: '', title: 'test' });
const button = screen.getByRole('button');
const icon = button.querySelector('svg');
expect(icon).toBeInTheDocument();
expect(icon).toHaveAttribute('aria-label', 'test');
});
});

View file

@ -1,100 +0,0 @@
<script lang="ts" module>
export type Color = 'transparent' | 'light' | 'dark' | 'red' | 'gray' | 'primary' | 'opaque' | 'alert' | 'neutral';
export type Padding = '1' | '2' | '3';
</script>
<script lang="ts">
import Icon from '$lib/components/elements/icon.svelte';
/**
* Override the default styling of the button for specific use cases, such as the icon color.
*/
interface Props {
id?: string;
type?: string;
href?: string;
icon: string;
color?: Color;
title: string;
/**
* The padding of the button, used by the `p-{padding}` Tailwind CSS class.
*/
padding?: Padding;
/**
* Size of the button, used for a CSS value.
*/
size?: string;
hideMobile?: boolean;
buttonSize?: string | undefined;
/**
* viewBox attribute for the SVG icon.
*/
viewBox?: string | undefined;
class?: string;
'aria-hidden'?: boolean | undefined | null;
'aria-checked'?: 'true' | 'false' | undefined | null;
'aria-current'?: 'page' | 'step' | 'location' | 'date' | 'time' | 'true' | 'false' | undefined | null;
'aria-controls'?: string | undefined | null;
'aria-expanded'?: boolean;
'aria-haspopup'?: boolean;
tabindex?: number | undefined | null;
role?: string | undefined | null;
onclick: (e: MouseEvent) => void;
disabled?: boolean;
}
let {
type = 'button',
href = undefined,
icon,
color = 'transparent',
title,
padding = '3',
size = '24',
hideMobile = false,
buttonSize = undefined,
viewBox = undefined,
class: className = '',
onclick,
...rest
}: Props = $props();
const colorClasses: Record<Color, string> = {
transparent: 'bg-transparent hover:bg-[#d3d3d3] dark:text-immich-dark-fg',
opaque: 'bg-transparent hover:bg-immich-bg/30 text-white hover:dark:text-white',
light: 'bg-white hover:bg-[#d3d3d3]',
red: 'text-red-400 bg-red-100 hover:bg-[#d3d3d3]',
dark: 'bg-[#202123] hover:bg-[#d3d3d3]',
alert: 'text-[#ff0000] hover:text-white',
gray: 'bg-[#d3d3d3] hover:bg-[#e2e7e9] text-immich-dark-gray hover:text-black',
neutral:
'dark:bg-immich-dark-gray dark:text-gray-300 hover:dark:bg-immich-dark-gray/50 hover:dark:text-gray-300 bg-gray-200 text-gray-700 hover:bg-gray-300',
primary:
'bg-immich-primary dark:bg-immich-dark-primary hover:bg-immich-primary/75 hover:dark:bg-immich-dark-primary/80 text-white dark:text-immich-dark-gray',
};
const paddingClasses: Record<Padding, string> = {
'1': 'p-1',
'2': 'p-2',
'3': 'p-3',
};
let colorClass = $derived(colorClasses[color]);
let mobileClass = $derived(hideMobile ? 'hidden sm:flex' : '');
let paddingClass = $derived(paddingClasses[padding]);
</script>
<svelte:element
this={href ? 'a' : 'button'}
type={href ? undefined : type}
{title}
{href}
style:width={buttonSize ? buttonSize + 'px' : ''}
style:height={buttonSize ? buttonSize + 'px' : ''}
class="flex place-content-center place-items-center rounded-full {colorClass} {paddingClass} transition-all disabled:cursor-default hover:dark:text-immich-dark-gray {className} {mobileClass}"
{onclick}
{...rest}
>
<Icon path={icon} {size} ariaLabel={title} {viewBox} color="currentColor" />
</svelte:element>

View file

@ -1,9 +1,9 @@
<script lang="ts">
import { mdiClose, mdiMagnify } from '@mdi/js';
import type { SearchOptions } from '$lib/utils/dipatch';
import { mdiClose, mdiMagnify } from '@mdi/js';
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
import { t } from 'svelte-i18n';
import { IconButton } from '@immich/ui';
interface Props {
name: string;
@ -43,11 +43,13 @@
? 'rounded-2xl'
: 'rounded-t-lg'} bg-gray-200 p-2 dark:bg-immich-dark-gray gap-2 place-items-center h-full"
>
<CircleIconButton
<IconButton
shape="round"
color="secondary"
variant="ghost"
icon={mdiMagnify}
title={$t('search')}
size="16"
padding="2"
aria-label={$t('search')}
size="small"
onclick={() => onSearch({ force: true })}
/>
<input
@ -65,6 +67,14 @@
</div>
{/if}
{#if name}
<CircleIconButton icon={mdiClose} title={$t('clear_value')} size="16" padding="2" onclick={resetSearch} />
<IconButton
shape="round"
color="secondary"
variant="ghost"
icon={mdiClose}
aria-label={$t('clear_value')}
size="small"
onclick={resetSearch}
/>
{/if}
</div>