feat(modal): add edit mode with anchored dropdowns and keyboard workflow

- Add TaskDetailModal edit mode with form validation, tags + annotations editing, and SaveEdits event
- Anchor dropdown/suggestion menus using deferred/anchored overlay to avoid clipping
- Add modal-specific keymap contexts (ModalInput/ModalDropdown) for Esc/Ctrl+Enter/Tab navigation
- Refresh modal project suggestions from tasks; improve project child filtering boundary match
- Small UI polish (toast styling, layout spacing) and update shortcuts docs
This commit is contained in:
Ignacio Perez
2026-01-02 10:38:25 -03:00
parent 7cf431d761
commit 00cfc3ff4d
16 changed files with 2364 additions and 335 deletions
+26 -11
View File
@@ -1,6 +1,7 @@
use std::sync::Arc;
use gpui::prelude::*;
use gpui::{Corner, anchored, deferred, px, point};
use crate::components::button::Button;
use crate::components::label::Label;
@@ -41,6 +42,7 @@ pub struct Dropdown {
selected_index: Option<usize>,
disabled: bool,
loading: bool,
inline_menu: bool,
placeholder: gpui::SharedString,
label_prefix: Option<gpui::SharedString>,
on_select: Option<Arc<dyn Fn(usize, &DropdownItem, &mut gpui::Context<Self>) + Send + Sync>>,
@@ -56,7 +58,8 @@ impl Dropdown {
selected_index: None,
disabled: false,
loading: false,
placeholder: "Seleccionar".into(),
inline_menu: false,
placeholder: "Select".into(),
label_prefix: None,
on_select: None,
}
@@ -117,6 +120,11 @@ impl Dropdown {
self
}
pub fn inline_menu(mut self) -> Self {
self.inline_menu = true;
self
}
pub fn on_select(
mut self,
handler: Arc<dyn Fn(usize, &DropdownItem, &mut gpui::Context<Self>) + Send + Sync>,
@@ -279,14 +287,8 @@ impl Dropdown {
})
.collect();
gpui::div()
.absolute()
.top_full()
.left_0()
.min_w(gpui::rems(12.0))
.min_w_full()
.mt_1()
.occlude()
let menu = gpui::div()
.w_full() // Same width as trigger
.p_1()
.border_1()
.border_color(theme.border)
@@ -294,8 +296,22 @@ impl Dropdown {
.rounded_md()
.overflow_hidden()
.shadow_lg()
.children(items)
.occlude()
.children(items);
if self.inline_menu {
menu.into_any_element()
} else {
deferred(
anchored()
.anchor(Corner::TopLeft)
.offset(point(px(0.0), px(4.0)))
.snap_to_window()
.child(menu),
)
.with_priority(1)
.into_any_element()
}
}
}
@@ -352,7 +368,6 @@ impl gpui::Render for Dropdown {
let mut container = gpui::div()
.id(self.id.clone())
.relative()
.flex()
.flex_col()
.child(trigger_wrap)