Skip to content

feat(grid): add keyboard arrow navigation and edit modes to grid view#8653

Open
CodeBySayak wants to merge 2 commits intoAppFlowy-IO:mainfrom
CodeBySayak:fix-keyboardkeys
Open

feat(grid): add keyboard arrow navigation and edit modes to grid view#8653
CodeBySayak wants to merge 2 commits intoAppFlowy-IO:mainfrom
CodeBySayak:fix-keyboardkeys

Conversation

@CodeBySayak
Copy link
Copy Markdown

@CodeBySayak CodeBySayak commented Apr 7, 2026

Description

This PR improves keyboard navigation in the Database Grid View by introducing spreadsheet-style interaction patterns.

Problem

Currently, users cannot efficiently navigate or edit cells using the keyboard. Navigation relies heavily on mouse interaction, limiting usability and accessibility. Additionally, common spreadsheet functions like cut, copy, paste, and fast deletion were not natively mapped at the grid level.

Solution

Introduced a navigation model that clearly separates selection and editing, along with native keyboard traversal support. Furthermore, this PR expands upon the CellShortcuts framework to bring robust support for single-cell data manipulation.

Changes

  • Navigation vs Edit Mode

    • Single-click selects a cell (Navigation Mode) without directly entering edit state.
    • Enter or double-click specifically triggers child Edit Mode. Selection and editing container decorations have been decoupled for an accurate visual source of truth.
  • Arrow Key Navigation

    • Enabled Up, Down, Left, Right traversal between cells.
    • Implemented using Flutter’s native Focus system.
  • Spreadsheet Editing Shortcuts

    • Introduced explicit Ctrl/Cmd+X (Cut), Ctrl/Cmd+C (Copy), and Ctrl/Cmd+V (Paste) handler mapping onto the cell framework.
    • Fully implemented framework support for Delete and Backspace to rapidly clear underlying cell text states.
  • Relational Data Enhancements

    • Included a specific handler for Relational Data cells; hitting Delete when a cell is highlighted safely and iteratively unlinks all related rows from the BLOC state via keyboard.
  • Refactoring & Reliability

    • Converted CellContainer to a StatefulWidget to properly manage its scoped FocusNode for reliable navigation.
    • Added new widget tests to explicitly test that container taps strictly require follow-up input (Enter, double tap) for focus propagation.

