This commit is contained in:
2026-01-26 09:05:48 +08:00
commit f218a21377
20 changed files with 7774 additions and 0 deletions

148
ui/components/toast.slint Normal file
View File

@@ -0,0 +1,148 @@
// Toast Component
// Notification toast with auto-dismiss and animations
import { Theme, Typography, SpacingSystem } from "../theme/theme.slint";
import { Animations } from "../utils/animations.slint";
// Toast message structure
export struct ToastMessage {
message: string,
variant: string, // default | success | error | warning
show: bool,
}
// Single toast item
export component Toast {
// Public properties
in property <string> message: "";
in property <string> variant: "default"; // default | success | error | warning
in-out property <bool> show: false;
// Callbacks
callback dismissed();
// Calculate colors based on variant
private property <color> bg-color: {
if variant == "success" { #10b981 }
else if variant == "error" { Theme.colors.destructive }
else if variant == "warning" { #f59e0b }
else { Theme.colors.card }
};
private property <color> text-color: {
if variant == "success" { #ffffff }
else if variant == "error" { Theme.colors.destructive-foreground }
else if variant == "warning" { #ffffff }
else { Theme.colors.card-foreground }
};
private property <string> icon: {
if variant == "success" { "✓" }
else if variant == "error" { "✕" }
else if variant == "warning" { "⚠" }
else { "" }
};
// Animation properties
property <length> translate-y: show ? 0px : -20px;
property <float> opacity-val: show ? 1.0 : 0.0;
animate translate-y, opacity-val {
duration: Animations.durations.normal;
easing: Animations.ease-out;
}
width: 350px;
height: show ? 60px : 0px;
if show: Rectangle {
y: translate-y;
opacity: opacity-val;
background: bg-color;
border-radius: SpacingSystem.radius.lg;
border-width: 1px;
border-color: Theme.colors.border;
drop-shadow-blur: 10px;
drop-shadow-color: #00000020;
drop-shadow-offset-y: 4px;
HorizontalLayout {
padding: SpacingSystem.spacing.s4;
spacing: SpacingSystem.spacing.s3;
alignment: space-between;
HorizontalLayout {
spacing: SpacingSystem.spacing.s3;
// Icon
Text {
text: icon;
font-size: Typography.sizes.xl;
color: text-color;
vertical-alignment: center;
}
// Message
Text {
text: message;
font-size: Typography.sizes.base;
color: text-color;
vertical-alignment: center;
wrap: word-wrap;
}
}
// Close button
Rectangle {
width: 24px;
height: 24px;
border-radius: SpacingSystem.radius.sm;
background: close-touch.has-hover ? #00000020 : Colors.transparent;
animate background {
duration: Animations.durations.fast;
easing: Animations.ease-in-out;
}
Text {
text: "✕";
font-size: Typography.sizes.sm;
color: text-color;
horizontal-alignment: center;
vertical-alignment: center;
}
close-touch := TouchArea {
clicked => {
root.dismissed();
}
}
}
}
}
}
// Toast container (manages multiple toasts)
export component ToastContainer {
in property <[ToastMessage]> toasts: [];
callback toast-dismissed(int);
// Position at top-right corner
width: 370px;
VerticalLayout {
spacing: SpacingSystem.spacing.s2;
alignment: start;
for toast[index] in toasts: Toast {
message: toast.message;
variant: toast.variant;
show: toast.show;
dismissed => {
root.toast-dismissed(index);
}
}
}
}