Alternative Dropdown
In this example we will create a button that serves as a good alternative to a dropdown.
We noticed this clever approach in the MailChimp mobile app and decided to recreate it in Fuse. That little button, when clicked, simply cycles through a list of modes unlike a dropdown menu that you select things from.
Basic app structure
Our MainView.ux
holds the basic markup for our apps layout, as well as the top-level JavaScript
data context responsible for showing the right numbers, depending on the selected value in our alternative dropdown button.
Here’s what the module that feeds our view looks like. modes
holds the labels for the button, and data
holds the numbers for all of the modes. info
is a reactive Observable that picks a subset of data from the data
array, depending on what the value of currentMode
Observable is.
<JavaScript>
var Observable = require("FuseJS/Observable");
var currentMode = Observable(0);
var modes = [
{label: "7 DAYS"},
{label: "30 DAYS"},
{label: "90 DAYS"},
{label: "1 YEAR"}
];
var data = [
{change: 492, total: 12774, unsubs: 327},
{change: 1279, total: 42891, unsubs: 1183},
{change: 2631, total: 99657, unsubs: 2478},
{change: 7395, total: 125758, unsubs: 5492}
];
var info = currentMode.map(function(x) {
return data[x];
});
module.exports = {
modes: modes,
currentMode: currentMode,
info: info
};
</JavaScript>
The UX code responsibe for showing the data and the alternative dropdown button is just a Grid
with 4 cells. Three of the cells hold our statistics and one is occupied by our CycleButton
component.
<Grid RowCount="2" ColumnCount="2" Margin="16">
<StackPanel Alignment="CenterLeft">
<Text Value="{info.change}" TextColor="DarkerTextColor" FontSize="24" />
<Text Value="Audience change" TextColor="LighterTextColor" FontSize="15" />
</StackPanel>
<CycleButton Width="100" Alignment="TopRight" Modes="{modes}" CurrentMode="{currentMode}" />
<StackPanel Alignment="CenterLeft">
<Text Value="{info.total}" TextColor="DarkerTextColor" FontSize="18" />
<Text Value="Total audience" TextColor="LighterTextColor" FontSize="15" />
</StackPanel>
<StackPanel Alignment="CenterLeft">
<Text Value="{info.unsubs}" TextColor="DarkerTextColor" FontSize="18" />
<Text Value="Unsubs/Bounces" TextColor="LighterTextColor" FontSize="15" />
</StackPanel>
</Grid>
As you can see, we’re passing {modes}
and {currentMode}
as properties to the CycleButton
component, so let’s take a look at how that is implemented.
The alternative dropdown component
Our custom CycleButton
component is a simple Rectangle
that accepts two properties: Modes
and CurrentMode
.
<Rectangle ux:Class="CycleButton" Padding="4" StrokeColor="BorderColor" StrokeWidth="2" Color="White" CornerRadius="2">
<object ux:Property="Modes" />
<int ux:Property="CurrentMode" />
Inside of the parent Rectangle
, we put a DockPanel
which holds the current selected mode label, as well as a list of dots to represent the number of modes (and highlight the active one). To determine which mode is active, we use the index()
UX expression, available when we’re inside of an Each
tag - all we need to do is compare the index to the current selected mode.
<DockPanel>
<Text Value="{currentModeLabel}" Alignment="Center" Dock="Fill" Color="LightBlue"/>
<Panel Margin="8,0,0,0" Width="16" Dock="Right">
<StackPanel Alignment="Center" ItemSpacing="2">
<Each Items="{ReadProperty Modes}">
<ModeIndicator IsActive="index() == {currentMode}" />
</Each>
</StackPanel>
</Panel>
</DockPanel>
<Circle ux:Class="ModeIndicator" IsActive="false" Width="4" BoxSizing="FillAspect" Color="BorderColor">
<bool ux:Property="IsActive" />
<WhileTrue Value="{Property IsActive}">
<Change this.Color="LightBlue" />
</WhileTrue>
</Circle>
Finally, we add a Clicked
handler on the component so that we can cycle through the different modes.
<Clicked Handler="{nextState}" />
Our component has its own JavaScript
module responsible for cycling through the different modes and picking the right label to show on the button. Since variables that are passed to components via ux:Property
are implicitly available as derived Observables in JavaScript
, we can refer to them using the this.*
syntax. That way, we get access to both the list of modes passed to the component, as well as the original instance of the currentMode
Observable. That is very important, because we want the parent data context to know about mode changes in our component, so that it can select the right subset of data to show in UI.
<JavaScript>
var modes = this.Modes;
var currentMode = this.CurrentMode;
var currentModeLabel = currentMode.combineLatest(modes, function(idx, list) {
return list[idx].label;
});
function nextState() {
currentMode.value = (currentMode.value + 1) % modes.value.length;
}
module.exports = {
nextState: nextState,
currentMode: currentMode,
currentModeLabel: currentModeLabel
}
</JavaScript>
And that’s about it. Feel free to download the full example, play with it and use it in your own projects!