Skip to content

feat(blocks): Notion-like block editor with keyboard navigation, block operations, and drag-and-drop#146

Merged
Nowely merged 25 commits intonextfrom
block
Mar 9, 2026
Merged

feat(blocks): Notion-like block editor with keyboard navigation, block operations, and drag-and-drop#146
Nowely merged 25 commits intonextfrom
block

Conversation

@Nowely
Copy link
Owner

@Nowely Nowely commented Mar 7, 2026

Summary

Introduces a experimental full block editing experience (Notion-style) for both React and Vue, enabled via the block prop on MarkedInput.

Features

  • Block management: Add, delete, duplicate, and reorder blocks via a side panel menu
  • Drag-and-drop: Draggable handles inside block divs (48px padding-left) for reordering
  • Keyboard navigation: Arrow keys (with x-position preservation for Up/Down), Enter to split blocks, Backspace/Delete to merge blocks
  • Block menu: Standardized icon components with "Add below", "Duplicate", and "Delete" actions
  • BLOCK_SEPARATOR (\n\n) as canonical delimiter between blocks
  • getAlwaysShowHandle utility for conditional handle visibility

Fixes

  • Caret positioning and raw offset calculation across blocks (handles marks correctly)
  • Empty block handling for Backspace/Delete
  • Block/mark/span categorization bug in #handleDelete (odd-indexed blocks no longer treated as marks)
  • Read-only mode margin/positioning for DraggableBlock

Tests

  • Comprehensive block component tests for React and Vue (keyboard nav, block ops, edge cases)

Nowely added 3 commits March 7, 2026 18:09
- Introduced new block operations: addBlock, deleteBlock, and duplicateBlock for improved block management.
- Updated BlockContainer and DraggableBlock components to support new operations with corresponding UI interactions.
- Enhanced splitTokensIntoBlocks function to handle trailing newlines as empty blocks.
- Updated Storybook documentation to reflect new functionality and UI changes for draggable blocks.
- Improved test coverage for block operations and UI interactions in both React and Vue components.
- Changed package version links for '@markput/react', '@markput/vue', and '@markput/react' in pnpm-lock.yaml to reference the 'dist' directories instead of the root.
- Added new package entries for 'packages/react/markput/dist' and 'packages/vue/markput/dist' with their respective dependencies.
- Introduced a new test file for block operations, enhancing test coverage for add, delete, and duplicate block functionalities.
…ions

- Removed redundant newline handling in addBlock function.
- Added error handling for missing markup in createMarkFromOverlay function.
- Updated filterSuggestions to use includes for better readability.
- Refactored BlockContainer to integrate a new BlockMenu for block actions (duplicate, delete).
- Enhanced DraggableBlock to support menu requests for block actions.
- Improved Suggestions component to utilize refs for better performance during keyboard navigation.
- Cleaned up various components by removing unused code and ensuring consistent formatting.
@vercel
Copy link

vercel bot commented Mar 7, 2026

Deployment failed with the following error:

Resource is limited - try again in 2 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/nowelys-projects?upgradeToPro=build-rate-limit

- Modified version links for '@markput/react' and '@markput/vue' in pnpm-lock.yaml to point directly to the package directories instead of the 'dist' subdirectories.
- Removed unnecessary package entries for 'packages/react/markput/dist' and 'packages/vue/markput/dist' to streamline the lock file.
- Ensured consistency in package linking across the project.
@vercel
Copy link

vercel bot commented Mar 7, 2026

Deployment failed with the following error:

