feat: Refine filters and dropdown UI for task table

Update task table filters and dropdowns, including due-date options, layout tweaks, and style polish.
Keep panel style refinement intact so layout stays stable.
This commit is contained in:
Ignacio Perez
2025-12-27 16:00:38 -03:00
parent 45d1036fa7
commit 29a23017f2
8 changed files with 566 additions and 56 deletions
+100 -20
View File
@@ -3,17 +3,31 @@ use std::sync::Arc;
use gpui::prelude::*;
use crate::components::button::Button;
use crate::components::label::Label;
use crate::theme::ActiveTheme;
#[derive(Clone, Debug)]
pub struct DropdownItem {
pub label: gpui::SharedString,
pub value: gpui::SharedString,
}
impl DropdownItem {
pub fn new(label: impl Into<gpui::SharedString>) -> Self {
let label = label.into();
Self {
value: label.clone(),
label,
}
}
pub fn with_value(
label: impl Into<gpui::SharedString>,
value: impl Into<gpui::SharedString>,
) -> Self {
Self {
label: label.into(),
value: value.into(),
}
}
}
@@ -27,6 +41,7 @@ pub struct Dropdown {
disabled: bool,
loading: bool,
placeholder: gpui::SharedString,
label_prefix: Option<gpui::SharedString>,
on_select: Option<Arc<dyn Fn(usize, &DropdownItem, &mut gpui::Context<Self>) + Send + Sync>>,
}
@@ -41,6 +56,7 @@ impl Dropdown {
disabled: false,
loading: false,
placeholder: "Seleccionar".into(),
label_prefix: None,
on_select: None,
}
}
@@ -68,6 +84,11 @@ impl Dropdown {
self
}
pub fn label_prefix(mut self, prefix: impl Into<gpui::SharedString>) -> Self {
self.label_prefix = Some(prefix.into());
self
}
pub fn selected_index(mut self, index: usize) -> Self {
self.selected_index = Some(index);
self
@@ -108,6 +129,23 @@ impl Dropdown {
.and_then(|index| self.items.get(index).map(|item| item.label.clone()))
}
pub fn set_selected_index(&mut self, index: usize, cx: &mut gpui::Context<Self>) {
if index < self.items.len() {
self.selected_index = Some(index);
cx.notify();
}
}
pub fn set_items(&mut self, items: Vec<DropdownItem>, cx: &mut gpui::Context<Self>) {
self.items = items;
if let Some(selected) = self.selected_index {
if selected >= self.items.len() {
self.selected_index = None;
}
}
cx.notify();
}
fn toggle_open(&mut self, cx: &mut gpui::Context<Self>) {
if self.disabled || self.loading || self.items.is_empty() {
return;
@@ -168,15 +206,21 @@ impl Dropdown {
let is_selected = self.selected_index == Some(index);
let mut row = gpui::div()
.id(index)
.w_full()
.px_2()
.py_1()
.text_sm()
.whitespace_nowrap()
.bg(theme.background)
.text_color(if is_selected {
theme.selection_foreground
} else {
theme.foreground
})
.when(is_selected, |el| el.bg(theme.selection))
.hover(|s: gpui::StyleRefinement| s.bg(theme.selection))
.when(!is_disabled, |el| {
el.hover(|s: gpui::StyleRefinement| s.bg(theme.selection))
})
.child(item.label.clone());
if is_disabled {
@@ -198,13 +242,17 @@ impl Dropdown {
.absolute()
.top_full()
.left_0()
.right_0()
.min_w(gpui::rems(12.0))
.min_w_full()
.mt_1()
.occlude()
.p_1()
.border_1()
.border_color(theme.border)
.bg(theme.panel)
.bg(theme.background)
.rounded_md()
.overflow_hidden()
.shadow_lg()
.children(items)
.into_any_element()
}
@@ -216,28 +264,60 @@ impl gpui::Render for Dropdown {
_window: &mut gpui::Window,
cx: &mut gpui::Context<Self>,
) -> impl IntoElement {
let theme = cx.theme();
let is_disabled = self.disabled || self.loading;
let label = self
let disabled = is_disabled || self.items.is_empty();
let mut label = self
.selected_label()
.unwrap_or_else(|| self.placeholder.clone());
let mut trigger = if let Some(button) = self.button.clone() {
let mut button = button;
button.on_click = None;
button.label = Some(label.clone());
button.disabled(is_disabled).loading(self.loading)
} else {
Button::label(self.id.clone(), label)
.disabled(is_disabled)
.loading(self.loading)
};
if is_disabled || self.items.is_empty() {
trigger = trigger.disabled(true);
if let Some(prefix) = &self.label_prefix {
label = format!("{}: {}", prefix, label).into();
}
let mut trigger_wrap = gpui::div().child(trigger);
if !is_disabled && !self.items.is_empty() {
let arrow = "";
let trigger: gpui::AnyElement = if let Some(button) = self.button.clone() {
let mut button = button;
button.on_click = None;
button.label = Some(format!("{} {}", label, arrow).into());
button
.disabled(disabled)
.loading(self.loading)
.bg(theme.background)
.border_color(theme.border)
.text_color(theme.foreground)
.into_any_element()
} else {
let mut trigger = gpui::div()
.flex()
.items_center()
.gap_2()
.px_3()
.py_2()
.rounded_md()
.border_1()
.border_color(theme.border)
.bg(theme.background)
.text_sm()
.text_color(theme.foreground)
.child(Label::new(label.clone()))
.child(Label::new(arrow).text_color(theme.muted));
if disabled {
trigger = trigger.text_color(theme.muted).cursor_not_allowed();
} else {
trigger = trigger
.cursor_pointer()
.hover(|s: gpui::StyleRefinement| s.bg(theme.selection));
}
trigger.into_any_element()
};
let mut trigger_wrap = gpui::div()
.min_w(gpui::rems(12.0))
.child(trigger);
if !disabled {
trigger_wrap = trigger_wrap.on_mouse_down(
gpui::MouseButton::Left,
cx.listener(Self::handle_trigger_mouse_down),