Skip to content

Commit

Permalink
feat: Improve design and UX of Dropdown
Browse files Browse the repository at this point in the history
  • Loading branch information
marc2332 committed Nov 17, 2023
1 parent 046f2a6 commit 96503d0
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 75 deletions.
2 changes: 1 addition & 1 deletion crates/components/src/button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub struct ButtonProps<'a> {
#[props(default = "4".to_string(), into)]
pub margin: String,
/// Corner radius for the Button.
#[props(default = "10".to_string(), into)]
#[props(default = "8".to_string(), into)]
pub corner_radius: String,
/// Width size for the Button.
#[props(default = "auto".to_string(), into)]
Expand Down
173 changes: 104 additions & 69 deletions crates/components/src/dropdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use dioxus::prelude::*;
use freya_elements::elements as dioxus_elements;
use freya_elements::events::keyboard::Key;
use freya_elements::events::{KeyboardEvent, MouseEvent};
use freya_hooks::{use_focus, use_get_theme};
use freya_hooks::{use_focus, use_get_theme, use_platform};
use winit::window::CursorIcon;

/// [`DropdownItem`] component properties.
#[derive(Props)]
Expand All @@ -20,7 +21,7 @@ pub struct DropdownItemProps<'a, T: 'static> {

/// Current status of the DropdownItem.
#[derive(Debug, Default, PartialEq, Clone, Copy)]
pub enum DropdownItemState {
pub enum DropdownItemStatus {
/// Default state.
#[default]
Idle,
Expand All @@ -43,32 +44,47 @@ where
let selected = use_shared_state::<T>(cx).unwrap();
let theme = use_get_theme(cx);
let focus = use_focus(cx);
let state = use_state(cx, DropdownItemState::default);
let status = use_state(cx, DropdownItemStatus::default);
let platform = use_platform(cx);

let focus_id = focus.attribute(cx);
let is_focused = focus.is_focused();
let is_selected = *selected.read() == cx.props.value;

let background = match *state.get() {
let background = match *status.get() {
_ if is_selected => theme.dropdown_item.select_background,
_ if is_focused => theme.dropdown_item.hover_background,
DropdownItemState::Hovering => theme.dropdown_item.hover_background,
DropdownItemState::Idle => theme.dropdown_item.background,
DropdownItemStatus::Hovering => theme.dropdown_item.hover_background,
DropdownItemStatus::Idle => theme.dropdown_item.background,
};
let color = theme.dropdown_item.font_theme.color;

use_on_unmount(cx, {
to_owned![status, platform];
move || {
if *status.get() == DropdownItemStatus::Hovering {
platform.set_cursor(CursorIcon::default());
}
}
});

let onclick = move |_: MouseEvent| {
if let Some(onclick) = &cx.props.onclick {
onclick.call(())
}
};

let onmouseenter = move |_| {
state.set(DropdownItemState::Hovering);
let onmouseenter = {
to_owned![platform];
move |_| {
platform.set_cursor(CursorIcon::Hand);
status.set(DropdownItemStatus::Hovering);
}
};

let onmouseleave = move |_| {
state.set(DropdownItemState::default());
platform.set_cursor(CursorIcon::default());
status.set(DropdownItemStatus::default());
};

let onkeydown = move |ev: KeyboardEvent| {
Expand All @@ -80,14 +96,15 @@ where
};

render!(rect {
color: color,
width: "100%",
color: "{color}",
height: "35",
focus_id: focus_id,
role: "button",
background: background,
padding: "6",
corner_radius: "3",
background: "{background}",
padding: "10 14",
corner_radius: "6",
main_align: "center",
cross_align: "center",
onmouseenter: onmouseenter,
onmouseleave: onmouseleave,
onclick: onclick,
Expand All @@ -107,7 +124,7 @@ pub struct DropdownProps<'a, T: 'static> {

/// Current status of the Dropdown.
#[derive(Debug, Default, PartialEq, Clone, Copy)]
pub enum DropdownState {
pub enum DropdownStatus {
/// Default state.
#[default]
Idle,
Expand Down Expand Up @@ -155,25 +172,28 @@ where
let selected = use_shared_state::<T>(cx).unwrap();
let theme = use_get_theme(cx);
let focus = use_focus(cx);
let state = use_state(cx, DropdownState::default);
let status = use_state(cx, DropdownStatus::default);
let opened = use_state(cx, || false);
let platform = use_platform(cx);

let is_opened = *opened.get();
let is_focused = focus.is_focused();
let focus_id = focus.attribute(cx);

let desplegable_background = theme.dropdown.desplegable_background;
let button_background = match *state.get() {
DropdownState::Hovering => theme.dropdown.hover_background,
DropdownState::Idle => theme.dropdown.background_button,
};
let color = theme.dropdown.font_theme.color;

// Update the provided value if the passed value changes
use_memo(cx, &cx.props.value, move |value| {
*selected.write() = value;
});

use_on_unmount(cx, {
to_owned![status, platform];
move || {
if *status.get() == DropdownStatus::Hovering {
platform.set_cursor(CursorIcon::default());
}
}
});

// Close the dropdown if clicked anywhere
let onglobalclick = move |_: MouseEvent| {
opened.set(false);
Expand All @@ -198,53 +218,68 @@ where
}
};

if *opened.get() {
render!(
rect {
width: "70",
height: "50",
margin: "5",
rect {
overflow: "clip",
focus_id: focus_id,
layer: "-1",
corner_radius: "3",
onglobalclick: onglobalclick,
onkeydown: onkeydown,
width: "130",
height: "auto",
background: desplegable_background,
shadow: "0 0 20 0 rgb(0, 0, 0, 100)",
padding: "7",
&cx.props.children
}
let onmouseenter = {
to_owned![status, platform];
move |_| {
platform.set_cursor(CursorIcon::Hand);
status.set(DropdownStatus::Hovering);
}
};

let onmouseleave = move |_| {
platform.set_cursor(CursorIcon::default());
status.set(DropdownStatus::default());
};

let desplegable_background = theme.dropdown.desplegable_background;
let button_background = match *status.get() {
DropdownStatus::Hovering => theme.dropdown.hover_background,
DropdownStatus::Idle => theme.dropdown.background_button,
};
let border_fill = theme.dropdown.border_fill;
let color = theme.dropdown.font_theme.color;

let selected = selected.read().to_string();

render!(
rect {
onmouseenter: onmouseenter,
onmouseleave: onmouseleave,
onclick: onclick,
onkeydown: onkeydown,
margin: "4",
focus_id: focus_id,
background: "{button_background}",
color: "{color}",
corner_radius: "8",
padding: "8 20",
border: "1 solid {border_fill}",
shadow: "0 4 5 0 rgb(0, 0, 0, 30)",
label {
text_align: "center",
"{selected}"
}
)
} else {
let selected = selected.read().to_string();
render!(
rect {
margin: "5",
overflow: "clip",
focus_id: focus_id,
background: button_background,
color: color,
corner_radius: "3",
onclick: onclick,
onkeydown: onkeydown,
width: "70",
height: "auto",
padding: "7",
label {
text_align: "center",
"{selected}"
}

}
if *opened.get() {
rsx!(
rect {
width: "100%",
height: "2",
background: color,
height: "0",
rect {
onglobalclick: onglobalclick,
onkeydown: onkeydown,
layer: "-99",
margin: "2 5",
border: "1 solid {border_fill}",
overflow: "clip",
corner_radius: "8",
background: "{desplegable_background}",
shadow: "0 4 5 0 rgb(0, 0, 0, 30)",
padding: "6",
&cx.props.children
}
}
}
)
}
)
}
)
}
9 changes: 6 additions & 3 deletions crates/hooks/src/use_theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub struct DropdownTheme {
pub background_button: &'static str,
pub hover_background: &'static str,
pub font_theme: FontTheme,
pub border_fill: &'static str,
}

/// Theming properties for Button components.
Expand Down Expand Up @@ -205,11 +206,12 @@ pub const LIGHT_THEME: Theme = Theme {
},
dropdown: DropdownTheme {
desplegable_background: "white",
background_button: "rgb(240, 240, 240)",
hover_background: "rgb(215, 215, 215)",
background_button: "rgb(245, 245, 245)",
hover_background: "rgb(235, 235, 235)",
font_theme: FontTheme {
color: "rgb(10, 10, 10)",
},
border_fill: "rgb(210, 210, 210)",
},
dropdown_item: DropdownItemTheme {
background: "white",
Expand Down Expand Up @@ -283,8 +285,9 @@ pub const DARK_THEME: Theme = Theme {
dropdown: DropdownTheme {
desplegable_background: "rgb(25, 25, 25)",
background_button: "rgb(35, 35, 35)",
hover_background: "rgb(80, 80, 80)",
hover_background: "rgb(45, 45, 45)",
font_theme: FontTheme { color: "white" },
border_fill: "rgb(80, 80, 80)",
},
dropdown_item: DropdownItemTheme {
background: "rgb(35, 35, 35)",
Expand Down
15 changes: 13 additions & 2 deletions examples/dropdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@ fn main() {
}

fn app(cx: Scope) -> Element {
let values = cx.use_hook(|| vec!["A".to_string(), "B".to_string(), "C".to_string()]);
let selected_dropdown = use_state(cx, || "A".to_string());
let values = cx.use_hook(|| {
vec![
"Value A".to_string(),
"Value B".to_string(),
"Value C".to_string(),
]
});
let selected_dropdown = use_state(cx, || "Value A".to_string());

render!(
Dropdown {
Expand All @@ -26,5 +32,10 @@ fn app(cx: Scope) -> Element {
)
})
}
Button {
label {
"Hello World"
}
}
)
}

0 comments on commit 96503d0

Please sign in to comment.