gui: improve channel selection behavior #21

Merged
restitux merged 2 commits from improve-channel-selection-behavior into main 2026-01-28 06:03:23 +00:00
2 changed files with 78 additions and 9 deletions
+39 -1
View File
@@ -83,6 +83,44 @@ a:visited {
} }
} }
.channel_header {
display: flex;
flex-direction: row;
align-items: center;
}
.channel_arrow {
width: 1em;
text-align: center;
margin-right: 0.25rem;
}
.channel_arrow--placeholder {
pointer-events: none;
visibility: hidden;
}
/* The whole right side of the row is the dblclick target */
restitux marked this conversation as resolved
Review

We might want to make this a single click on mobile. In my experience double clicks (especially if there are a lot of nearby elements) can be awkward on mobile.

We might want to make this a single click on mobile. In my experience double clicks (especially if there are a lot of nearby elements) can be awkward on mobile.
Review

I agree that double click is bad ux on mobile. Maybe we can just have a button here that appears only on mobile? Honestly it might prove to be nice to have on desktop as well.

I agree that double click is bad ux on mobile. Maybe we can just have a button here that appears only on mobile? Honestly it might prove to be nice to have on desktop as well.
.channel_row_click {
flex: 1;
padding: 0.1rem 0.25rem 0.1rem 0.5rem;
cursor: pointer;
}
/* Hover highlight for whole row area (title + blank space) */
.channel_row_click:hover {
background-color: var(--channel-hover-bg, #222); /* pick your color */
}
/* still keep text non-selectable if desired */
.channel_details {
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
.channel { .channel {
&_details { &_details {
flex: 0 0 100%; flex: 0 0 100%;
@@ -392,4 +430,4 @@ a:visited {
color: red; color: red;
} }
} }
} }
+39 -8
View File
@@ -291,22 +291,53 @@ pub fn Channel(id: ChannelId) -> Element {
return rsx!("missing channel {id}"); return rsx!("missing channel {id}");
}; };
let mut open = use_signal(|| true);
let has_children = !state.users.is_empty() || !state.children.is_empty();
rsx!( rsx!(
details { div {
class: "channel_details", class: "channel_details",
open: true,
summary { div {
span { class: "channel_header",
role: "button", // Arrow: only toggles open
ondoubleclick: move |evt| { if has_children {
span {
class: "channel_arrow",
onclick: move |evt| {
evt.stop_propagation();
evt.prevent_default();
let mut w = open.write();
*w = !*w;
},
if *open.read() { "" } else { "" }
}
} else {
span {
class: "channel_arrow channel_arrow--placeholder",
" "
}
}
// Clickable row area (everything except the arrow)
div {
class: "channel_row_click",
ondblclick: move |evt| {
evt.stop_propagation(); evt.stop_propagation();
evt.prevent_default(); evt.prevent_default();
net.send(EnterChannel { channel: id, user }) net.send(EnterChannel { channel: id, user })
}, },
"{state.name}" // remove dblclick from the inner span
span {
class: "channel_title",
"{state.name}"
}
// if you add icons/badges later, put them here too
} }
} }
if state.users.len() + state.children.len() > 0 {
if *open.read() && has_children {
div { div {
class: "channel_children", class: "channel_children",
for id in state.users.iter() { for id in state.users.iter() {