From cc96af201a48d6d537ac7abae65901e5a604f3c4 Mon Sep 17 00:00:00 2001 From: kgridou <32600911+kgridou@users.noreply.github.com> Date: Tue, 17 Feb 2026 23:36:10 +0100 Subject: [PATCH] toast swipe --- .../src/lib/components/toast/toast-stack.ts | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/libs/ui-lab/src/lib/components/toast/toast-stack.ts b/libs/ui-lab/src/lib/components/toast/toast-stack.ts index 8a0aebad4..4d84d83da 100644 --- a/libs/ui-lab/src/lib/components/toast/toast-stack.ts +++ b/libs/ui-lab/src/lib/components/toast/toast-stack.ts @@ -29,6 +29,9 @@ import { ScToaster } from './toaster'; (animationend)="onAnimationEnd($event, toast.id)" (pointerEnter)="toastService.pause(toast.id)" (pointerLeave)="toastService.resume(toast.id)" + (touchstart)="onTouchStart($event)" + (touchmove)="onTouchMove($event)" + (touchend)="onTouchEnd($event, toast.id)" >
@if (toast.title) { @@ -77,6 +80,8 @@ import { ScToaster } from './toaster'; export class ScToastStack { protected readonly toastService = inject(ScToaster); + private readonly swipeThreshold = 100; + protected dismiss(id: string): void { this.toastService.dismiss(id); } @@ -95,4 +100,36 @@ export class ScToastStack { this.toastService.remove(id); } } + + protected onTouchStart(event: TouchEvent): void { + const el = event.currentTarget as HTMLElement; + el.dataset['swipeStartX'] = String(event.touches[0].clientX); + el.style.transition = 'none'; + } + + protected onTouchMove(event: TouchEvent): void { + const el = event.currentTarget as HTMLElement; + const startX = Number(el.dataset['swipeStartX'] ?? 0); + const deltaX = event.touches[0].clientX - startX; + + el.style.transform = `translateX(${deltaX}px)`; + el.style.opacity = String( + Math.max(0, 1 - Math.abs(deltaX) / this.swipeThreshold), + ); + } + + protected onTouchEnd(event: TouchEvent, id: string): void { + const el = event.currentTarget as HTMLElement; + const startX = Number(el.dataset['swipeStartX'] ?? 0); + const deltaX = event.changedTouches[0].clientX - startX; + + if (Math.abs(deltaX) >= this.swipeThreshold) { + this.toastService.dismiss(id); + } else { + // Snap back + el.style.transition = 'transform 0.3s ease, opacity 0.3s ease'; + el.style.transform = ''; + el.style.opacity = ''; + } + } }