add svelte-sonner for toast notifications
This commit is contained in:
parent
1933f3d8e9
commit
513c7660f5
6 changed files with 91 additions and 33 deletions
|
|
@ -79,6 +79,7 @@
|
||||||
"modern-normalize": "^3.0.1",
|
"modern-normalize": "^3.0.1",
|
||||||
"runed": "^0.31.1",
|
"runed": "^0.31.1",
|
||||||
"svelecte": "^5.3.0",
|
"svelecte": "^5.3.0",
|
||||||
|
"svelte-sonner": "^1.0.7",
|
||||||
"wx-grid-data-provider": "^2.2.0",
|
"wx-grid-data-provider": "^2.2.0",
|
||||||
"wx-svelte-grid": "^2.0.0",
|
"wx-svelte-grid": "^2.0.0",
|
||||||
"zod": "^4.1.5"
|
"zod": "^4.1.5"
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,9 @@ importers:
|
||||||
svelecte:
|
svelecte:
|
||||||
specifier: ^5.3.0
|
specifier: ^5.3.0
|
||||||
version: 5.3.0(svelte@5.38.7)
|
version: 5.3.0(svelte@5.38.7)
|
||||||
|
svelte-sonner:
|
||||||
|
specifier: ^1.0.7
|
||||||
|
version: 1.0.7(svelte@5.38.7)
|
||||||
wx-grid-data-provider:
|
wx-grid-data-provider:
|
||||||
specifier: ^2.2.0
|
specifier: ^2.2.0
|
||||||
version: 2.2.0
|
version: 2.2.0
|
||||||
|
|
@ -2390,6 +2393,11 @@ packages:
|
||||||
run-parallel@1.2.0:
|
run-parallel@1.2.0:
|
||||||
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
||||||
|
|
||||||
|
runed@0.28.0:
|
||||||
|
resolution: {integrity: sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ==}
|
||||||
|
peerDependencies:
|
||||||
|
svelte: ^5.7.0
|
||||||
|
|
||||||
runed@0.29.2:
|
runed@0.29.2:
|
||||||
resolution: {integrity: sha512-0cq6cA6sYGZwl/FvVqjx9YN+1xEBu9sDDyuWdDW1yWX7JF2wmvmVKfH+hVCZs+csW+P3ARH92MjI3H9QTagOQA==}
|
resolution: {integrity: sha512-0cq6cA6sYGZwl/FvVqjx9YN+1xEBu9sDDyuWdDW1yWX7JF2wmvmVKfH+hVCZs+csW+P3ARH92MjI3H9QTagOQA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|
@ -2557,6 +2565,11 @@ packages:
|
||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
svelte-sonner@1.0.7:
|
||||||
|
resolution: {integrity: sha512-1EUFYmd7q/xfs2qCHwJzGPh9n5VJ3X6QjBN10fof2vxgy8fYE7kVfZ7uGnd7i6fQaWIr5KvXcwYXE/cmTEjk5A==}
|
||||||
|
peerDependencies:
|
||||||
|
svelte: ^5.0.0
|
||||||
|
|
||||||
svelte-toolbelt@0.9.3:
|
svelte-toolbelt@0.9.3:
|
||||||
resolution: {integrity: sha512-HCSWxCtVmv+c6g1ACb8LTwHVbDqLKJvHpo6J8TaqwUme2hj9ATJCpjCPNISR1OCq2Q4U1KT41if9ON0isINQZw==}
|
resolution: {integrity: sha512-HCSWxCtVmv+c6g1ACb8LTwHVbDqLKJvHpo6J8TaqwUme2hj9ATJCpjCPNISR1OCq2Q4U1KT41if9ON0isINQZw==}
|
||||||
engines: {node: '>=18', pnpm: '>=8.7.0'}
|
engines: {node: '>=18', pnpm: '>=8.7.0'}
|
||||||
|
|
@ -5279,6 +5292,11 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
queue-microtask: 1.2.3
|
queue-microtask: 1.2.3
|
||||||
|
|
||||||
|
runed@0.28.0(svelte@5.38.7):
|
||||||
|
dependencies:
|
||||||
|
esm-env: 1.2.2
|
||||||
|
svelte: 5.38.7
|
||||||
|
|
||||||
runed@0.29.2(svelte@5.38.7):
|
runed@0.29.2(svelte@5.38.7):
|
||||||
dependencies:
|
dependencies:
|
||||||
esm-env: 1.2.2
|
esm-env: 1.2.2
|
||||||
|
|
@ -5424,6 +5442,11 @@ snapshots:
|
||||||
sass: 1.92.1
|
sass: 1.92.1
|
||||||
typescript: 5.9.2
|
typescript: 5.9.2
|
||||||
|
|
||||||
|
svelte-sonner@1.0.7(svelte@5.38.7):
|
||||||
|
dependencies:
|
||||||
|
runed: 0.28.0(svelte@5.38.7)
|
||||||
|
svelte: 5.38.7
|
||||||
|
|
||||||
svelte-toolbelt@0.9.3(svelte@5.38.7):
|
svelte-toolbelt@0.9.3(svelte@5.38.7):
|
||||||
dependencies:
|
dependencies:
|
||||||
clsx: 2.1.1
|
clsx: 2.1.1
|
||||||
|
|
|
||||||
32
src/app.scss
32
src/app.scss
|
|
@ -78,3 +78,35 @@ $font-size-base: 16px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Toast notification overrides (svelte-sonner)
|
||||||
|
[data-sonner-toaster] {
|
||||||
|
--normal-bg: var(--toast-bg);
|
||||||
|
--normal-text: var(--toast-text);
|
||||||
|
--normal-border: var(--toast-border);
|
||||||
|
|
||||||
|
--success-bg: var(--toast-success-bg);
|
||||||
|
--success-text: var(--toast-success-text);
|
||||||
|
--success-border: var(--toast-success-text);
|
||||||
|
|
||||||
|
--error-bg: var(--toast-error-bg);
|
||||||
|
--error-text: var(--toast-error-text);
|
||||||
|
--error-border: var(--toast-error-text);
|
||||||
|
|
||||||
|
--warning-bg: var(--toast-warning-bg);
|
||||||
|
--warning-text: var(--toast-warning-text);
|
||||||
|
--warning-border: var(--toast-warning-text);
|
||||||
|
|
||||||
|
--info-bg: var(--toast-info-bg);
|
||||||
|
--info-text: var(--toast-info-text);
|
||||||
|
--info-border: var(--toast-info-text);
|
||||||
|
|
||||||
|
font-family: var(--font-family);
|
||||||
|
z-index: 102; // Above dialogs (101)
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-sonner-toast] {
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
font-size: 1.4rem;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,35 @@
|
||||||
<svelte:options runes={true} />
|
<svelte:options runes={true} />
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { toast } from 'svelte-sonner'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
value: string | number
|
value: string | number
|
||||||
}
|
}
|
||||||
|
|
||||||
let { value }: Props = $props()
|
let { value }: Props = $props()
|
||||||
|
|
||||||
let copied = $state(false)
|
|
||||||
|
|
||||||
async function copyToClipboard() {
|
async function copyToClipboard() {
|
||||||
if (!value) return
|
if (!value) return
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(String(value))
|
await navigator.clipboard.writeText(String(value))
|
||||||
copied = true
|
toast.success('Copied to clipboard')
|
||||||
setTimeout(() => {
|
|
||||||
copied = false
|
|
||||||
}, 1500)
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to copy:', err)
|
toast.error('Failed to copy')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button class="copyable-text" class:copied onclick={copyToClipboard} title="Click to copy">
|
<button class="copyable-text" onclick={copyToClipboard} title="Click to copy">
|
||||||
<span class="text">{value}</span>
|
<span class="text">{value}</span>
|
||||||
{#if copied}
|
|
||||||
<span class="copied-indicator">Copied!</span>
|
|
||||||
{/if}
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@use '$src/themes/colors' as colors;
|
@use '$src/themes/colors' as colors;
|
||||||
@use '$src/themes/spacing' as spacing;
|
|
||||||
@use '$src/themes/typography' as typography;
|
|
||||||
|
|
||||||
.copyable-text {
|
.copyable-text {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: spacing.$unit;
|
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
@ -52,10 +43,6 @@
|
||||||
&:hover {
|
&:hover {
|
||||||
color: colors.$grey-10;
|
color: colors.$grey-10;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.copied .text {
|
|
||||||
color: colors.$grey-50;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
|
|
@ -63,19 +50,4 @@
|
||||||
text-decoration-style: dotted;
|
text-decoration-style: dotted;
|
||||||
text-underline-offset: 2px;
|
text-underline-offset: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.copied-indicator {
|
|
||||||
font-size: typography.$font-small;
|
|
||||||
color: colors.$grey-50;
|
|
||||||
animation: fadeIn 0.15s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fadeIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
import { browser } from '$app/environment'
|
import { browser } from '$app/environment'
|
||||||
import { QueryClientProvider } from '@tanstack/svelte-query'
|
import { QueryClientProvider } from '@tanstack/svelte-query'
|
||||||
|
import { Toaster } from 'svelte-sonner'
|
||||||
import type { LayoutData } from './$types'
|
import type { LayoutData } from './$types'
|
||||||
|
|
||||||
const { data, children } = $props<{
|
const { data, children } = $props<{
|
||||||
|
|
@ -18,5 +19,6 @@
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<QueryClientProvider client={data.queryClient}>
|
<QueryClientProvider client={data.queryClient}>
|
||||||
|
<Toaster position="bottom-right" richColors toastOptions={{ duration: 4000 }} />
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
|
|
|
||||||
|
|
@ -376,6 +376,20 @@
|
||||||
--light-shadow-hover: #{colors.$light--shadow--light--hover};
|
--light-shadow-hover: #{colors.$light--shadow--light--hover};
|
||||||
--light-accent: #{colors.$light--bg--light};
|
--light-accent: #{colors.$light--bg--light};
|
||||||
|
|
||||||
|
// Toast notifications
|
||||||
|
--toast-bg: #{colors.$grey-100};
|
||||||
|
--toast-text: #{colors.$text--primary--color--light};
|
||||||
|
--toast-text-secondary: #{colors.$text--secondary--color--light};
|
||||||
|
--toast-border: #{colors.$border--subtle--light};
|
||||||
|
--toast-success-bg: #e8f5e9;
|
||||||
|
--toast-success-text: #2e7d32;
|
||||||
|
--toast-error-bg: #{colors.$error--bg--light};
|
||||||
|
--toast-error-text: #{colors.$error};
|
||||||
|
--toast-warning-bg: #{colors.$notice--bg--light};
|
||||||
|
--toast-warning-text: #{colors.$notice--text--light};
|
||||||
|
--toast-info-bg: #e3f2fd;
|
||||||
|
--toast-info-text: #1565c0;
|
||||||
|
|
||||||
// Gradients
|
// Gradients
|
||||||
--hero-gradient: #{effects.$hero--gradient--light};
|
--hero-gradient: #{effects.$hero--gradient--light};
|
||||||
--hero-gradient-overlay: #{effects.$hero--gradient--light--overlay};
|
--hero-gradient-overlay: #{effects.$hero--gradient--light--overlay};
|
||||||
|
|
@ -728,6 +742,20 @@
|
||||||
--light-shadow-hover: #{colors.$light--shadow--dark--hover};
|
--light-shadow-hover: #{colors.$light--shadow--dark--hover};
|
||||||
--light-accent: #{colors.$light--bg--dark};
|
--light-accent: #{colors.$light--bg--dark};
|
||||||
|
|
||||||
|
// Toast notifications
|
||||||
|
--toast-bg: #{colors.$grey-20};
|
||||||
|
--toast-text: #{colors.$text--primary--color--dark};
|
||||||
|
--toast-text-secondary: #{colors.$text--secondary--color--dark};
|
||||||
|
--toast-border: #{colors.$border--subtle--dark};
|
||||||
|
--toast-success-bg: #1b3d1f;
|
||||||
|
--toast-success-text: #66bb6a;
|
||||||
|
--toast-error-bg: #{colors.$error--bg--dark};
|
||||||
|
--toast-error-text: #{colors.$error};
|
||||||
|
--toast-warning-bg: #{colors.$notice--bg--dark};
|
||||||
|
--toast-warning-text: #{colors.$notice--text--dark};
|
||||||
|
--toast-info-bg: #0d2744;
|
||||||
|
--toast-info-text: #64b5f6;
|
||||||
|
|
||||||
// Gradients
|
// Gradients
|
||||||
--hero-gradient: #{effects.$hero--gradient--dark};
|
--hero-gradient: #{effects.$hero--gradient--dark};
|
||||||
--hero-gradient-overlay: #{effects.$hero--gradient--dark--overlay};
|
--hero-gradient-overlay: #{effects.$hero--gradient--dark--overlay};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue