greena13 / react-hotkeys

Declarative hotkey and focus area management for React
https://github.com/greena13/react-hotkeys
ISC License
2.15k stars 160 forks source link

[BUG] Parent HotKeys not receiving handlers #280

Open v-bbrady opened 4 years ago

v-bbrady commented 4 years ago

I am working within a Notebook style application attempting to add Keyboard shortcuts. I am using react-hotkeys to do so. I've run into an issue. If I wrap the component (ConectedCell.tsx) that holds all the actions and properties that I wish to trigger with the HotKeys and pass it both the handlers and the keymap, then everything works as expected as hotkeys works by triggering shortcuts when the HotKeys components child is in focus. However doing this the hotkeys only work when a cell is in focus, and I would like them to work at the notebook level, so any where in the application I have focus will be able to call the actions at the cell level. So I have wrapped a parent component (NotebookShell) of the ConnectedCell so that when the notebook is in focus it will still trigger the hotkeys. I have passed in the keymap from a helper file into the NotebookShell, and I have passed in the handlers to ConnectedCell.tsx Area . The keymap is working fine, however they do not seem to be getting the handlers.

Note: Even with the setup below, the hotkeys will fire when a cell is specifically focused, however when I focus the notebook they do not. I have the logs at the bottom, and you can see when I click the cell in the options you will see the handlers, however when I click the Notebook, you will not see the handlers in the log, they will be empty.

According to the react-hotkeys documentation, the setup I have should work.

KeyMap

export const keyMap = {
  DELETE_CELL: "shift+d"
};

--

Handlers

export const handlers = (props: ICellProps) => {
    return {
        DELETE_CELL: () => onKeyDeleteCell(props),
    };
};

--


**Child Component, ConnectedCell.tsx, 3rd Child of NotebookShell.tsx**

public render() {
    const {
        deleteCell
    } = this.props;

    let cellElement = (
        <div id={`cell-${id}`} className="cell-core-area">
            <HotKeys handlers={handlers(this.props)}>
                <CellCoreArea />
            </Hotkeys>
        </div>
    );
}

--

NotebookShell, Parent of ConnectedCell, 3rd Parent of Connected Cell

  public render() {
        <HotKeys keyMap={keyMap} allowChanges={true}>
          <div
            className="notebook-shell component-box"
            onFocus={this.onFocus}
            onBlur={this.onBlur}
            tabIndex={-1}
            ref={this.notebookRef}
          >
            <div className="aznb-full-flex-column">
              <NotebookHeader>
                {showCommandBar ? <Toolbar /> : null}
                {showKernelspecsMessageBar ? <KernelspecsMessageBar /> : null}
                <ErrorBar />
                {showStatusBar ? <StatusBar /> : null}
                <SurveyBar />
              </NotebookHeader>
              <div className="aznb-full-flex-column notebook-container">
                <Notebook contentRef={contentRef} />
              </div>
              <ShellExtensions exProps={{}} />
            </div>
          </div>
        </HotKeys>
      );
    }
    return null;
  }

--

Log when cell is focused


 {
    "actions": {},
    "handlers": {
        "CHANGE_CELL_TYPE_TO_CODE": "() => onKeyChangeCellTypeToCode(props)",
        "CHANGE_CELL_TYPE_TO_MARKDOWN": "() => onKeyChangeCellTypeToMarkdown(props)",
        "COPY_CELL": "() => onKeyCopyCell(props)",
        "CUT_CELL": "() => onKeyCutCell(props)",
        "DELETE_CELL": "() => onKeyDeleteCell(props)",
        "EXECUTE_CELL": "() => onKeyExecuteCell(props)",
        "EXECUTE_CELL_INSERT_BELOW": "() => onKeyExecuteCellThenInsertCellBelow(props)",
        "FOCUS_ABOVE": "() => onKeyFocusAbove(props)",
        "FOCUS_BELOW": "() => onKeyFocusBelow(props)",
        "FOCUS_EDITOR": "(e) => onKeyFocusEditor(props, e)",
        "INSERT_ABOVE": "() => onKeyInsertCellAbove(props)",
        "INSERT_BELOW": "() => onKeyInsertCellBelow(props)",
        "INTERRUPT_KERNEL": "() => onKeyInterruptKernel(props)",
        "PASTE_CELL_BELOW": "() => onKeyPasteCellBelow(props)",
        "RESTART_KERNEL": "() => onKeyRestartKernel(props)",
        "TOGGLE_OUTPUT": "() => onKeyToggleCellOutputVisibility(props)",
        "TOGGLE_OUTPUT_SCROLLING": "() => onKeyToggleOutputScrolling(props)"
    },
    "componentId": 1,
    "options": {
        "defaultKeyEvent": "keydown"
    }
}

--

Log when notebook is focused

 {
    "actions": {        
        "DELETE_CELL": [
        {
            "prefix": "",
            "actionName": "DELETE_CELL",
            "sequenceLength": 1,
            "id": "Shift+d",
            "keyDictionary": {
                "Shift": true,
                "d": true
            },
            "keyEventType": 0,
            "size": 2
        }
    ],
  },
  "handlers": {},
  "componentId": 1,
  "options": {
        "defaultKeyEvent": "keydown"
    }
}

Version : v2.0.0