react-arborist icon indicating copy to clipboard operation
react-arborist copied to clipboard

Windows: Multi-select should use Ctrl+Click, not Meta+Click

Open CodyAdam opened this issue 7 months ago • 3 comments
trafficstars

When performing multi-selection with the mouse, the expected behavior (familiar from applications like Windows File Explorer) is to use CTRL + Click to select multiple items.

Currently, react-arborist uses e.metaKey to detect multi-selection, which is standard on macOS but not Windows. This results in an inconsistent user experience on Windows.

Current Behavior

The relevant code can be found here:

https://github.com/brimdata/react-arborist/blob/c851c6f76ad5878f3ea7b68602e9b2f970df142e/modules/react-arborist/src/interfaces/node-api.ts#L199-L208

Expected Behavior

The following diff demonstrates how to modify the handleClick function to correctly handle multi-selection on both macOS and Windows by checking for either e.metaKey (Cmd on macOS) or e.ctrlKey (Ctrl on Windows):

  handleClick = (e: React.MouseEvent) => {
-   if (e.metaKey                && !this.tree.props.disableMultiSelection) {
+   if ((e.metaKey || e.ctrlKey) && !this.tree.props.disableMultiSelection) {
      this.isSelected ? this.deselect() : this.selectMulti();
    } else if (e.shiftKey && !this.tree.props.disableMultiSelection) {
      this.selectContiguous();
    } else {
      this.select();
      this.activate();
    }
  };

Alternative Solution

An alternative, more flexible solution would be to allow users to customize the selection shortcuts.
I support the proposition made in https://github.com/brimdata/react-arborist/issues/57. While this approach is more involved, it provides greater flexibility.

The proposed change in this issue offers a quick fix for the common Windows use case.

CodyAdam avatar Apr 09 '25 14:04 CodyAdam

Made the PR, ready for merge: https://github.com/brimdata/react-arborist/pull/304 Deployed Demo: https://react-arborist-showcase.vercel.app/ Tests are passing ✅

CodyAdam avatar Apr 09 '25 14:04 CodyAdam

For now as a workaround for my project, I use a script that patch the library, and it run in post-install:

import fs from "fs";
import path from "path";

export function patchReactArborist() {
  try {
    console.log("Patching react-arborist");
    const rootDir = "node_modules/react-arborist";
    const replaceBefore = "e.metaKey";
    const replaceAfter = "(e.metaKey || e.ctrlKey)";

    // Function to recursively find and patch files
    function findAndPatchFiles(directory: string) {
      const files = fs.readdirSync(directory);

      for (const file of files) {
        const filePath = path.join(directory, file);
        const stats = fs.statSync(filePath);

        if (stats.isDirectory()) {
          // Recursively search subdirectories
          findAndPatchFiles(filePath);
        } else if (
          stats.isFile() &&
          (file.endsWith(".ts") || file.endsWith(".tsx") || file.endsWith(".js") || file.endsWith(".jsx"))
        ) {
          // Process TypeScript and JavaScript files
          const content = fs.readFileSync(filePath, "utf8");

          if (content.includes(replaceBefore)) {
            const newContent = content.replace(new RegExp(replaceBefore, "g"), replaceAfter);
            fs.writeFileSync(filePath, newContent);
            console.log(`Patched ${filePath}`);
          }
        }
      }
    }

    findAndPatchFiles(rootDir);
    console.log("Patched react-arborist");
  } catch (error) {
    console.error("Error patching react-arborist", error);
  }
}

Output :

Patching react-arborist
Patched node_modules\react-arborist\dist\main\components\default-container.js
Patched node_modules\react-arborist\dist\main\interfaces\node-api.js
Patched node_modules\react-arborist\dist\module\components\default-container.js
Patched node_modules\react-arborist\dist\module\interfaces\node-api.js
Patched node_modules\react-arborist\src\components\default-container.tsx
Patched node_modules\react-arborist\src\interfaces\node-api.ts
Patched react-arborist

CodyAdam avatar Apr 10 '25 10:04 CodyAdam

@jameskerr Please review 🙏

CodyAdam avatar Apr 24 '25 12:04 CodyAdam