137 lines
5.3 KiB
Plaintext
137 lines
5.3 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 text color based on variant (shadcn design tokens)
|
|
private property <color> text-color: {
|
|
if disabled { Theme.colors.muted-foreground }
|
|
else if variant == "destructive" { Theme.colors.destructive } // destructive uses destructive color for text
|
|
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;
|
|
|
|
// Focus scope for keyboard navigation
|
|
focus-scope := FocusScope {
|
|
enabled: !disabled;
|
|
|
|
key-pressed(event) => {
|
|
if (event.text == " " || event.text == "\n") {
|
|
root.clicked();
|
|
return accept;
|
|
}
|
|
return reject;
|
|
}
|
|
}
|
|
|
|
// Main container
|
|
container := Rectangle {
|
|
border-radius: SpacingSystem.radius.md;
|
|
// Outline variant always has border
|
|
border-width: root.variant == "outline" ? 1px : 0px;
|
|
border-color: root.variant == "outline" ? Theme.colors.border : Colors.transparent;
|
|
|
|
// Touch interaction area (must be defined before using touch.pressed/has-hover)
|
|
touch := TouchArea {
|
|
enabled: !disabled;
|
|
|
|
clicked => {
|
|
// Don't set focus on mouse click (shadcn style)
|
|
root.clicked();
|
|
}
|
|
}
|
|
|
|
// Calculate background color based on variant and state (shadcn official design)
|
|
background: {
|
|
if root.disabled {
|
|
Theme.colors.muted
|
|
} else if root.variant == "destructive" {
|
|
// destructive: bg-destructive/10 hover:bg-destructive/20
|
|
if touch.pressed { Theme.colors.destructive.with-alpha(0.25) }
|
|
else if touch.has-hover { Theme.colors.destructive.with-alpha(0.2) }
|
|
else { Theme.colors.destructive.with-alpha(0.1) }
|
|
} else if root.variant == "outline" {
|
|
// outline: bg-background hover:bg-muted
|
|
if touch.pressed { Theme.colors.muted.darker(0.05) }
|
|
else if touch.has-hover { Theme.colors.muted }
|
|
else { Theme.colors.background }
|
|
} else if root.variant == "secondary" {
|
|
// secondary: bg-secondary hover:bg-secondary/80
|
|
if touch.pressed { Theme.colors.secondary.with-alpha(0.7) }
|
|
else if touch.has-hover { Theme.colors.secondary.with-alpha(0.8) }
|
|
else { Theme.colors.secondary }
|
|
} else if root.variant == "ghost" {
|
|
// ghost: transparent hover:bg-muted
|
|
if touch.pressed { Theme.colors.muted.darker(0.05) }
|
|
else if touch.has-hover { Theme.colors.muted }
|
|
else { Colors.transparent }
|
|
} else {
|
|
// default (primary): bg-primary hover:bg-primary/80
|
|
if touch.pressed { Theme.colors.primary.with-alpha(0.7) }
|
|
else if touch.has-hover { Theme.colors.primary.with-alpha(0.8) }
|
|
else { Theme.colors.primary }
|
|
}
|
|
};
|
|
|
|
// Smooth transitions
|
|
animate background {
|
|
duration: Animations.durations.fast;
|
|
easing: Animations.ease-in-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;
|
|
}
|
|
}
|
|
|
|
// Focus ring only for keyboard navigation (shadcn style)
|
|
// Will only show when using Tab key, not on mouse click
|
|
focus-ring := Rectangle {
|
|
width: 100%;
|
|
height: 100%;
|
|
border-radius: SpacingSystem.radius.md;
|
|
border-width: focus-scope.has-focus ? 2px : 0px;
|
|
border-color: Theme.colors.ring;
|
|
|
|
animate border-width {
|
|
duration: Animations.durations.fast;
|
|
easing: Animations.ease-in-out;
|
|
}
|
|
}
|
|
}
|
|
}
|