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
+6
View File
@@ -6,6 +6,8 @@ pub enum ContextId {
SidebarProjects,
SidebarTags,
Modal,
ModalInput,
ModalDropdown,
FilterBar,
TextInput,
}
@@ -19,6 +21,8 @@ impl ContextId {
"sidebarprojects" | "SidebarProjects" => Some(Self::SidebarProjects),
"sidebartags" | "SidebarTags" => Some(Self::SidebarTags),
"modal" | "Modal" => Some(Self::Modal),
"modalinput" | "ModalInput" => Some(Self::ModalInput),
"modaldropdown" | "ModalDropdown" => Some(Self::ModalDropdown),
"filterbar" | "FilterBar" => Some(Self::FilterBar),
"textinput" | "TextInput" => Some(Self::TextInput),
_ => None,
@@ -33,6 +37,8 @@ impl ContextId {
Self::SidebarProjects => "SidebarProjects",
Self::SidebarTags => "SidebarTags",
Self::Modal => "Modal",
Self::ModalInput => "ModalInput",
Self::ModalDropdown => "ModalDropdown",
Self::FilterBar => "FilterBar",
Self::TextInput => "TextInput",
}
+72
View File
@@ -429,5 +429,77 @@ pub fn build_default_keymap() -> KeymapLayer {
Command::SaveModal,
);
layer.bind(
ContextId::ModalInput,
KeyChord::new(Key::Escape, Mods::none()),
Command::CloseModal,
);
layer.bind(
ContextId::ModalInput,
KeyChord::new(Key::Enter, Mods::ctrl()),
Command::SaveModal,
);
layer.bind(
ContextId::ModalInput,
KeyChord::new(Key::Tab, Mods::none()),
Command::FocusFilterNext,
);
layer.bind(
ContextId::ModalInput,
KeyChord::new(Key::Tab, Mods::shift()),
Command::FocusFilterPrev,
);
layer.bind(
ContextId::ModalDropdown,
KeyChord::new(Key::Escape, Mods::none()),
Command::CloseModal,
);
layer.bind(
ContextId::ModalDropdown,
KeyChord::new(Key::Enter, Mods::ctrl()),
Command::SaveModal,
);
layer.bind(
ContextId::ModalDropdown,
KeyChord::new(Key::Enter, Mods::none()),
Command::ToggleDropdown,
);
layer.bind(
ContextId::ModalDropdown,
KeyChord::new(Key::Space, Mods::none()),
Command::ToggleDropdown,
);
layer.bind(
ContextId::ModalDropdown,
KeyChord::new(Key::Char('j'), Mods::none()),
Command::SelectNextOption,
);
layer.bind(
ContextId::ModalDropdown,
KeyChord::new(Key::Char('k'), Mods::none()),
Command::SelectPrevOption,
);
layer.bind(
ContextId::ModalDropdown,
KeyChord::new(Key::ArrowDown, Mods::none()),
Command::SelectNextOption,
);
layer.bind(
ContextId::ModalDropdown,
KeyChord::new(Key::ArrowUp, Mods::none()),
Command::SelectPrevOption,
);
layer.bind(
ContextId::ModalDropdown,
KeyChord::new(Key::Tab, Mods::none()),
Command::FocusFilterNext,
);
layer.bind(
ContextId::ModalDropdown,
KeyChord::new(Key::Tab, Mods::shift()),
Command::FocusFilterPrev,
);
layer
}