mirror of
https://github.com/podman-desktop/podman-desktop
synced 2026-04-21 17:47:22 +00:00
refactor(Button): migrate to svelte5 (#12079)
* refactor(Button): migrate to svelte5 Signed-off-by: axel7083 <42176370+axel7083@users.noreply.github.com> * fix: support legacy Signed-off-by: axel7083 <42176370+axel7083@users.noreply.github.com> * fix: make aria-label optional Signed-off-by: axel7083 <42176370+axel7083@users.noreply.github.com> * fix: should not use render for non-snippet Signed-off-by: axel7083 <42176370+axel7083@users.noreply.github.com> --------- Signed-off-by: axel7083 <42176370+axel7083@users.noreply.github.com>
This commit is contained in:
parent
b0ebc44707
commit
c40ad4e0ed
1 changed files with 60 additions and 45 deletions
|
|
@ -1,101 +1,116 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import type { Snippet } from 'svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import Fa from 'svelte-fa';
|
||||
|
||||
import Spinner from '../progress/Spinner.svelte';
|
||||
import { isFontAwesomeIcon } from '../utils/icon-utils';
|
||||
import type { ButtonType } from './Button';
|
||||
|
||||
export let title: string | undefined = undefined;
|
||||
export let inProgress = false;
|
||||
export let disabled = false;
|
||||
export let type: ButtonType = 'primary';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export let icon: any = undefined;
|
||||
export let selected: boolean | undefined = undefined;
|
||||
|
||||
$: if (selected !== undefined && type !== 'tab') {
|
||||
console.error('property selected can be used with type=tab only');
|
||||
interface Props {
|
||||
title?: string;
|
||||
inProgress?: boolean;
|
||||
disabled?: boolean;
|
||||
type?: ButtonType;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
icon?: any;
|
||||
selected?: boolean;
|
||||
padding?: string;
|
||||
class?: string;
|
||||
hidden?: boolean;
|
||||
'aria-label'?: string;
|
||||
onclick?: () => void;
|
||||
children?: Snippet;
|
||||
}
|
||||
export let padding: string =
|
||||
'px-4 ' + (type === 'tab' ? 'pb-1' : type === 'secondary' ? 'py-[4px]' : type === 'danger' ? 'py-[3px]' : 'py-[5px]');
|
||||
|
||||
let iconType: string | undefined = undefined;
|
||||
// support legacy usage (on:click)
|
||||
const dispatch = createEventDispatcher<{ click: undefined }>();
|
||||
|
||||
onMount(() => {
|
||||
if (isFontAwesomeIcon(icon)) {
|
||||
iconType = 'fa';
|
||||
} else {
|
||||
iconType = 'unknown';
|
||||
}
|
||||
});
|
||||
let {
|
||||
title,
|
||||
inProgress = false,
|
||||
disabled = false,
|
||||
type = 'primary',
|
||||
icon,
|
||||
selected,
|
||||
padding = 'px-4 ' +
|
||||
(type === 'tab' ? 'pb-1' : type === 'secondary' ? 'py-[4px]' : type === 'danger' ? 'py-[3px]' : 'py-[5px]'),
|
||||
class: classNames,
|
||||
hidden,
|
||||
'aria-label': ariaLabel,
|
||||
onclick = dispatch.bind(undefined, 'click'),
|
||||
children,
|
||||
}: Props = $props();
|
||||
|
||||
let classes = '';
|
||||
$: {
|
||||
let iconType: string = $derived(isFontAwesomeIcon(icon) ? 'fa' : 'unknown');
|
||||
|
||||
let classes = $derived.by(() => {
|
||||
let result: string = '';
|
||||
if (disabled || inProgress) {
|
||||
if (type === 'primary') {
|
||||
classes = 'bg-[var(--pd-button-disabled)]';
|
||||
result = 'bg-[var(--pd-button-disabled)]';
|
||||
} else if (type === 'secondary') {
|
||||
classes = 'border-[1px] border-[var(--pd-button-disabled)] bg-[var(--pd-button-disabled)]';
|
||||
result = 'border-[1px] border-[var(--pd-button-disabled)] bg-[var(--pd-button-disabled)]';
|
||||
} else if (type === 'danger') {
|
||||
classes =
|
||||
result =
|
||||
'border-2 border-[var(--pd-button-danger-disabled-border)] text-[var(--pd-button-danger-disabled-text)] bg-[var(--pd-button-danger-disabled-bg)]';
|
||||
}
|
||||
if (type !== 'danger') {
|
||||
classes += ' text-[var(--pd-button-disabled-text)]';
|
||||
result += ' text-[var(--pd-button-disabled-text)]';
|
||||
}
|
||||
} else if (type === 'primary') {
|
||||
classes =
|
||||
result =
|
||||
'bg-[var(--pd-button-primary-bg)] text-[var(--pd-button-text)] border-none hover:bg-[var(--pd-button-primary-hover-bg)]';
|
||||
} else if (type === 'secondary') {
|
||||
classes =
|
||||
result =
|
||||
'border-[1px] border-[var(--pd-button-secondary)] text-[var(--pd-button-secondary)] hover:bg-[var(--pd-button-secondary-hover)] hover:border-[var(--pd-button-secondary-hover)] hover:text-[var(--pd-button-text)]';
|
||||
} else if (type === 'danger') {
|
||||
classes =
|
||||
result =
|
||||
'border-2 border-[var(--pd-button-danger-border)] bg-[var(--pd-button-danger-bg)] text-[var(--pd-button-danger-text)] hover:bg-[var(--pd-button-danger-hover-bg)] hover:text-[var(--pd-button-danger-hover-text)]';
|
||||
} else if (type === 'tab') {
|
||||
classes = 'border-b-[3px] border-[var(--pd-button-tab-border)]';
|
||||
result = 'border-b-[3px] border-[var(--pd-button-tab-border)]';
|
||||
} else {
|
||||
// link
|
||||
classes = 'border-none text-[var(--pd-button-link-text)] hover:bg-[var(--pd-button-link-hover-bg)]';
|
||||
result = 'border-none text-[var(--pd-button-link-text)] hover:bg-[var(--pd-button-link-hover-bg)]';
|
||||
}
|
||||
|
||||
if (type !== 'tab') {
|
||||
classes += ' rounded-[4px]';
|
||||
result += ' rounded-[4px]';
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
</script>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="relative {padding} box-border whitespace-nowrap select-none transition-all outline-transparent focus:outline-[var(--pd-button-primary-hover-bg)] {classes} {$$props.class ??
|
||||
''}"
|
||||
class="relative {padding} box-border whitespace-nowrap select-none transition-all outline-transparent focus:outline-[var(--pd-button-primary-hover-bg)] {classes} {classNames}"
|
||||
class:border-[var(--pd-button-tab-border-selected)]={type === 'tab' && selected}
|
||||
class:hover:border-[var(--pd-button-tab-hover-border)]={type === 'tab' && !selected}
|
||||
class:text-[var(--pd-button-tab-text-selected)]={type === 'tab' && selected}
|
||||
class:text-[var(--pd-button-tab-text)]={type === 'tab' && !selected}
|
||||
hidden={$$props.hidden}
|
||||
hidden={hidden}
|
||||
title={title}
|
||||
aria-label={$$props['aria-label']}
|
||||
on:click
|
||||
aria-label={ariaLabel}
|
||||
onclick={onclick}
|
||||
disabled={disabled || inProgress}>
|
||||
{#if icon ?? inProgress}
|
||||
<div
|
||||
class="flex flex-row p-0 m-0 bg-transparent justify-center items-center space-x-[4px]"
|
||||
class:py-[3px]={!$$slots.default}>
|
||||
class:py-[3px]={!children}>
|
||||
{#if inProgress}
|
||||
<Spinner size="1em" />
|
||||
{:else if isFontAwesomeIcon(icon)}
|
||||
<Fa icon={icon} />
|
||||
{:else if iconType === 'unknown'}
|
||||
<svelte:component this={icon} />
|
||||
{@const Icon = icon}
|
||||
<Icon/>
|
||||
{/if}
|
||||
{#if $$slots.default}
|
||||
<span><slot /></span>
|
||||
{#if children}
|
||||
<span>{@render children()}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
{/if}
|
||||
</button>
|
||||
|
|
|
|||
Loading…
Reference in a new issue