Resource is limited - try again in 9 minutes (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/nowelys-projects?upgradeToPro=build-rate-limit

- Introduced new Storybook stories for the DraggableBlock component in both React and Vue, showcasing various use cases including basic draggable functionality, read-only mode, and plain text blocks.
- Updated styles in DraggableBlock components to ensure consistent appearance across frameworks, including adjustments to background and user interaction properties.
- Enhanced documentation to provide clear descriptions of draggable block features and interactions.
@vercel
Copy link

vercel bot commented Mar 8, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
markput-react Ready Ready Preview, Comment Mar 9, 2026 9:40pm
markput-vue Ready Ready Preview, Comment Mar 9, 2026 9:40pm
markput-website Ready Ready Preview, Comment Mar 9, 2026 9:40pm

- Added a new constant, BLOCK_SEPARATOR, to standardize the separator used between blocks, enhancing consistency across block operations.
- Updated addBlock, deleteBlock, and duplicateBlock functions to utilize BLOCK_SEPARATOR instead of hardcoded newlines, improving maintainability.
- Refactored tests and related components to accommodate the new block separator, ensuring all functionalities align with the updated structure.
- Enhanced splitTokensIntoBlocks to handle double newlines as separate blocks, improving the handling of empty lines in text.
- Updated documentation to reflect changes in block operations and their expected behaviors.
Nowely added 2 commits March 8, 2026 15:28
…er key

- Added a new method, #handleEnter, to the KeyDownController to handle the Enter key event.
- The method inserts a BLOCK_SEPARATOR at the current caret position when the Enter key is pressed, enhancing block management.
- Updated the Store and shared types to include a block state, ensuring proper handling of block-related functionalities.
- Adjusted MarkedInput components in both React and Vue to support the new block property.
- Updated documentation to reflect changes in block operations and their expected behaviors.
- Updated the marginLeft property in DraggableBlock components for both React and Vue to ensure consistent behavior in read-only mode.
- Modified drop indicator positioning to align with the read-only state, enhancing the user interface and interaction experience.
Nowely added 10 commits March 8, 2026 15:41
- Added a new constant, COMPLEX_MARKDOWN, to provide rich markdown content for Storybook examples.
- Updated MarkdownDocument and ComplexMarkdown stories to utilize COMPLEX_MARKDOWN, improving the demonstration of markdown capabilities in the UI.
- Removed hardcoded markdown strings from stories, enhancing maintainability and consistency across examples.
… and Vue

- Introduced an 'Add below' option in the BlockMenu component for both React and Vue, allowing users to add new blocks directly from the menu.
- Updated BlockContainer to handle the new onAdd prop, linking the menu action to the block addition functionality.
- Adjusted styles in DraggableBlock components to maintain consistent UI across frameworks, including padding and width modifications.
- Removed deprecated add button functionality from DraggableBlock to streamline the interface.
…t and Vue

- Introduced DuplicateIcon and TrashIcon components for better visual representation of actions in the BlockMenu.
- Updated styles for menu items to enhance UI consistency, including padding adjustments and transition effects.
- Replaced text-based icons with SVGs for improved scalability and design consistency across both React and Vue implementations.
…for React and Vue

- Updated SVG icon sizes and styles for Add, Duplicate, and Trash icons to ensure consistency across both React and Vue implementations.
- Adjusted padding and color properties for menu items to enhance visual coherence and user experience.
- Introduced a separator style to improve the layout of the BlockMenu, providing better separation between menu items.
…React and Vue

- Added new CSS styles for icons, including Add, Duplicate, Trash, and Grip icons, to ensure consistent appearance across both frameworks.
- Replaced inline SVGs in BlockMenu and DraggableBlock components with styled span elements for improved maintainability and styling coherence.
- Updated relevant components to utilize the new icon styles, enhancing the overall user interface and experience.
…k components

- Replaced template literals with the `cx` utility for className assignments in BlockMenu to enhance readability and maintainability.
- Introduced a constant for the Grip icon in DraggableBlock to standardize icon class usage, improving code consistency across components.
- Updated ContentEditableController to support block mode, allowing for proper handling of DraggableBlock divs.
- Refined KeyDownController's #handleEnter method to insert BLOCK_SEPARATOR at the correct position within blocks, improving user experience during text input.
- Modified MarkedInput and related components to accept a new block configuration object, enabling features like always-visible drag handles for mobile devices.
- Enhanced DraggableBlock and BlockContainer components to utilize the new block properties, ensuring consistent behavior across frameworks.
- Added comprehensive tests for block interactions in Storybook, validating the new functionalities and ensuring robust performance.
…ue handling

- Introduced getAlwaysShowHandle function to determine visibility of drag handles based on block properties.
- Updated BlockContainer and BlockContainer.vue to utilize getAlwaysShowHandle for improved readability and maintainability.
- Refactored KeyDownController and Store to streamline value application logic, enhancing block management across components.
- Adjusted tests to ensure compatibility with the new functionality and maintain robust performance.
- Added a new utility function, getEditableInBlock, to simplify test interactions with editable blocks.
- Updated tests to verify behavior when adding blocks below empty blocks, ensuring correct block count and content rendering.
- Improved assertions to reflect expected outcomes when manipulating block structures in the editor.
- Updated test descriptions for clarity and consistency, aligning with best practices.
- Consolidated test cases for rendering blocks in various components, ensuring accurate block count assertions.
- Improved readability of test structure by organizing related tests under descriptive sections.
…enhance block operations

- Added functionality in KeyDownController to delete an empty block when Backspace is pressed, improving user experience.
- Updated BlockContainer to handle null checks for value and onChange, ensuring robust block operations.
- Enhanced tests to verify the behavior of block deletion and focus management after adding new blocks, ensuring accurate block count and content rendering.
Nowely and others added 5 commits March 9, 2026 17:20
- Fix Caret.setIndex to use TreeWalker for accurate text node positioning,
  handling non-text firstChild and Infinity offset (fixes IndexSizeError)
- Add Caret.getCaretRect, isCaretOnFirstLine/LastLine, setAtX for
  ArrowUp/Down cross-block navigation with x-position preservation
- Add mergeBlocks() to blockOperations for Backspace/Delete block merging
- Fix navigation/index.ts shiftFocusPrev/Next to correctly skip non-editable
  marks and cross block boundaries
- Add #handleArrowUpDown in KeyDownController for cross-block Up/Down
- Fix #handleEnter to use raw value offset (via getCaretRawPosInBlock) instead
  of visual caret offset, fixing Enter with marks present
- Add Backspace merge: non-empty block at pos 0 merges with previous block
- Add Delete merge: caret at end of non-last block merges with next block
- Fix #handleDelete to skip mark/span logic in block mode (focus target is a
  block div, not a span/mark), preventing wrongful deleteMark calls on
  odd-indexed blocks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…k navigation

