149 lines
4.3 KiB
Plaintext
149 lines
4.3 KiB
Plaintext
// 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);
|
||
}
|
||
}
|
||
}
|
||
}
|