(Note regarding #8646: The original issue lists Grid Multi-Cell Selection (Shift+Arrow / Mouse Drag). Because AppFlowy isolates rows utilizing virtualized blocks, creating 2D bounding boxes requires an independent GridSelectionManager overlay. Multi-cell functionality is out of scope for general navigation behavior and should be tracked in its own specific architectural issue).

How to Test

  1. Open any Grid/Table database view.
  2. Single-click a cell → it should highlight with the primary border without entering edit mode.
  3. Use arrow keys → focus should move cleanly across adjacent cells.
  4. Press Enter or double-click → cell should successfully pass focus into edit mode.
  5. Use Ctrl/Cmd+C, Cmd+X, and Cmd+V while standard text/number cells are highlighted (but not edited) to verify copying and pasting clipboard values.
  6. Target a relational cell with existing linked tags and hit Delete to verify the links are uncoupled via keyboard.

PR Checklist

  • My code adheres to AppFlowy's Conventions
  • I've listed at least one issue that this PR fixes in the description above.
  • I've added a test(s) to validate changes in this PR, or this PR only contains semantic changes.
  • All existing tests are passing.

closes #8646

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Apr 7, 2026

Reviewer's Guide

Converts the grid cell container to a stateful, focus-aware widget that distinguishes between selection and edit modes, integrates Flutter Focus-based keyboard handling (Enter key, focus border), and wires tap/double-tap behavior to enter edit mode while preserving accessories rendering.

Sequence diagram for keyboard Enter handling and focus-based edit mode

sequenceDiagram
  actor User
  participant CellContainer
  participant CellFocus as CellContainer_FocusNode
  participant ChildCell as GridCellChild
  participant Notifier as CellContainerNotifier

  User->>CellContainer: Press_Enter_key
  CellContainer->>CellFocus: onKeyEvent(KeyDownEvent, Enter)
  CellFocus-->>CellContainer: KeyEventResult.handled
  CellContainer->>ChildCell: requestFocus.notify()
  ChildCell->>Notifier: set isFocus true
  Notifier-->>CellContainer: isChildFocus changes
  CellContainer->>CellContainer: rebuild with isSelectedOrEditing true
  CellContainer->>User: show cell in edit mode (focused child)
Loading

Class diagram for updated CellContainer and focus-aware state

classDiagram
  class CellContainer {
    +Widget child
    +GridCellAccessoryBuilder accessoryBuilder
    +double width
    +bool isPrimary
    +CellContainer(child, accessoryBuilder, width, isPrimary)
    +State createState()
  }

  class _CellContainerState {
    -FocusNode _focusNode
    +void initState()
    +void dispose()
    +void _onFocusChange()
    +Widget build(BuildContext context)
  }

  class CellContainerNotifier {
    +bool isFocus
    +void addListener(Function listener)
    +void removeListener(Function listener)
  }

  class GridCellShortcuts {
    +Widget child
  }

  class GridCellAccessoryBuildContext {
    +BuildContext anchorContext
    +bool isCellEditing
  }

  class _GridCellEnterRegion {
    +List accessories
    +bool isPrimary
    +Widget child
  }

  CellContainer ..> GridCellShortcuts : uses
  CellContainer ..> CellContainerNotifier : provides_to_Selector
  CellContainer ..> GridCellAccessoryBuildContext : builds_with_isCellEditing
  CellContainer ..> _GridCellEnterRegion : wraps_when_accessories

  CellContainer --> _CellContainerState : creates_state
  _CellContainerState --> CellContainer : accesses_widget_properties
  _CellContainerState ..> FocusNode : manages_focus_for_cell
  _CellContainerState ..> CellContainerNotifier : reads_isFocus_via_Selector
  _CellContainerState ..> GridCellShortcuts : centers_child_with_shortcuts
  _CellContainerState ..> _GridCellEnterRegion : wraps_for_accessories
  _CellContainerState ..> GridCellAccessoryBuildContext : passes_isCellEditing_flag
  _CellContainerState ..> BoxDecoration : _makeBoxDecoration(context, isSelectedOrEditing)
Loading

File-Level Changes

Change Details Files
Convert CellContainer to a stateful, focus-managed widget.
  • Change CellContainer from StatelessWidget to StatefulWidget with an associated _CellContainerState class.
  • Introduce a FocusNode lifecycle in state (initState, dispose) and trigger rebuilds on focus changes via a listener.
  • Update all references from constructor fields to widget.* inside the stateful implementation.
frontend/appflowy_flutter/lib/plugins/database/widgets/row/cells/cell_container.dart
Add keyboard focus and Enter-key handling to support navigation vs edit mode.
  • Wrap the cell content in a Focus widget bound to the new FocusNode.
  • Handle KeyDownEvent for LogicalKeyboardKey.enter to trigger the child cell’s requestFocus.notify (enter edit mode).
  • Adjust decoration logic to use a combined isSelectedOrEditing state so focused cells are visually highlighted even when not editing.
frontend/appflowy_flutter/lib/plugins/database/widgets/row/cells/cell_container.dart
Refine tap/double-tap semantics and visual styling for selection vs edit.
  • Change onTap behavior so the first tap gives the cell keyboard focus (selection) and a subsequent tap when already focused triggers edit via requestFocus.notify.
  • Add onDoubleTap handler to immediately enter edit mode without relying on prior selection.
  • Pass the child’s focus state (isChildFocus) into accessory builder as isCellEditing, and slightly increase the focused border width for better visual emphasis.
frontend/appflowy_flutter/lib/plugins/database/widgets/row/cells/cell_container.dart

Assessment against linked issues

Issue Objective Addressed Explanation
#8646 Support spreadsheet-like keyboard navigation between grid cells, including separating navigation vs edit modes and allowing entering edit mode from the keyboard.
#8646 Implement spreadsheet-style keyboard shortcuts for grid data editing: cut (Ctrl+X), copy (Ctrl+C), paste (Ctrl+V), Shift+arrow multi-cell selection, mouse drag multi-cell selection, and Delete key to clear cell contents. The PR only introduces focus management, navigation vs edit modes, and Enter-based editing, plus arrow-key traversal. It does not add any handling for cut/copy/paste shortcuts, Shift+arrow multi-selection, mouse drag selection, or Delete key behavior.
#8646 Ensure that the improved keyboard navigation and editing shortcuts work for relational data in the grid view. The changes are limited to the generic CellContainer focus and navigation behavior and do not add or verify any specific handling for relational data interactions or keyboard shortcuts on relational cells.

Possibly linked issues

  • #N/A: PR implements arrow-key navigation and edit vs selection modes, directly fulfilling the requested database cell navigation feature.
  • #FR: They both target keyboard navigation and accessibility in grid/database views by enhancing focus and cell interaction behavior.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • The onTap handler currently enters edit mode on a second single-click (when _focusNode already has focus), which conflicts with the described behavior of single-click = select and Enter/double-click = edit; consider tightening the logic so single-click never directly triggers child.requestFocus.notify() and only Enter/double-tap do.
  • The isSelectedOrEditing flag conflates the cell’s own focus node and the child’s edit focus (isChildFocus), which can make visual state difficult to reason about; consider separating ‘selection’ and ‘editing’ decorations so each is driven by a single, clear source of truth.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `onTap` handler currently enters edit mode on a second single-click (when `_focusNode` already has focus), which conflicts with the described behavior of single-click = select and Enter/double-click = edit; consider tightening the logic so single-click never directly triggers `child.requestFocus.notify()` and only Enter/double-tap do.
- The `isSelectedOrEditing` flag conflates the cell’s own focus node and the child’s edit focus (`isChildFocus`), which can make visual state difficult to reason about; consider separating ‘selection’ and ‘editing’ decorations so each is driven by a single, clear source of truth.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FR] full keyboard navigation and shortcuts for grid data editing (including copying and pasting relational data)

1 participant