- Updated splitTokensIntoBlocks to ensure three empty blocks are created for two separators, improving block structure handling.
- Modified flushBlock logic to allow for empty block creation based on the canCreateEmpty flag, enhancing flexibility in block management.
- Enhanced BlockContainer to return an EMPTY_BLOCK when no tokens are present, ensuring consistent rendering and user experience.
- Added new tests for block keyboard navigation, verifying correct focus movement across blocks and merging behavior with Backspace/Delete.
- Implemented `handleBlockBeforeInput` to manage text insertions and deletions in block mode, ensuring proper state updates.
- Updated `selectAllText` to allow native browser handling of Ctrl+A in block mode, preventing unintended selection across blocks.
- Added tests to verify correct behavior for typing and deleting within blocks, addressing previous bugs related to raw value updates and selection management.
…itor

- Refactored getCaretRawPosInBlock to streamline caret position calculation, ensuring accurate placement within text and mark tokens.
- Enhanced handleBlockBeforeInput to correctly manage text insertions and deletions, addressing issues with caret behavior at mark boundaries.
- Introduced focusAndSetCaret function to simplify caret management after input events.
- Added tests for new input handling scenarios, verifying correct behavior for typing and pasting in blocks, particularly with mark tokens.
- Enhanced getDomRawPos to provide more accurate caret positioning at element boundaries, particularly when dealing with comment nodes.
- Updated getDomRawPosInMark to correctly map cursor positions when marks end with block separators, ensuring proper placement of the caret.
- Added a data-testid attribute to DraggableBlock for improved testing capabilities.
- Introduced comprehensive tests for block rendering and interaction scenarios in the new Block.spec.ts file, validating the expected behavior across various use cases.
- Changed the data-testid attribute in DraggableBlock to use v-bind for better Vue integration.
- Updated the MarkedInput documentation to reflect the correct line number and enhanced block prop definition, allowing for an object configuration to control drag handle visibility.
@Nowely Nowely changed the title feat(blocks): add block management operations and enhance block UI feat(blocks): Notion-like block editor with keyboard navigation, block operations, and drag-and-drop Mar 9, 2026
@Nowely Nowely merged commit 4bd5534 into next Mar 9, 2026
10 checks passed
@Nowely Nowely deleted the block branch March 9, 2026 21:41
Nowely pushed a commit that referenced this pull request Mar 15, 2026
🤖 I have created a release *beep* *boop*
---


## [0.6.0](0.5.0...0.6.0)
(2026-03-15)


### Features

* **blocks:** add block merging via Backspace/Delete and TodoList story
([#148](#148))
([0685033](0685033))
* **blocks:** Notion-like block editor with keyboard navigation, block
operations, and drag-and-drop
([#146](#146))
([4bd5534](4bd5534))
* **drag:** replace block mode with drag-and-drop row management
([#149](#149))
([83034e8](83034e8))
* **storybook:** add withPlainValue decorator and enhance drag/text
stories ([#151](#151))
([1d76c1f](1d76c1f))


### Refactoring

* **storybook:** standardize stories with PlainValuePanel and StoryObj
types across React and Vue
([#152](#152))
([42d21f3](42d21f3))


### Miscellaneous

* upgrade to Vite 8, Vitest 4.1, and Astro 6
([#150](#150))
([693966d](693966d))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.

1 participant