feat: add edit-mode commands and refactor modal UI
Introduces modal edit navigation commands and a dedicated keymap context, supports opening tasks directly in edit mode, and reorganizes the task detail modal into reusable components with sectioned render/state helpers to reduce duplication.
This commit is contained in:
@@ -11,6 +11,7 @@ pub enum Command {
|
||||
|
||||
// Actions
|
||||
OpenSelectedTask,
|
||||
OpenTaskEdit,
|
||||
Sync,
|
||||
|
||||
// Focus
|
||||
@@ -27,6 +28,21 @@ pub enum Command {
|
||||
SaveModal,
|
||||
ModalScrollUp,
|
||||
ModalScrollDown,
|
||||
EnterEditMode,
|
||||
ExitEditField,
|
||||
EnterEditField,
|
||||
ModalFocusNext,
|
||||
ModalFocusPrev,
|
||||
SubmitOrExitField,
|
||||
ModalItemPrev,
|
||||
ModalItemNext,
|
||||
DeleteSelectedItem,
|
||||
EditSelectedItem,
|
||||
CopySelectedItem,
|
||||
ConfirmYes,
|
||||
ConfirmNo,
|
||||
Undo,
|
||||
Redo,
|
||||
|
||||
// Filter
|
||||
ApplySearch,
|
||||
@@ -62,6 +78,7 @@ impl Command {
|
||||
"PrevPage" => Some(Self::PrevPage),
|
||||
"ClearSelection" => Some(Self::ClearSelection),
|
||||
"OpenSelectedTask" => Some(Self::OpenSelectedTask),
|
||||
"OpenTaskEdit" => Some(Self::OpenTaskEdit),
|
||||
"Sync" => Some(Self::Sync),
|
||||
"FocusSearch" => Some(Self::FocusSearch),
|
||||
"FocusTable" => Some(Self::FocusTable),
|
||||
@@ -74,6 +91,21 @@ impl Command {
|
||||
"SaveModal" => Some(Self::SaveModal),
|
||||
"ModalScrollUp" => Some(Self::ModalScrollUp),
|
||||
"ModalScrollDown" => Some(Self::ModalScrollDown),
|
||||
"EnterEditMode" => Some(Self::EnterEditMode),
|
||||
"ExitEditField" => Some(Self::ExitEditField),
|
||||
"EnterEditField" => Some(Self::EnterEditField),
|
||||
"ModalFocusNext" => Some(Self::ModalFocusNext),
|
||||
"ModalFocusPrev" => Some(Self::ModalFocusPrev),
|
||||
"SubmitOrExitField" => Some(Self::SubmitOrExitField),
|
||||
"ModalItemPrev" => Some(Self::ModalItemPrev),
|
||||
"ModalItemNext" => Some(Self::ModalItemNext),
|
||||
"DeleteSelectedItem" => Some(Self::DeleteSelectedItem),
|
||||
"EditSelectedItem" => Some(Self::EditSelectedItem),
|
||||
"CopySelectedItem" => Some(Self::CopySelectedItem),
|
||||
"ConfirmYes" => Some(Self::ConfirmYes),
|
||||
"ConfirmNo" => Some(Self::ConfirmNo),
|
||||
"Undo" => Some(Self::Undo),
|
||||
"Redo" => Some(Self::Redo),
|
||||
"ApplySearch" => Some(Self::ApplySearch),
|
||||
"ClearFilters" => Some(Self::ClearFilters),
|
||||
"ClearAllFilters" => Some(Self::ClearAllFilters),
|
||||
@@ -104,6 +136,7 @@ impl Command {
|
||||
Self::PrevPage => "PrevPage",
|
||||
Self::ClearSelection => "ClearSelection",
|
||||
Self::OpenSelectedTask => "OpenSelectedTask",
|
||||
Self::OpenTaskEdit => "OpenTaskEdit",
|
||||
Self::Sync => "Sync",
|
||||
Self::FocusSearch => "FocusSearch",
|
||||
Self::FocusTable => "FocusTable",
|
||||
@@ -116,6 +149,21 @@ impl Command {
|
||||
Self::SaveModal => "SaveModal",
|
||||
Self::ModalScrollUp => "ModalScrollUp",
|
||||
Self::ModalScrollDown => "ModalScrollDown",
|
||||
Self::EnterEditMode => "EnterEditMode",
|
||||
Self::ExitEditField => "ExitEditField",
|
||||
Self::EnterEditField => "EnterEditField",
|
||||
Self::ModalFocusNext => "ModalFocusNext",
|
||||
Self::ModalFocusPrev => "ModalFocusPrev",
|
||||
Self::SubmitOrExitField => "SubmitOrExitField",
|
||||
Self::ModalItemPrev => "ModalItemPrev",
|
||||
Self::ModalItemNext => "ModalItemNext",
|
||||
Self::DeleteSelectedItem => "DeleteSelectedItem",
|
||||
Self::EditSelectedItem => "EditSelectedItem",
|
||||
Self::CopySelectedItem => "CopySelectedItem",
|
||||
Self::ConfirmYes => "ConfirmYes",
|
||||
Self::ConfirmNo => "ConfirmNo",
|
||||
Self::Undo => "Undo",
|
||||
Self::Redo => "Redo",
|
||||
Self::ApplySearch => "ApplySearch",
|
||||
Self::ClearFilters => "ClearFilters",
|
||||
Self::ClearAllFilters => "ClearAllFilters",
|
||||
|
||||
@@ -6,6 +6,7 @@ pub enum ContextId {
|
||||
SidebarProjects,
|
||||
SidebarTags,
|
||||
Modal,
|
||||
ModalEditNav,
|
||||
ModalInput,
|
||||
ModalDropdown,
|
||||
FilterBar,
|
||||
@@ -21,6 +22,7 @@ impl ContextId {
|
||||
"sidebarprojects" | "SidebarProjects" => Some(Self::SidebarProjects),
|
||||
"sidebartags" | "SidebarTags" => Some(Self::SidebarTags),
|
||||
"modal" | "Modal" => Some(Self::Modal),
|
||||
"modaleditnav" | "ModalEditNav" => Some(Self::ModalEditNav),
|
||||
"modalinput" | "ModalInput" => Some(Self::ModalInput),
|
||||
"modaldropdown" | "ModalDropdown" => Some(Self::ModalDropdown),
|
||||
"filterbar" | "FilterBar" => Some(Self::FilterBar),
|
||||
@@ -37,6 +39,7 @@ impl ContextId {
|
||||
Self::SidebarProjects => "SidebarProjects",
|
||||
Self::SidebarTags => "SidebarTags",
|
||||
Self::Modal => "Modal",
|
||||
Self::ModalEditNav => "ModalEditNav",
|
||||
Self::ModalInput => "ModalInput",
|
||||
Self::ModalDropdown => "ModalDropdown",
|
||||
Self::FilterBar => "FilterBar",
|
||||
|
||||
+117
-21
@@ -131,6 +131,11 @@ pub fn build_default_keymap() -> KeymapLayer {
|
||||
KeyChord::new(Key::Enter, Mods::none()),
|
||||
Command::OpenSelectedTask,
|
||||
);
|
||||
layer.bind(
|
||||
ContextId::Table,
|
||||
KeyChord::new(Key::Char('e'), Mods::none()),
|
||||
Command::OpenTaskEdit,
|
||||
);
|
||||
layer.bind(
|
||||
ContextId::Table,
|
||||
KeyChord::new(Key::ArrowLeft, Mods::none()),
|
||||
@@ -397,7 +402,7 @@ pub fn build_default_keymap() -> KeymapLayer {
|
||||
Command::BlurInput,
|
||||
);
|
||||
|
||||
// Modal
|
||||
// Modal (View mode)
|
||||
layer.bind(
|
||||
ContextId::Modal,
|
||||
KeyChord::new(Key::Escape, Mods::none()),
|
||||
@@ -428,36 +433,137 @@ pub fn build_default_keymap() -> KeymapLayer {
|
||||
KeyChord::new(Key::Enter, Mods::ctrl()),
|
||||
Command::SaveModal,
|
||||
);
|
||||
|
||||
// Enter edit mode with 'e'
|
||||
layer.bind(
|
||||
ContextId::ModalInput,
|
||||
ContextId::Modal,
|
||||
KeyChord::new(Key::Char('e'), Mods::none()),
|
||||
Command::EnterEditMode,
|
||||
);
|
||||
|
||||
// ModalEditNav - Edit mode, navigating between fields (not typing)
|
||||
layer.bind(
|
||||
ContextId::ModalEditNav,
|
||||
KeyChord::new(Key::Escape, Mods::none()),
|
||||
Command::CloseModal,
|
||||
);
|
||||
layer.bind(
|
||||
ContextId::ModalInput,
|
||||
KeyChord::new(Key::Enter, Mods::ctrl()),
|
||||
ContextId::ModalEditNav,
|
||||
KeyChord::new(Key::Char('s'), Mods::ctrl()),
|
||||
Command::SaveModal,
|
||||
);
|
||||
layer.bind(
|
||||
ContextId::ModalInput,
|
||||
ContextId::ModalEditNav,
|
||||
KeyChord::new(Key::Char('j'), Mods::none()),
|
||||
Command::ModalFocusNext,
|
||||
);
|
||||
layer.bind(
|
||||
ContextId::ModalEditNav,
|
||||
KeyChord::new(Key::Char('k'), Mods::none()),
|
||||
Command::ModalFocusPrev,
|
||||
);
|
||||
layer.bind(
|
||||
ContextId::ModalEditNav,
|
||||
KeyChord::new(Key::ArrowDown, Mods::none()),
|
||||
Command::ModalFocusNext,
|
||||
);
|
||||
layer.bind(
|
||||
ContextId::ModalEditNav,
|
||||
KeyChord::new(Key::ArrowUp, Mods::none()),
|
||||
Command::ModalFocusPrev,
|
||||
);
|
||||
layer.bind(
|
||||
ContextId::ModalEditNav,
|
||||
KeyChord::new(Key::Tab, Mods::none()),
|
||||
Command::FocusFilterNext,
|
||||
Command::ModalFocusNext,
|
||||
);
|
||||
layer.bind(
|
||||
ContextId::ModalEditNav,
|
||||
KeyChord::new(Key::Tab, Mods::shift()),
|
||||
Command::ModalFocusPrev,
|
||||
);
|
||||
// Enter/Space - Edit selected item (if any), or enter field (fallback)
|
||||
layer.bind(
|
||||
ContextId::ModalEditNav,
|
||||
KeyChord::new(Key::Enter, Mods::none()),
|
||||
Command::EditSelectedItem,
|
||||
);
|
||||
layer.bind(
|
||||
ContextId::ModalEditNav,
|
||||
KeyChord::new(Key::Space, Mods::none()),
|
||||
Command::EditSelectedItem,
|
||||
);
|
||||
layer.bind(
|
||||
ContextId::ModalEditNav,
|
||||
KeyChord::new(Key::Char('u'), Mods::none()),
|
||||
Command::Undo,
|
||||
);
|
||||
layer.bind(
|
||||
ContextId::ModalEditNav,
|
||||
KeyChord::new(Key::Char('r'), Mods::none()),
|
||||
Command::Redo,
|
||||
);
|
||||
// h/l - Navigate items within tags
|
||||
layer.bind(
|
||||
ContextId::ModalEditNav,
|
||||
KeyChord::new(Key::Char('h'), Mods::none()),
|
||||
Command::ModalItemPrev,
|
||||
);
|
||||
layer.bind(
|
||||
ContextId::ModalEditNav,
|
||||
KeyChord::new(Key::Char('l'), Mods::none()),
|
||||
Command::ModalItemNext,
|
||||
);
|
||||
// Delete/Backspace - Delete selected item
|
||||
layer.bind(
|
||||
ContextId::ModalEditNav,
|
||||
KeyChord::new(Key::Delete, Mods::none()),
|
||||
Command::DeleteSelectedItem,
|
||||
);
|
||||
layer.bind(
|
||||
ContextId::ModalEditNav,
|
||||
KeyChord::new(Key::Backspace, Mods::none()),
|
||||
Command::DeleteSelectedItem,
|
||||
);
|
||||
// y - Copy selected annotation OR confirm in delete dialog
|
||||
layer.bind(
|
||||
ContextId::ModalEditNav,
|
||||
KeyChord::new(Key::Char('y'), Mods::none()),
|
||||
Command::ConfirmYes,
|
||||
);
|
||||
// n - Cancel delete dialog
|
||||
layer.bind(
|
||||
ContextId::ModalEditNav,
|
||||
KeyChord::new(Key::Char('n'), Mods::none()),
|
||||
Command::ConfirmNo,
|
||||
);
|
||||
|
||||
// ModalInput - Edit mode, actively typing in an input
|
||||
layer.bind(
|
||||
ContextId::ModalInput,
|
||||
KeyChord::new(Key::Escape, Mods::none()),
|
||||
Command::ExitEditField,
|
||||
);
|
||||
layer.bind(
|
||||
ContextId::ModalInput,
|
||||
KeyChord::new(Key::Tab, Mods::shift()),
|
||||
Command::FocusFilterPrev,
|
||||
KeyChord::new(Key::Char('s'), Mods::ctrl()),
|
||||
Command::SaveModal,
|
||||
);
|
||||
// Enter submits or exits field depending on field type
|
||||
layer.bind(
|
||||
ContextId::ModalInput,
|
||||
KeyChord::new(Key::Enter, Mods::none()),
|
||||
Command::SubmitOrExitField,
|
||||
);
|
||||
|
||||
// ModalDropdown - Edit mode, dropdown is open
|
||||
layer.bind(
|
||||
ContextId::ModalDropdown,
|
||||
KeyChord::new(Key::Escape, Mods::none()),
|
||||
Command::CloseModal,
|
||||
Command::ExitEditField,
|
||||
);
|
||||
layer.bind(
|
||||
ContextId::ModalDropdown,
|
||||
KeyChord::new(Key::Enter, Mods::ctrl()),
|
||||
KeyChord::new(Key::Char('s'), Mods::ctrl()),
|
||||
Command::SaveModal,
|
||||
);
|
||||
layer.bind(
|
||||
@@ -490,16 +596,6 @@ pub fn build_default_keymap() -> KeymapLayer {
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user