react-vidio-tv-navigation

    1.0.40 • Public • Published

    React Focus Navigation

    This project aims to build a navigation tool based on React.

    How to use

    There are 3 main components:

    • Controller
    • Parent
    • Child

    The main idea is the following:

    There is one single controller in charge of managing the focus, it means that there is a global state that represents where the focus is.

    This controller has N different parents and each parent has M children. The structure could be like the following:

                         /-----   Controller   -----
                      /               |               \
                   /                  |                 \
                /                     |                   \
            Parent1                 Parent2              Parent3
          /   |   \                   |                   /    \
         /    |    \                  |                  /       \
    Child1  Child2 Child3           Child1             Child1    Child2
    

    By default, the focus starts on the first child of the first parent. If this component has implemented the method "onFocus", it will be executed ( You can define which component you want to start with the focus by passing withFocus )

    Let's say that we want to move the focus to the next child, we need to press the arrow keys in order to move the focus ( we will review this point later ) and the focus is moved from the Child 1 to Child 2.

    If Child 1 has implemented the method "onBlur" it will be executed by the controller before moving the focus to the next Child ( Child 2 ) and if this component has implemented the function "onFocus" it will be executed.

    Once the focus is on one component, by pressing "ENTER", if the component has implemented the method "onEnter" it will be executed.

    Apart of the children management, it's also posible to attach some events when the focus is moved to one of the children associated to one parent. Using the same approach that we used with children, we can define onFocus and onBlur and pass these methods as props to the Parent.

    Example

    import React from "react";
    import ReactDOM from "react-dom";
    import Controller from "./../src/controller";
    import { VerticalParent } from "./../src/parent";
    import { Child } from "./../src/child";
    
    export class Item extends React.Component {
      constructor(props) {
        super(props);
        this.state = { focused: false };
      }
    
      focus() {
        this.setState(() => {
          return { focused: true };
        });
      }
    
      blur() {
        this.setState(() => {
          return { focused: false };
        });
      }
    
      enter(index) {
        alert("Enter on element", index);
      }
    
      render() {
        return (
          <Child
            onFocus={index => this.focus()}
            onEnter={index => this.enter(index)} // These methods return the index of the element
            onBlur={index => this.blur()}
          >
            <span
              className={this.state.focused ? "focusable focused" : "focusable"}
            />
          </Child>
        );
      }
    }
    
    class App extends React.Component {
      focus() {
        alert("focus");
      }
    
      blur() {
        alert("blur");
      }
      render() {
        return (
          <Controller>
            <h1>
              {" "}
              Vertical Columns - By default, it moves the focus either up or down{" "}
            </h1>
            <p>
              If we move out of the scope of each list, it moves to the next or
              previous parent ( if present, if not, nothing happens )
            </p>
            <p>
              Also, by clicking left or right, it moves to the next or previous
              parent ( if present, if not, nothing happens )
            </p>
            <div className={"vertical-columns"}>
              <VerticalParent
                onFocus={() => this.focus()}
                onBlur={() => this.blur()}
                className={"vertical-focusable"}
              >
                <Item />
                <Item />
              </VerticalParent>
              <VerticalParent className={"vertical-focusable"}>
                <Item />
                <Item />
                <span className="focusable">
                  <span> I am not allowed to be focused </span>
                  <span> This item is not part of the Matrix </span>
                </span>
                <Item />
              </VerticalParent>
            </div>
          </Controller>
        );
      }
    }
    
    ReactDOM.render(<App />, document.querySelector("#root"));

    Parents

    There are 3 kind of different parents:

    • Vertical Parent
    • Horizontal Parent
    • Matrix Parent

    As you can imagine, it represents the different ways we can navigate through the parents using the arrow keys.

    Vertical Parent

    By default, if we want to move from one child to another, we should use "UP" and "DOWN". By pressing "UP" we will move the focus to the next child ( if present ) and by pressing "DOWN" we will move the focus to the previous child ( if present ).

    We can combine multiple parents in order to create something like the following:

    VerticalParent 1    VerticalParent 2
    |                   |
    |-- Child 1         |-- Child 1
    |                   |
    |-- Child 2         |-- Child 2
    

    Let's say the focus is on the child 2 of the parent 1. There are 4 possible scenarios:

    • By pressing 'UP' we will move the focus to the Child 1
    • By pressing 'DOWN' there won't be any efect as there are not more children
    • By pressing 'LEFT' there won't be any efect as there are not previous parents
    • By pressing 'RIGHT' we will move the focus to the first child of the next parent

    Horizontal Parent

    By default, if we want to move from one child to another, we should use "LEFT" and "RIGHT". By pressing "RIGHT" we will move the focus to the next child ( if present ) and by pressing "LEFT" we will move the focus to the previous child ( if present ).

    We can combine multiple parents in order to create something like the following:

    HorizontalParent 1 -- Child 1 -- Child 2
    HorizontalParent 2 -- Child 1 -- Child 2
    

    Let's say the focus is on the child 2 of the parent 1. There are 4 possible scenarios:

    • By pressing 'LEFT' we will move the focus to the Child 1
    • By pressing 'RIGHT' there won't be any efect as there are not more children
    • By pressing 'DOWN' we will move the focus to Child 1 of the next parent ( Parent 2)
    • By pressing 'UP' there won't be any efect as there are not previous parents

    Matrix Parent

    By default, if we want to move from one child to another, we can use any arrow key in order to simulate all directions.

    Given the following example:

              MatrixParent 1
                || ( It represents that all children belong to Parent 1)
                ||
    Child 1 -- Child 2 -- Child 3
      |          |           |
    Child 4 -- Child 5 -- Child 6
      |          |           |
    Child 7 -- Child 8 -- Child 9
    

    Let's say the focus is on the child 5. There are 4 possible scenarios:

    • By pressing 'LEFT' we will move the focus to the Child 4
    • By pressing 'RIGHT' we will move the focus to the Child 6
    • By pressing 'DOWN' we will move the focus to the Child 8
    • By pressing 'UP' we will move the focus to the Child 2

    Joining multiple different Parents

    It's possible to combine different kind of parents in order to try to represent the best navigation experience. Let's say we have the following structure:

    VerticalParent 1  --------  MatrixParent 2
    |                               ||
    |-Child 1                       ||
    |                Child 1 -- Child 2 -- Child 3
    |-Child 2             |           |
    |                Child 4 -- Child 5 -- Child 6
    |-Child 3             |           |
                     Child 7 -- Child 8 -- Child 9
    

    Let's say that by default, the Controller moves the focus to the Child 1 on the first Parent ( Parent 1).

    There are 4 different scenarios:

    • By pressing 'DOWN', it moves the focus to the Child 2 of the Parent 1
    • By pressing 'RIGHT", It moves the focus to the Child 1 of the Parent 2
    • By pressing 'LEFT, there won't be any effect as there are not previous parents
    • By pressing 'UP', there won't be any effect as there are not previous children.

    Imagine we press 'RIGHT". It will move the focus to the Child 1 of the Parent 2. As we have moved from a VerticalParent to a MatrixParent, now we have changed the way we can navigate.

    Then, there are 4 different scenarios:

    • By pressing 'DOWN', it moves the focus from Child 1 to Child 4
    • By pressing 'UP', it moves the focus to the previous parent ( Vertical Parent)
    • By pressing 'RIGHT", it moves the focus from Child 1 to Child 2 ( Same Parent )
    • By pressing 'LEFT', it moves the focus to the previous parent as well ( Vertical Parent )

    Starting with the focus

    Sometimes, you will need to start with the focus in another Parent that it's not the first one. You can do that by passing the withFocus as props:

    <Controller>
      <VerticalParent>
        <Children />
        <Children />
      </VerticalParent>
      <VerticalParent>
        <Children />
        <Children />
      </VerticalParent>
      <VerticalParent withFocus>
        <Children />
        <Children />
      </VerticalParent>
    </Controller>

    This way, the app will start having the focus on the third parent instead of the first one. In case of more than one parent using withFocus, the last one will have the focus.

    Identifying wich elements execute the functions

    Both onFocus, onBlur and onEnter, will provide the index as argument of the function execution. This way, it's easier to define which element has either received the focus or received an 'enter' and apply the logic associated to this. Given this example:

      onFocus(index) {
        if (this.props.onFocus) {
          this.props.onFocus();
        }
    
        if (this.content) {
          const items = this.content.getElementsByClassName("item");
          const offsetWidth = items[0].offsetWidth + 20;
          this.content.scrollLeft = offsetWidth * index;
        }
      }
    
      render() {
        const { contents } = this.props;
        return (
            <h1>{this.props.title}</h1>
            <div
              className="content"
              ref={content => {
                this.content = content;
              }}
            >
              <HorizontalParent
                style={{ overflow: "hidden", display: "block" }}
                onFocus={index => this.onFocus(index)}
                withFocus={this.props.withFocus}
              >
                {contents.map(content => (
                  <Child key={content.id} {...content} />
                ))}
              </HorizontalParent>
            </div>
          </div>
        );
      }

    We can move the scroll of the list based on the element with the focus.

    Example

    There is an example in order to understand how to combine multiple Parents. Please, check the folder example in order to use how to reproduce different scenarios

    Install

    npm i react-vidio-tv-navigation

    DownloadsWeekly Downloads

    0

    Version

    1.0.40

    License

    ISC

    Unpacked Size

    390 kB

    Total Files

    48

    Last publish

    Collaborators

    • themcmxciv