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 selected-index: -1; in property placeholder: "Select..."; in property enabled: true; callback selected(int, string); private property open: false; private property 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 label: ""; in property selected: false; callback clicked(); private property 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; } } } }