Files
shadcn-slint/ui/components/button.slint
2026-01-26 09:23:11 +08:00

126 lines
4.4 KiB
Plaintext

// Button Component
// Shadcn-style button with multiple variants and sizes
import { Theme, Typography, SpacingSystem } from "../theme/theme.slint";
import { Animations } from "../utils/animations.slint";
export component Button {
// Public properties
in property <string> text: "Button";
in property <string> variant: "default"; // default | destructive | outline | secondary | ghost
in property <string> size: "md"; // sm | md | lg
in property <bool> disabled: false;
// Callbacks
callback clicked();
// Calculate background color based on variant
private property <color> base-bg-color: {
if disabled { Theme.colors.muted }
else if variant == "destructive" { Theme.colors.destructive }
else if variant == "outline" { Colors.transparent }
else if variant == "secondary" { Theme.colors.secondary }
else if variant == "ghost" { Colors.transparent }
else { Theme.colors.primary }
};
// Calculate text color based on variant
private property <color> text-color: {
if disabled { Theme.colors.muted-foreground }
else if variant == "destructive" { Theme.colors.destructive-foreground }
else if variant == "outline" { Theme.colors.foreground }
else if variant == "secondary" { Theme.colors.secondary-foreground }
else if variant == "ghost" { Theme.colors.foreground }
else { Theme.colors.primary-foreground }
};
// Calculate size-based dimensions
private property <length> btn-height: size == "sm" ? 36px : size == "lg" ? 44px : 40px;
private property <length> btn-padding-x: size == "sm" ? 12px : size == "lg" ? 24px : 16px;
private property <length> font-size: size == "sm" ? Typography.sizes.sm : size == "lg" ? Typography.sizes.lg : Typography.sizes.base;
min-width: btn-height;
min-height: btn-height;
// Main container
container := Rectangle {
background: base-bg-color;
border-radius: SpacingSystem.radius.md;
border-width: variant == "outline" ? 1px : 0px;
border-color: Theme.colors.border;
// Smooth transitions
animate background {
duration: Animations.durations.fast;
easing: Animations.ease-in-out;
}
// Hover overlay - light mask
hover-overlay := Rectangle {
background: Colors.transparent;
border-radius: SpacingSystem.radius.md;
animate background {
duration: Animations.durations.fast;
easing: Animations.ease-in-out;
}
}
// Press overlay - darker mask
press-overlay := Rectangle {
background: Colors.transparent;
border-radius: SpacingSystem.radius.md;
animate background {
duration: 100ms;
easing: Animations.ease-out;
}
}
// Content layout
HorizontalLayout {
padding-left: btn-padding-x;
padding-right: btn-padding-x;
spacing: SpacingSystem.spacing.s2;
alignment: center;
// Button text
Text {
text: root.text;
color: text-color;
font-size: font-size;
font-weight: Typography.weights.medium;
vertical-alignment: center;
horizontal-alignment: center;
}
}
// Touch interaction area
touch := TouchArea {
enabled: !disabled;
clicked => {
root.clicked();
}
}
// States for visual feedback with color overlays
states [
pressed when touch.pressed && !disabled: {
press-overlay.background: #00000040; // Dark overlay for press
}
hovered when touch.has-hover && !disabled && !touch.pressed: {
hover-overlay.background: #ffffff15; // Light overlay for hover
}
normal when !disabled: {
hover-overlay.background: Colors.transparent;
press-overlay.background: Colors.transparent;
}
disabled-state when disabled: {
hover-overlay.background: Colors.transparent;
press-overlay.background: Colors.transparent;
}
]
}
}