Skip to content

Shift+click doesn't extend the selection into another paragraph #20

Open

Description

@rogegoncalves

Summary

Drag selection already works across paragraphs, but Shift+click doesn't. With the caret in paragraph A, Shift+clicking in paragraph B never selects from A to B, so toolbar commands such as Bold apply only to part of the intended range. Word and Google Docs extend the selection to the clicked point.

Reproduction (playground at v0.10.0, Chromium with no extensions)

Document: a heading, a long paragraph, a one-line paragraph, another long paragraph. In 0.10.0 the heading and the one-line paragraph render through the wrapped-paragraph textarea. Put the caret in one paragraph, Shift+click in another, then click Bold:

Caret in → Shift+click in Result
¶1 → ¶3 (both contenteditable) selects only to the end of ¶1; only ¶1 is bolded
¶1 → ¶2 (wrapped) selection collapses at the click in ¶2; nothing is bolded
¶2 (wrapped) → ¶3 selection collapses at the click in ¶3; nothing is bolded

Cause

onViewerPointerDown (registered as onPointerDownCapture on the viewer) returns early when event.shiftKey is set, so it never reaches the boundary-based selection that drag uses (resolveBoundaryFromPoint + setSelectionFromDocxBoundaries). Each paragraph is its own editing host, so native Shift+click stays inside the origin paragraph.

Suggested patch (packages/react-viewer/src/editor.tsx)

-      if (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) {
+      // Shift+click extends the current selection to the clicked point, even
+      // when it lands in another paragraph. Reuses the boundary resolution and
+      // range application that drag selection uses.
+      if (event.shiftKey && !event.ctrlKey && !event.metaKey && !event.altKey) {
+        const anchor = editor.activeTextRange?.start;
+        const clicked = resolveBoundaryFromPoint({ x: event.clientX, y: event.clientY });
+        if (
+          anchor &&
+          clicked &&
+          paragraphLocationKey(anchor.location) !== paragraphLocationKey(clicked.location)
+        ) {
+          event.preventDefault();
+          event.stopPropagation();
+          setSelectionFromDocxBoundaries(anchor, clicked);
+        }
+        return;
+      }
+
+      if (event.ctrlKey || event.metaKey || event.altKey) {
         return;
       }

It also adds editor.activeTextRange and setSelectionFromDocxBoundaries to the useCallback dependencies.

With the patch, using the same harness, ¶1→¶3, ¶1→¶2, ¶2→¶3 and ¶3→¶1 all select the full range, and Bold applies to every paragraph in it. Shift+click inside a single paragraph is unchanged. vitest run gives the same result with and without the patch (554 passed, 15 skipped). I did not run the visual Playwright suites.

Known limitations:

  • The anchor is editor.activeTextRange.start. That is right for a caret; when extending an existing selection, the anchor should be the selection's anchor end. You may already track that somewhere.
  • Shift+ArrowUp/Down across a paragraph boundary has the same problem and isn't covered.

Context: we currently ship a workaround that synthesizes a drag from the anchor to the click point; built-in support would let us remove it.

Activity

  1. andrewlu0 commented on Oct 15, 2026

    @andrewlu0
    Collaborator

    hi could u try this on the latest version 0.11? i think it should be fixed

  2. rogegoncalves commented on Oct 18, 2026

    @rogegoncalves
    Author

    Thanks, confirmed: 0.11.0 fixes it. I tested in our app with @extend-ai/react-docx@0.11.0 from npm (Chromium, no extensions), with our Shift+click workaround removed: Shift+click between regular paragraphs, from a wrapped heading to a paragraph, and from a paragraph to a wrapped heading. Bold and Backspace act on the whole range, the same as a mouse drag. We'll remove our workaround when we upgrade.

    The keyboard part from my original report (Shift+ArrowUp/Down across paragraphs) is still there in 0.11. I opened #21 with the root cause, a fix and a browser test. Feel free to close this one.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions