Toggle A DropDown Menu

Hi everyone. This my drop down menu component

<Panel ux:Class="DropdownMenu" ux:Name="menu" BackgroundColor="#FFF" BorderColor="#BBB" TextColor="#333333" FontSize="14">
  <!-- PROPERTIES -->
  <object ux:Property="ListItems" />
  <string ux:Property="Selected" />
  <string ux:Property="Text" />
  <float4 ux:Property="TextColor" />
  <Brush ux:Property="BackgroundColor" />
  <Brush ux:Property="BorderColor" />
  <float ux:Property="FontSize" />


  <!-- JAVASCRIPT -->
  <JavaScript>
  var Observable = require('FuseJS/Observable');

  var selected = Observable();

  function onSelected(arg) {
    selected.value = arg.data.name;
    //console.log(menu.Selected);
  }

  module.exports = {
    selected: selected,
    onSelected: onSelected
  }
  </JavaScript>

  <Panel ux:Class="DropdownOption" ux:Name="self" Height="40">
    <string ux:Property="Text" />
    <Brush ux:Property="BackgroundColor" />
    <Brush ux:Property="BorderColor" />
    <float ux:Property="FontSize" />
    <float4 ux:Property="TextColor" />
    <Rectangle Layer="Background" ux:Name="bgCol" Fill="{ReadProperty self.BackgroundColor}">
      <Stroke Brush="{ReadProperty self.BorderColor}" Width="1" />
    </Rectangle>
    <Text Margin="0,0,10,0" Font="Regular" Alignment="CenterRight" Value="{ReadProperty self.Text}" FontSize="{ReadProperty self.FontSize}" Color="{ReadProperty self.TextColor}" />

  </Panel>


  <DockPanel Clicked="{onSelected}" ux:Class="DropdownSelectedItem" ux:Name="self" Height="40" >
    <string ux:Property="Text" />
    <Brush ux:Property="BackgroundColor" />
    <Brush ux:Property="BorderColor" />
    <float ux:Property="FontSize" />
    <float4 ux:Property="TextColor" />
    

    <Image Dock="Right" File="../Assets/Icons/unfold-more.png" Color="{ReadProperty self.TextColor}" Width="24" Height="24" Margin="0" />

    <Text Margin="0,0,10,0" Font="Regular" FontSize="{ReadProperty self.FontSize}" Color="{ReadProperty self.TextColor}" Alignment="CenterRight" Value="{ReadProperty self.Text}" />

    <Panel Layer="Background">
      <Rectangle Layer="Background" Fill="{ReadProperty self.BackgroundColor}" Height="100%" Alignment="Top">
        <!--Stroke Brush="{ReadProperty self.BorderColor}" Width="1" /-->
      </Rectangle>
    </Panel>
  </DockPanel>



  <!-- PANEL CONTAINING EVERYTHING -->
  <Panel Width="100%" Height="40">
    <DataBinding Target="menu.Selected" Key="selected" />
    <Rectangle>
      <Stroke Brush="{ReadProperty menu.BorderColor}" Width="1" />
    </Rectangle>
    <DropdownSelectedItem TextColor="{ReadProperty menu.TextColor}" FontSize="{ReadProperty menu.FontSize}" Text="{selected}" BackgroundColor="{ReadProperty menu.BackgroundColor}" BorderColor="{ReadProperty menu.BorderColor}" />
    <Clicked>
      <Toggle Target="expandDropdown" />
    </Clicked>
    
    <WhileTrue ux:Name="expandDropdown">
      <StackPanel Offset="0,40" >
        <Each Items="{ReadProperty menu.ListItems}" >
          <DropdownOption FontSize="{ReadProperty menu.FontSize}" TextColor="{ReadProperty menu.TextColor}" Text="{name}" BackgroundColor="{ReadProperty menu.BackgroundColor}" Clicked="{onSelected}" BorderColor="{ReadProperty menu.BorderColor}" />
        </Each>
      </StackPanel>
    </WhileTrue>
  </Panel>

