-
This commit is contained in:
174
ui/components/select.slint
Normal file
174
ui/components/select.slint
Normal file
@@ -0,0 +1,174 @@
|
||||
import { Theme, SpacingSystem, Typography, Animations } from "../theme/theme.slint";
|
||||
|
||||
export struct SelectOption {
|
||||
label: string,
|
||||
value: string,
|
||||
}
|
||||
|
||||
export component Select {
|
||||
in property <[SelectOption]> options: [];
|
||||
in-out property <int> selected-index: -1;
|
||||
in property <string> placeholder: "Select...";
|
||||
in property <bool> enabled: true;
|
||||
|
||||
callback selected(int, string);
|
||||
|
||||
private property <bool> open: false;
|
||||
private property <bool> hovered: false;
|
||||
|
||||
min-width: 120px;
|
||||
min-height: 36px;
|
||||
|
||||
states [
|
||||
disabled when !root.enabled: {
|
||||
trigger.opacity: 0.5;
|
||||
}
|
||||
]
|
||||
|
||||
VerticalLayout {
|
||||
// Trigger button
|
||||
trigger := Rectangle {
|
||||
height: 36px;
|
||||
background: Theme.colors.background;
|
||||
border-radius: SpacingSystem.radius.md;
|
||||
border-width: 1px;
|
||||
border-color: Theme.colors.input;
|
||||
|
||||
drop-shadow-blur: 1px;
|
||||
drop-shadow-color: #00000010;
|
||||
drop-shadow-offset-y: 1px;
|
||||
|
||||
HorizontalLayout {
|
||||
padding-left: SpacingSystem.spacing.s3;
|
||||
padding-right: SpacingSystem.spacing.s3;
|
||||
spacing: SpacingSystem.spacing.s2;
|
||||
alignment: space-between;
|
||||
|
||||
value-text := Text {
|
||||
text: root.selected-index >= 0 && root.selected-index < root.options.length
|
||||
? root.options[root.selected-index].label
|
||||
: root.placeholder;
|
||||
color: root.selected-index >= 0
|
||||
? Theme.colors.foreground
|
||||
: Theme.colors.muted-foreground;
|
||||
font-size: Typography.sizes.sm;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
|
||||
chevron := Text {
|
||||
text: "›";
|
||||
color: Theme.colors.muted-foreground;
|
||||
font-size: Typography.sizes.base;
|
||||
font-weight: Typography.weights.bold;
|
||||
vertical-alignment: center;
|
||||
width: 16px;
|
||||
horizontal-alignment: center;
|
||||
|
||||
rotation-angle: root.open ? -90deg : 90deg;
|
||||
rotation-origin-x: self.width / 2;
|
||||
rotation-origin-y: self.height / 2;
|
||||
|
||||
animate rotation-angle { duration: Animations.durations.fast; easing: Animations.ease-out; }
|
||||
}
|
||||
}
|
||||
|
||||
touch := TouchArea {
|
||||
enabled: root.enabled;
|
||||
|
||||
clicked => {
|
||||
root.open = !root.open;
|
||||
}
|
||||
|
||||
moved => {
|
||||
root.hovered = self.has-hover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dropdown menu
|
||||
if root.open: dropdown := Rectangle {
|
||||
y: trigger.height + 4px;
|
||||
background: Theme.colors.background;
|
||||
border-radius: SpacingSystem.radius.md;
|
||||
border-width: 1px;
|
||||
border-color: Theme.colors.border;
|
||||
|
||||
drop-shadow-blur: 8px;
|
||||
drop-shadow-color: #00000020;
|
||||
drop-shadow-offset-y: 4px;
|
||||
|
||||
VerticalLayout {
|
||||
padding: SpacingSystem.spacing.s1;
|
||||
|
||||
for option[index] in root.options: SelectItem {
|
||||
label: option.label;
|
||||
selected: index == root.selected-index;
|
||||
|
||||
clicked => {
|
||||
root.selected-index = index;
|
||||
root.selected(index, option.value);
|
||||
root.open = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component SelectItem {
|
||||
in property <string> label: "";
|
||||
in property <bool> selected: false;
|
||||
|
||||
callback clicked();
|
||||
|
||||
private property <bool> hovered: false;
|
||||
|
||||
height: 32px;
|
||||
|
||||
states [
|
||||
selected when root.selected: {
|
||||
container.background: Theme.colors.accent;
|
||||
}
|
||||
hovered when root.hovered && !root.selected: {
|
||||
container.background: Theme.colors.accent.transparentize(0.5);
|
||||
}
|
||||
]
|
||||
|
||||
container := Rectanglen background: transparent;
|
||||
border-radius: SpacingSystem.radius.sm;
|
||||
|
||||
animate background { duration: Animations.durations.fast; }
|
||||
|
||||
HorizontalLayout {
|
||||
padding-left: SpacingSystem.spacing.s2;
|
||||
padding-right: SpacingSystem.spacing.s2;
|
||||
spacing: SpacingSystem.spacing.s2;
|
||||
|
||||
if root.selected: checkmark := Text {
|
||||
text: "✓";
|
||||
color: Theme.colors.primary;
|
||||
font-size: Typography.sizes.sm;
|
||||
font-weight: Typography.weights.bold;
|
||||
vertical-alignment: center;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
Text {
|
||||
text: root.label;
|
||||
color: Theme.colors.foreground;
|
||||
font-size: Typography.sizes.sm;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
}
|
||||
|
||||
touch := TouchArea {
|
||||
clicked => {
|
||||
root.clicked();
|
||||
}
|
||||
|
||||
moved => {
|
||||
root.hovered = self.has-hover;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user