public/controls/New-UiDropdownButton.ps1
|
function New-UiDropdownButton { <# .SYNOPSIS Creates a compact popup button with selectable items. .DESCRIPTION Creates a button that displays a dropdown popup with selectable items when clicked. Similar to a ComboBox but styled as a compact icon button, ideal for panel headers or toolbar-style UIs. Supports an OnChange callback when the selection changes. .PARAMETER Items Array of items to display in the popup. .PARAMETER Default The default selected item. .PARAMETER Variable Variable name to register the control with for hydration access. .PARAMETER Icon Icon name from Segoe MDL2 Assets (e.g., 'ChevronDown', 'Settings', 'Filter'). Default is 'ChevronDown'. .PARAMETER Tooltip Tooltip text shown on hover. .PARAMETER OnChange ScriptBlock to execute when selection changes. Receives the new selection as parameter. .PARAMETER Width Button width in pixels. Default is 32. .PARAMETER Height Button height in pixels. Default is 32. .PARAMETER ShowText Show the selected item text next to the icon. .PARAMETER NoAutoAdd Don't automatically add the dropdown to the current layout panel. .PARAMETER WPFProperties Hashtable of additional WPF properties to apply to the container. .EXAMPLE New-UiDropdownButton -Items @('Option1', 'Option2', 'Option3') -Default 'Option1' -Tooltip "Select Option" .EXAMPLE New-UiDropdownButton -Items $parameterSets -Variable 'selectedSet' -Icon 'Filter' -OnChange { param($newValue) Write-Host "Selected: $newValue" } #> [CmdletBinding()] param( [Parameter(Mandatory)] [array]$Items, [string]$Default, [string]$Variable, [string]$Tooltip, [scriptblock]$OnChange, [int]$Width = 32, [int]$Height = 32, [switch]$ShowText, [switch]$NoAutoAdd, [hashtable]$WPFProperties ) DynamicParam { Get-IconDynamicParameter -ParameterName 'Icon' -DefaultValue 'ChevronDown' } begin { $Icon = if ($PSBoundParameters.ContainsKey('Icon')) { $PSBoundParameters['Icon'] } else { 'ChevronDown' } } process { # Grab session context and theme colors $colors = Get-ThemeColors $session = Get-UiSession $parent = $session.CurrentParent Write-Debug "Creating dropdown button with $($Items.Count) items" # Determine initial selection $currentSelection = if ($Default -and $Items -contains $Default) { $Default } else { $Items[0] } Write-Debug "Initial selection: $currentSelection" $container = [System.Windows.Controls.StackPanel]@{ Orientation = 'Horizontal' Margin = [System.Windows.Thickness]::new(0, 0, 4, 0) Tag = @{ SelectedItem = $currentSelection; ControlType = 'ComboButton' } } $button = [System.Windows.Controls.Button]@{ Height = $Height Padding = [System.Windows.Thickness]::new(0) VerticalAlignment = 'Center' } if ($Tooltip) { $button.ToolTip = $Tooltip } $buttonContent = [System.Windows.Controls.StackPanel]@{ Orientation = 'Horizontal' VerticalAlignment = 'Center' } if ($ShowText) { $button.Width = [double]::NaN $button.Padding = [System.Windows.Thickness]::new(8, 4, 8, 4) $textBlock = [System.Windows.Controls.TextBlock]@{ Text = $currentSelection VerticalAlignment = 'Center' Margin = [System.Windows.Thickness]::new(0, 0, 6, 0) Tag = 'ComboButtonText' } [PsUi.ThemeEngine]::RegisterElement($textBlock) [void]$buttonContent.Children.Add($textBlock) } else { $button.Width = $Width } # Icon $iconBlock = [System.Windows.Controls.TextBlock]@{ FontFamily = [System.Windows.Media.FontFamily]::new('Segoe MDL2 Assets') FontSize = 12 HorizontalAlignment = 'Center' VerticalAlignment = 'Center' } # Map icon name to character $iconChar = [PsUi.ModuleContext]::GetIcon($Icon) if (!$iconChar) { # Fallback for legacy icon names $iconChar = switch ($Icon) { 'ChevronDown' { [PsUi.ModuleContext]::GetIcon('ChevronDown') } 'Filter' { [PsUi.ModuleContext]::GetIcon('Filter') } 'Settings' { [PsUi.ModuleContext]::GetIcon('Settings') } 'List' { [PsUi.ModuleContext]::GetIcon('BulletList') } 'More' { [PsUi.ModuleContext]::GetIcon('More') } default { [PsUi.ModuleContext]::GetIcon('ChevronDown') } } } $iconBlock.Text = $iconChar $iconBlock.Tag = 'ComboButtonText' [PsUi.ThemeEngine]::RegisterElement($iconBlock) [void]$buttonContent.Children.Add($iconBlock) $button.Content = $buttonContent Set-ButtonStyle -Button $button -IconOnly:(!$ShowText) # Create popup $popup = [System.Windows.Controls.Primitives.Popup]@{ PlacementTarget = $button Placement = [System.Windows.Controls.Primitives.PlacementMode]::Bottom StaysOpen = $false AllowsTransparency = $true } # Popup border with shadow - use ControlBg for themed background (matches theme popup) $popupBorder = [System.Windows.Controls.Border]@{ Background = ConvertTo-UiBrush $colors.ControlBg BorderBrush = ConvertTo-UiBrush $colors.Border BorderThickness = [System.Windows.Thickness]::new(1) Padding = [System.Windows.Thickness]::new(4) CornerRadius = [System.Windows.CornerRadius]::new(4) Tag = 'PopupBorder' } $shadow = [System.Windows.Media.Effects.DropShadowEffect]@{ BlurRadius = 10 ShadowDepth = 2 Opacity = 0.3 } $popupBorder.Effect = $shadow # Items stack $itemsStack = [System.Windows.Controls.StackPanel]@{ Orientation = 'Vertical' } foreach ($item in $Items) { $itemButton = [System.Windows.Controls.Button]@{ Height = 32 MinWidth = 120 HorizontalContentAlignment = 'Left' Padding = [System.Windows.Thickness]::new(8, 4, 8, 4) Margin = [System.Windows.Thickness]::new(2) } $itemStack = [System.Windows.Controls.StackPanel]@{ Orientation = 'Horizontal' } # Checkmark for selected item $checkmark = [System.Windows.Controls.TextBlock]@{ Text = if ($item -eq $currentSelection) { [char]0x2713 } else { ' ' } FontSize = 14 Width = 20 Foreground = ConvertTo-UiBrush $colors.Accent Tag = 'AccentText' } [void]$itemStack.Children.Add($checkmark) # Item text $itemLabel = [System.Windows.Controls.TextBlock]@{ Text = $item FontSize = 12 VerticalAlignment = 'Center' Foreground = ConvertTo-UiBrush $colors.ControlFg } [void]$itemStack.Children.Add($itemLabel) $itemButton.Content = $itemStack $itemButton.Tag = @{ ItemValue = $item Checkmark = $checkmark Container = $container TextBlock = if ($ShowText) { $textBlock } else { $null } ItemsStack = $itemsStack OnChange = $OnChange } Set-ButtonStyle -Button $itemButton # Click handler for item selection $itemButton.Add_Click({ $tag = $this.Tag $selectedValue = $tag.ItemValue $containerRef = $tag.Container # Update container's selected item $containerRef.Tag.SelectedItem = $selectedValue # Update checkmarks in all items foreach ($child in $tag.ItemsStack.Children) { if ($child -is [System.Windows.Controls.Button] -and $child.Tag) { $childTag = $child.Tag if ($childTag.Checkmark) { $childTag.Checkmark.Text = if ($childTag.ItemValue -eq $selectedValue) { [char]0x2713 } else { ' ' } } } } # Update button text if ShowText if ($tag.TextBlock) { $tag.TextBlock.Text = $selectedValue } # Close popup $popup.IsOpen = $false # Fire OnChange callback if ($tag.OnChange) { try { & $tag.OnChange $selectedValue } catch { Write-Warning "OnChange callback error: $_" } } }.GetNewClosure()) [void]$itemsStack.Children.Add($itemButton) } $popupBorder.Child = $itemsStack $popup.Child = $popupBorder $button.Tag = $popup # Refresh colors when popup opens (handles theme changes after initial creation) $popup.Add_Opened({ $freshColors = Get-ThemeColors $popupBorder.Background = ConvertTo-UiBrush $freshColors.ControlBg $popupBorder.BorderBrush = ConvertTo-UiBrush $freshColors.Border # Update text colors in all items foreach ($child in $itemsStack.Children) { if ($child -is [System.Windows.Controls.Button]) { $content = $child.Content if ($content -is [System.Windows.Controls.StackPanel]) { foreach ($textElem in $content.Children) { if ($textElem -is [System.Windows.Controls.TextBlock]) { if ($textElem.Tag -eq 'AccentText') { $textElem.Foreground = ConvertTo-UiBrush $freshColors.Accent } else { $textElem.Foreground = ConvertTo-UiBrush $freshColors.ControlFg } } } } } } }.GetNewClosure()) # Button click toggles popup $button.Add_Click({ try { $popup.IsOpen = !$popup.IsOpen } catch { Write-Verbose "Failed to toggle popup: $_" } }.GetNewClosure()) [void]$container.Children.Add($button) # Apply custom WPF properties if specified if ($WPFProperties) { Set-UiProperties -Control $container -Properties $WPFProperties } # Register with session if Variable provided if ($Variable) { Write-Debug "Registering as '$Variable'" $session.AddControlSafe($Variable, $container) } # Add to parent panel unless caller wants manual placement if (!$NoAutoAdd) { [void]$parent.Children.Add($container) } return @{ Container = $container Button = $button Popup = $popup GetValue = { $container.Tag.SelectedItem }.GetNewClosure() SetValue = { param($newValue) if ($Items -contains $newValue) { $container.Tag.SelectedItem = $newValue if ($ShowText -and $textBlock) { $textBlock.Text = $newValue } foreach ($child in $itemsStack.Children) { if ($child -is [System.Windows.Controls.Button] -and $child.Tag) { $child.Tag.Checkmark.Text = if ($child.Tag.ItemValue -eq $newValue) { [char]0x2713 } else { ' ' } } } } }.GetNewClosure() } } } |