</Panel>

When the menu is clicked, the user could click any where and the menu still would be expanded, even when redirected to another page.

i am trying to toggle any instance from that dropdown menu. How can that be acheived?

P.S: I tried to add whileNotFocused on the component to toggle expandDropdown but with no luck.

Hi Arwa,

could you please post a complete reproduction that one could copy-paste and run? It’s hard to suggest a solution without seeing how you use the component.

A single-UX-file app would be nice and shouldn’t be too hard to put together.

<Page ux:Class="Projects" Background="#e8eaed">  
	<JavaScript>
    var Observable = require('FuseJS/Observable');

    var organizationsOptions = Observable({name: "First Option"}, {name: "First Option"}, {name: "First Option"});
    var tagsOptions = Observable({name: "First Option"}, {name: "First Option"}, {name: "First Option"});

    module.exports = {
    organizationsOptions: organizationsOptions,
    tagsOptions: tagsOptions
  };
  </JavaScript>
  <DockPanel>
    <Grid Columns="1*,1*" Dock="Top">
      <DropdownMenu ux:Name="dropOne" Text="organization" ListItems="{organizationsOptions}" Selected="{selectedOrg}" TextColor="#FFF" BackgroundColor="#333333" BorderColor="#000" />
      <DropdownMenu ux:Name="dropTWo" Text="tag" ListItems="{tagsOptions}" Selected="{selectedTag}" TextColor="#FFF" BackgroundColor="#333333" BorderColor="#000" />
    <Grid>
  </DockPanel>
</Page> 

Hi Arwa,

if the question is “how to close the dropdown when I click outside of it”, then the answer to that is: you need to add an overlay (underlay?) Panel that covers the whole screen behind the dropdown and has a Clicked handler on it that toggles the expanded state.

I was first able able to get the Panel over/underlay to work as expected (Test_Combox.ux: 57 - 62), but I now realize that with several comboboxes I need an outside click to close all drop downs.

As an attempt I’ve created a invisible, Opacity is 0, Panel in Test_Combox_Instance.ux that is supposed to set the toggle observable to false. The Combobox class has toggle as a ux:Property. My hope was that a click on the invisible Panel would change the toggle property value, which would propagated to all Combobox instances and be used to close their dropdown lists.

However, I recieve

{toggle} is not a valid property path - /.../Test_Combobox_Instance.ux(1:1):E

Object reference not set to an instance of an object - (0:0):E

My question, aside from what is recommended practice here, is how can I make the toggle property be available within a Panel and actionable by Set. I’ve tried {Toggle}, {toggle.value}, etc, getting errors about not being in scope, or not being a valid property path.

Any thoughts?

All you need to do is leave the setting of toggle value to JavaScript.

E.g., in UX:

<Tapped>
    <Callback Handler="{changeToggle}" />
</Tapped>

and in JS:

// ...
var Observable = require("FuseJS/Observable");
var toggle = Observable(false);
function changeToggle() {
    toggle.value = ! toggle.value;
}
// ...
module.exports = {
    // ...
    toggle: toggle,
    changeToggle: changeToggle
};

Thank you Uldis, with you hint above I was able to add something to the effect of:

Test_Combobox_Instance.ux 31-38, after the StackPanel

  <Panel Color="#FFF" Opacity="0" Layer="Underlay">
      <Tapped>
        <Callback Handler="{set_bg_clicked_true}" />
        <DebugAction Message="{bg_clicked}" />
      </Tapped>
  </Panel>
</Panel>

Test_Combobox.ux: 19-23, after the Tapped UX in the Panel

			<WhileTrue Value={bg_clicked}>
				<Set thePopup.Value="false" />
		        <DebugAction Message="bg_clicked: {bg_clicked}, setting to false" />
		        <Callback Handler="{set_bg_clicked_false}" />
			</WhileTrue>

with set_bg_clicked_false simply setting bg_clicked = false. This implements, for the most part (some minor layer related hitbox interactions), outside clicking to reset the dropdown. Thanks!