Skip to content

Focus Target is a focus management utility that allows React developers to easily create navigation flows and control where and how the focus in the page goes.

License

Notifications You must be signed in to change notification settings

AnderSouza/focus-target

Repository files navigation

Focus Target

GitHub repo size GitHub npm

Focus Target is a focus management utility that allows React developers to easily create navigation flows and control where and how the focus in the page goes. You can set keyboard shortcuts and define what element will have the initial focus.

🌟 Features

  • 👍 React Component with a non-intrusive syntax.
  • ⚙️ Custom hook to provide more flexibility.
  • ⌨️ Set your own keyboard shortcuts. As many as you like.
  • 🔍 Initial focus definition.
  • 🛡️ Built with TypeScript.

💻 Installation

You can get it through NPM.

npm install --save focus-target

🛠️ Usage

Here's how to use the component:

import React from "react";
import { EventBoundary } from "focus-target";
const targets = [
  {
    name: "firstName",
    keys: [["Control", "Alt", "1"]],
  },
  {
    name: "lastName",
    keys: [["Control", "Alt", "2"]],
  },
  {
    name: "age",
    previous: "lastName",
    keys: [["Control", "Alt", "3"]],
  },
];
export function App() {
  return (
    <EventBoundary targets={targets} initialFocus="firstName">
      <label>First name:</label>
      <input name="firstName" type="text" />
      <label>Last name:</label>
      <input name="lastName" type="text" />
      <label>Age:</label>
      <input name="age" type="number" />
    </EventBoundary>
  );
}

This is the Target type:

 type Target = {
  name: string;
  previous?: string;
  keys: string[][];
}

The EventBoundary takes an array of Target objects. The initialFocus basically receives the name of the input used to apply the focus on once the component mounts.

The targets themselves are composed of the values of the name attribute of the inputs, the event.key value for any keys you decide to use and the previous element. The previous element corresponds to the element that is focused at the moment you press the keys shortcut. Look at this target taken from the example:

{
  name: "age",
  previous: "lastName",
  keys: [["Control", "Alt", "3"]],
}

This target determines that the shortcut Control + Alt + 3 is only going to work if the input with the name lastName is focused at the moment the shortcut is pressed.

EventBoundary only captures events when itself or its inputs are focused. In order to capture events globally, you should use the useFocusTarget hook described further down.

Lastly, you should know that refs and event handlers are being passed to the elements under the hood. They are required to make the event capturing and focus work properly. But they are only going to work if the inputs are at the first nesting level. The following example wouldn't work. Note how the inputs are inside the labels, therefore not in the first nesting level.

export function App() {
  return (
    <EventBoundary targets={targets} initialFocus="firstName">
      <label>
        First name: <input name="firstName" type="text" />
      </label>
      <label>
        Last name: <input name="lastName" type="text" />
      </label>
      <label>
        Age:
        <input name="age" type="number" />
      </label>
    </EventBoundary>
  );
}

Here's how to use the useFocusTarget hook:

import React, { useEffect } from "react";
import { useFocusTarget } from "focus-target";

const targets = [
  {
    name: "firstName",
    keys: [["Control", "Alt", "1"]],
  },
  {
    name: "lastName",
    keys: [["Control", "Alt", "2"]],
  },
  {
    name: "age",
    previous: "lastName",
    keys: [["Control", "Alt", "3"]],
  },
];

export function App() {
  const {
    getRef,
    handleKeyDown,
    handleKeyUp,
    handleFocus,
    focus,
  } = useFocusTarget(targets, false);
  useEffect(() => {
    focus("firstName");
  }, []);
  return (
    <div onKeyDown={handleKeyDown} onKeyUp={handleKeyUp}>
      <label>First name:</label>
      <input
        name="firstName"
        type="text"
        ref={getRef("firstName")}
        onFocus={handleFocus}
      />
      <label>Last name:</label>
      <input
        name="lastName"
        type="text"
        ref={getRef("lastName")}
        onFocus={handleFocus}
      />
      <label>Age:</label>
      <input
        name="age"
        type="number"
        ref={getRef("age")}
        onFocus={handleFocus}
      />
    </div>
  );
}

The useFocusTarget hook can be used instead of the EventBoundary component. It takes two arguments: the targets and a boolean to indicate whether the event capturing will be global or not.

In the above example the global event capturing is deactivated and a div was used to wrap the inputs inside the handleKeyDown and handleKeyUp handlers. Activating global event capturing would make these two handlers be attached to the window object. In addition, useEffect was used to setup the initial focus on the firstName input once the component mounts.

getRef returns a referent to be attached to the correspondent input and handleFocus is used in order to know which component is currently focused.

📧 Contact

If you want to contact me, checkout my LinkedIn.

📄 License

This project is licensed under the MIT License.

About

Focus Target is a focus management utility that allows React developers to easily create navigation flows and control where and how the focus in the page goes.

Topics

Resources

License

Stars

Watchers

Forks