greW
greW

Reputation: 1338

control custom list component with keyboard using react

I have a component that shows a List with Items inside (names for this example), every name is wrapped under Item component which is under List component.

What I'm trying to achieve is to be able to "navigate" in the list's items with the keyboard arrows and enter press to act like a mouse click - in the Input component (on the current item that you achieved to get on with the arrow keys),

The issue is that I'm not sure how to tackle it, I tried a few ways but every each one of them got into a dead end.

I'm adding an example list in codepen that simulate my original code - that includes also all the modules I'm working with.

https://codepen.io/anon/pen/rKGdMJ?editors=0011

thank you!

Upvotes: 2

Views: 845

Answers (2)

Aliaksandr Sushkevich
Aliaksandr Sushkevich

Reputation: 12364

I think one of the approaches could be adding a state with property itemPosition. And saving <List> component to a ref. Then you increase or decrease itemPosition using arrows and select an item through the ref with something like childNodes[itemPosition]. And you handle enter press the similar way then just call .click() on the item.

Upvotes: 2

greendemiurge
greendemiurge

Reputation: 509

I'm going to throw out a big-picture suggestion rather than a particular solution and see if it is helpful.

Handling keypresses in a helpful and consistent manner is a larger problem and a good time to turn to contributed code. It will almost always result in a better outcome than rolling your own system.

For me, I have had good success using the react-keydown library. I will say, it comes with a little extra setup given that you need to configure webpack to use javascript decorators (let me know if you are interested and want help with that).

Below is a code example that uses the library to control tabbing on a page:

class EditContainer extends React.Component {
    constructor(props) {
        super(props);
        this.submit = this.submit.bind(this);
    }

  @keydown(
    'ctrl+1', 'meta+1',
    'ctrl+2', 'meta+2',
    'ctrl+3', 'meta+3',
    'ctrl+4', 'meta+4',
    'ctrl+5', 'meta+5',
    'ctrl+6', 'meta+6',
    'ctrl+7', 'meta+7',
    'ctrl+8', 'meta+8',
    'ctrl+9', 'meta+9',
    'ctrl+s', 'meta+s',
    )
  submit(event) {
    event.preventDefault();
    const {panels, changeActivePanel, saveRecord} = this.props;

    if (event.key === "s") {
      saveRecord();
      return;
    }

    const number_key = parseInt(event.key);
    if (!isNaN(number_key) && (number_key <= panels.length)) {
      changeActivePanel(panels[number_key - 1]);
    }
  }

  render() {
    return (
      <div className="edit-container">
        <Col sm={10} md={10} lg={10}>
        <EditLeftColumn/>
        </Col>
          <Col sm={2} md={2} lg={2}>
        <EditRightColumn />
          </Col>
      </div>
    );
  }
}

And here for reference is my babel.js that lets this work:

  "plugins": [
    "transform-decorators-legacy",
    "transform-object-rest-spread",
    "syntax-async-functions"
  ],
  "presets": ["env", "react"],
  "env": {
    "test": {
      "plugins": [
        "transform-decorators-legacy"
      ],
      "presets": ["env", "react"]
    }
  }
}

As you can see, this makes it really easy to bind individual keys to events and it does not require you to have a specific input field in focus. I think it would be the easiest way to get where you want to go. Comment back if you need more details on implementation. The transform-decorators-legacy is the part you should be interested in to get this library working.

Hope that helps, and good luck!

Upvotes: 0

Related Questions