@x33025/sveltely 0.0.47 → 0.0.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { CircleAlertIcon } from '@lucide/svelte';
|
|
2
3
|
import type { Component } from 'svelte';
|
|
3
4
|
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
4
5
|
import Spinner from './Spinner.svelte';
|
|
6
|
+
import Tooltip from './Tooltip.svelte';
|
|
5
7
|
|
|
6
8
|
type Props = {
|
|
7
9
|
icon?: Component<{ class?: string }>;
|
|
8
10
|
label: string;
|
|
9
11
|
action: () => void | Promise<void>;
|
|
12
|
+
error?: unknown | null;
|
|
13
|
+
style?: 'iconAndLabel' | 'iconOnly';
|
|
10
14
|
} & Omit<HTMLButtonAttributes, 'children' | 'onclick'>;
|
|
11
15
|
|
|
12
16
|
let {
|
|
13
17
|
icon,
|
|
14
18
|
label,
|
|
15
19
|
action,
|
|
20
|
+
error = $bindable<unknown | null>(null),
|
|
21
|
+
style: styleMode = 'iconAndLabel',
|
|
16
22
|
disabled = false,
|
|
17
23
|
class: className = '',
|
|
18
24
|
type = 'button',
|
|
@@ -23,28 +29,46 @@
|
|
|
23
29
|
|
|
24
30
|
const handleClick = async () => {
|
|
25
31
|
if (disabled || pending) return;
|
|
32
|
+
error = null;
|
|
26
33
|
pending = true;
|
|
27
34
|
try {
|
|
28
35
|
await action();
|
|
36
|
+
} catch (caught) {
|
|
37
|
+
error = caught;
|
|
29
38
|
} finally {
|
|
30
39
|
pending = false;
|
|
31
40
|
}
|
|
32
41
|
};
|
|
42
|
+
|
|
43
|
+
const errorLabel = $derived.by(() => {
|
|
44
|
+
if (!error) return null;
|
|
45
|
+
if (typeof error === 'string' && error.trim().length > 0) return error;
|
|
46
|
+
if (error instanceof Error && error.message.trim().length > 0) return error.message;
|
|
47
|
+
return 'Something went wrong';
|
|
48
|
+
});
|
|
33
49
|
</script>
|
|
34
50
|
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
{
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
<Tooltip label={errorLabel ?? ''} disabled={!errorLabel}>
|
|
52
|
+
<button
|
|
53
|
+
{type}
|
|
54
|
+
class="inline-flex items-center gap-2 disabled:cursor-not-allowed disabled:opacity-50 {className}"
|
|
55
|
+
disabled={disabled || pending}
|
|
56
|
+
aria-busy={pending}
|
|
57
|
+
aria-label={styleMode === 'iconOnly' ? label : undefined}
|
|
58
|
+
data-error={error ? 'true' : 'false'}
|
|
59
|
+
{...props}
|
|
60
|
+
onclick={handleClick}
|
|
61
|
+
>
|
|
62
|
+
{#if pending}
|
|
63
|
+
<Spinner class="size-4" />
|
|
64
|
+
{:else if error}
|
|
65
|
+
<CircleAlertIcon class="size-4" />
|
|
66
|
+
{:else if icon}
|
|
67
|
+
{@const Icon = icon}
|
|
68
|
+
<Icon class="size-4" />
|
|
69
|
+
{/if}
|
|
70
|
+
{#if styleMode === 'iconAndLabel'}
|
|
71
|
+
<span>{label}</span>
|
|
72
|
+
{/if}
|
|
73
|
+
</button>
|
|
74
|
+
</Tooltip>
|
|
@@ -6,7 +6,9 @@ type Props = {
|
|
|
6
6
|
}>;
|
|
7
7
|
label: string;
|
|
8
8
|
action: () => void | Promise<void>;
|
|
9
|
+
error?: unknown | null;
|
|
10
|
+
style?: 'iconAndLabel' | 'iconOnly';
|
|
9
11
|
} & Omit<HTMLButtonAttributes, 'children' | 'onclick'>;
|
|
10
|
-
declare const AsyncButton: Component<Props, {}, "">;
|
|
12
|
+
declare const AsyncButton: Component<Props, {}, "error">;
|
|
11
13
|
type AsyncButton = ReturnType<typeof AsyncButton>;
|
|
12
14
|
export default AsyncButton;
|
package/dist/style/index.css
CHANGED
package/dist/style.css
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
|
8
8
|
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
|
|
9
9
|
"Courier New", monospace;
|
|
10
|
+
--color-red-500: oklch(63.7% 0.237 25.331);
|
|
11
|
+
--color-red-600: oklch(57.7% 0.245 27.325);
|
|
10
12
|
--color-red-700: oklch(50.5% 0.213 27.518);
|
|
11
13
|
--color-gray-200: oklch(92.8% 0.006 264.531);
|
|
12
14
|
--color-gray-700: oklch(37.3% 0.034 259.733);
|
|
@@ -367,6 +369,9 @@
|
|
|
367
369
|
.border-zinc-200 {
|
|
368
370
|
border-color: var(--color-zinc-200);
|
|
369
371
|
}
|
|
372
|
+
.border-zinc-300 {
|
|
373
|
+
border-color: var(--color-zinc-300);
|
|
374
|
+
}
|
|
370
375
|
.bg-white {
|
|
371
376
|
background-color: var(--color-white);
|
|
372
377
|
}
|
|
@@ -540,6 +545,16 @@
|
|
|
540
545
|
opacity: 50%;
|
|
541
546
|
}
|
|
542
547
|
}
|
|
548
|
+
.error\:border-red-500 {
|
|
549
|
+
&[data-error='true'] {
|
|
550
|
+
border-color: var(--color-red-500);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
.error\:text-red-600 {
|
|
554
|
+
&[data-error='true'] {
|
|
555
|
+
color: var(--color-red-600);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
543
558
|
}
|
|
544
559
|
@layer base {
|
|
545
560
|
html, body {
|