C1rdec
C1rdec

Reputation: 1687

Navigation in DetailsList not possible with Monaco editor

Hi I'm using the DetailsList and I want to be able to move my selection from column to column using tab. But I came across this issue on Github: https://github.com/microsoft/fluentui/issues/4690

Arrow keys needs to be used to navigate across the list but unfortunately I'm using a Monaco Editor in the list and the arrow key is blocked inside the Editor...

I would like to know if there is way to disable the List to set the TabIndex to -1

or

if Monaco can release the arrow key when the cursor is at the end of the text (Like a textbox).

enter image description here

Upvotes: 2

Views: 809

Answers (1)

diedu
diedu

Reputation: 20785

I got something working following this rationale:

  1. listen to the onKeydown event on monaco editor
  2. identify the position of the caret
  3. know the total of lines
  4. get the string of a specific line
  5. move the focus out from monaco editor

Knowing these then you can check if the caret is at the end of the last line and move the focus when the user press the right arrow key. I also added the code to check when the caret is at the very beginning and move the focus to the cell to the left.

This is the code I ended up with

import * as React from "react";
import "./styles.css";
import { DetailsList, IColumn } from "@fluentui/react";
import MonacoEditor from "react-monaco-editor";

export default function App() {
  const columns: IColumn[] = [
    {
      key: "name",
      minWidth: 50,
      maxWidth: 50,
      name: "Name",
      onRender: (item, index) => (
        <input id={`name-row-${index}`} value={item.name} />
      )
    },
    {
      key: "type",
      minWidth: 200,
      name: "Type",
      onRender: (item, index) => {
        return (
          <MonacoEditor
            editorDidMount={(editor, monaco) => {
              editor.onKeyDown((event) => {
                if (event.code === "ArrowRight") {
                  const { column, lineNumber } = editor.getPosition();
                  const model = editor.getModel();
                  if (lineNumber === model?.getLineCount()) {
                    const lastString = model?.getLineContent(lineNumber);
                    if (column > lastString?.length) {
                      const nextInput = document.getElementById(
                        `default-value-row-${index}`
                      );
                      (nextInput as HTMLInputElement).focus();
                    }
                  }
                }
                if (event.code === "ArrowLeft") {
                  const { column, lineNumber } = editor.getPosition();
                  if (lineNumber === 1 && column === 1) {
                    const previousInput = document.getElementById(
                      `name-row-${index}`
                    );
                    (previousInput as HTMLInputElement).focus();
                  }
                }
              });
            }}
            value={item.type}
          />
        );
      }
    },
    {
      key: "defaultValue",
      minWidth: 100,
      name: "Default Value",
      onRender: (item, index) => (
        <input id={`default-value-row-${index}`} value={item.defaultValue} />
      )
    }
  ];

  const items = [{ name: "name", type: "type", defaultValue: "name" }];

  return <DetailsList columns={columns} items={items} />;
}

You can see it working in this codesandbox https://codesandbox.io/s/wild-smoke-vy61m?file=/src/App.tsx

monaco-editor seems to be something quite complex, probably you'll have to improve this code in order to support other interactions (ex: I don't know if this works when code is folded)

Upvotes: 2

Related Questions