Используйте клавишу Tab, не работающую с редактируемой таблицей данных Prime-реакции - PullRequest
0 голосов
/ 03 июня 2018

Я использовал редактируемые данные таблицы прайм-реакции, и она работала щелчком мыши.Но проблема в том, что клавиша табуляции не работает. Я хочу включить режим редактирования одной ячейки в другую, используя клавишу табуляции.Я использую Prime response версии 1.4.0

Полный код приведен ниже: -

index.js , где код моей редактируемой таблицы содержит

import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { FormattedMessage } from 'react-intl';
import { createStructuredSelector } from 'reselect';
import { compose } from 'redux';
import injectSaga from 'utils/injectSaga';
import injectReducer from 'utils/injectReducer';
import reducer from './reducer';
import saga from './saga';
import messages from './messages';
import { Messages } from 'primereact/components/messages/Messages';
import { Growl } from 'primereact/components/growl/Growl';
import { Panel } from 'primereact/components/panel/Panel';
import { Button } from 'primereact/components/button/Button';
import { DataTable } from 'primereact/components/datatable/DataTable';
import { Column } from 'primereact/components/column/Column';
import { InputText } from 'primereact/components/inputtext/InputText';
import { Dropdown } from 'primereact/components/dropdown/Dropdown';
import CustomDataTable from 'components/CustomDataTable';
import { makeSelectSelectedStudent, makeSelectSectionName, makeSelectStudentUpdateBasicInformation, makeSelectSectionList, makeSelectStdBasicInfo, makeSelectEditorStudent, makeSelectSetMessage, makeSelectSetErrMessage, makeSelectLoaderOff, makeSelectLoaderOn } from './selectors';
import { selectStudent, setEditorData, submitUpdate, getMessage, getErrMessage, submitForm, changeSectionName, getLoaderOn, getLoaderOff } from './actions';
import AppPrivateLayout from '../AppPrivateLayout';

export class StudentUpdateBasicInformation extends React.Component {
  componentDidUpdate() {
    this.props.changeMessage('');
  }

  rollBody(rowData) {
    return <InputText type="text" value={rowData.studentRoll} />;
  }

  nameBody(rowData) {
    return <InputText type="text" value={rowData.studentName} />;
  }

  fatherNameBody(rowData) {
    return <InputText type="text" value={rowData.fatherName} />;
  }

  motherNameBody(rowData) {
    return <InputText type="text" value={rowData.motherName} />;
  }

  contactBody(rowData) {
    return <InputText type="text" value={rowData.guardianMobile} />;
  }

  genderBody(rowData) {
    let gender = [
      { label: 'Male', value: 'Male' },
      { label: 'Female', value: 'Female' },
      { label: 'Others', value: 'Others' }
    ];

    return <Dropdown value={rowData.studentGender} options={gender} style={{ width: '100%' }} placeholder="Select" />
  }

  religionBody(rowData) {
    let religion = [
      { label: 'Islam', value: 'Islam' },
      { label: 'Hindu', value: 'Hindu' },
      { label: 'Buddhist', value: 'Buddhist' },
      { label: 'Christian', value: 'Christian' },
      { label: 'Others', value: 'Others' }
    ];
    return <Dropdown value={rowData.studentReligion} options={religion} style={{ width: '100%' }} placeholder="Select" />
  }

  bloodBody(rowData) {
    let blood = [
      { label: 'A+', value: 'A+' },
      { label: 'A-', value: 'A-' },
      { label: 'B+', value: 'B+' },
      { label: 'B-', value: 'B-' },
      { label: 'O+', value: 'O+' },
      { label: 'O-', value: 'O-' }

    ];
    return <Dropdown value={rowData.bloodGroup} options={blood} style={{ width: '100%' }} placeholder="Select" />
  }

  render() {
    let rollEditor = (row) => {
      return <InputText onChange={(evt) => { this.props.onEditorValueChange(row, evt.target.value); }} placeholder="Roll" value={row.rowData.studentRoll} />
    }

    let nameEditor = (row) => {
      return <InputText onChange={(evt) => { this.props.onEditorValueChange(row, evt.target.value); }} placeholder="Student name" value={row.rowData.studentName} />
    }

    let fatherNameEditor = (row) => {
      return <InputText onChange={(evt) => { this.props.onEditorValueChange(row, evt.target.value); }} placeholder="Father name" value={row.rowData.fatherName} />
    }

    let motherNameEditor = (row) => {
      return <InputText onChange={(evt) => { this.props.onEditorValueChange(row, evt.target.value); }} placeholder="Mother name" value={row.rowData.motherName} />
    }

    let contactEditor = (row) => {
      return <InputText onChange={(evt) => { this.props.onEditorValueChange(row, evt.target.value); }} placeholder="Contact Number" value={row.rowData.guardianMobile} />
    }

    let genderEditor = (row) => {
      let gender = [
        { label: 'Male', value: 'Male' },
        { label: 'Female', value: 'Female' },
        { label: 'Others', value: 'Others' }
      ];

      return <Dropdown value={row.rowData.gender} options={gender} onChange={(evt) => this.props.onEditorValueChange(row, evt.value)} style={{ width: '100%' }} placeholder="Select" />
    }

    let religionEditor = (row) => {
      let religion = [
        { label: 'Islam', value: 'Islam' },
        { label: 'Hindu', value: 'Hindu' },
        { label: 'Buddhist', value: 'Buddhist' },
        { label: 'Christian', value: 'Christian' },
        { label: 'Others', value: 'Others' }
      ];
      return <Dropdown value={row.rowData.religion} options={religion} onChange={(evt) => { this.props.onEditorValueChange(row, evt.value); }} style={{ width: '100%' }} placeholder="Select" />
    }

    let bloodEditor = (row) => {
      let blood = [
        { label: 'A+', value: 'A+' },
        { label: 'A-', value: 'A-' },
        { label: 'B+', value: 'B+' },
        { label: 'B-', value: 'B-' },
        { label: 'O+', value: 'O+' },
        { label: 'O-', value: 'O-' }

      ];
      return <Dropdown value={row.rowData.blood} options={blood} onChange={(evt) => { this.props.onEditorValueChange(row, evt.value); }} style={{ width: '100%' }} placeholder="Select" />
    }

    let msg = "";
    if (this.props.setMessage) {
      msg = { severity: 'success', detail: this.props.setMessage.message };
      this.growl.show(msg);
    }
    else if (this.props.setErrMessage) {
      msg = { severity: 'error', summary: 'Failed', detail: this.props.setErrMessage };
      this.growl.show(msg);
    }
    if(this.props.loaderOn){
      if(this.props.loaderOn === 'On') {
      $('.loaderDiv').show();
    } else if(this.props.loaderOn === 'Off'){
      $('.loaderDiv').hide();
    }
  }
    let content = '';
    if (this.props.stdBasicInfo && this.props.stdBasicInfo.length) {
      $('#UpdateBtnID').show();
      let selectedStudentArr = [];
      if (this.props.selectedStudent.length) {
        Array.prototype.push.apply(selectedStudentArr, this.props.selectedStudent);
      }
      let columnData = [
        <Column selectionMode="multiple" header="Mark" style={{ width: '3em' }} />,
        <Column field="studentRoll" header="Roll No." editor={rollEditor} body={this.rollBody} style={{ width: '55px' }} />,
        <Column field="studentName" header="Name" editor={nameEditor} body={this.nameBody} style={{ width: '170px' }} />,
        <Column field="fatherName" header="Father Name" editor={fatherNameEditor} body={this.fatherNameBody} style={{ width: '145px' }} />,
        <Column field="motherName" header="Mother Name" editor={motherNameEditor} body={this.motherNameBody} style={{ width: '145px' }} />,
        <Column field="guardianMobile" header="Contact No." editor={contactEditor} style={{ width: '100px' }} body={this.contactBody} />,
        <Column field="studentGender" header="Gender" editor={genderEditor} body={this.genderBody} style={{ width: '85px' }} />,
        <Column field="studentReligion" header="Religion" editor={religionEditor} body={this.religionBody} style={{ width: '85px' }} />,
        <Column field="bloodGroup" header="Blood Group" editor={bloodEditor} style={{ width: '80px' }} body={this.bloodBody} />
      ];
      content = <CustomDataTable
        info={this.props.stdBasicInfo}
        onSelectionChange={this.props.onSelectionChange}
        selectedData={selectedStudentArr}
        columnData={columnData}
        isSelectionOn={true}
        editable={true}
        header={'Student List'}
        rows={10}
      />
    }

    //FOR SECTION LIST
    let sectionListOptions = [];
    if (this.props.sectionList && this.props.sectionList.length) {
      sectionListOptions = this.props.sectionList.map((item) => ({
        value: item.classConfigId,
        label: item.classShiftSection,
      }))
    }

    return (
      <div>
        <AppPrivateLayout>
        <Panel header="Student Information Update">
          <form method="post" onSubmit={this.props.onSubmitForm} >
            <div className='ui-g form-group'>

              <div className='ui-g-2 ui-lg-2 ui-md-2'></div>
              <div className='ui-g-2 ui-lg-2 ui-md-2 ui-sm-12 netiLabel'>
                <label> Section <span className="required"> * </span></label>
              </div>
              <div className='ui-g-3 ui-lg-3 ui-md-4 ui-sm-12 ui-fluid'>
                <Dropdown value={this.props.sectionname} onChange={this.props.onChangeSectionList} options={sectionListOptions} placeholder="Select Section" autoWidth={false} />
              </div>
              <div className='ui-g-2 ui-lg-2 ui-md-3 ui-sm-12 ui-fluid'>
                <Button icon="ui-icon-search" title="Search" label='Search'></Button>
              </div>
              <div className='ui-g-2 ui-lg-2 ui-fluid'></div>
            </div>
          </form>
          {content}
          <div className='ui-g'>
            <Growl ref={(el) => this.growl = el} />
            <div className='ui-g-4 ui-lg-4 ui-md-4 ui-sm-12 ui-fluid'>

            </div>
            <div className='ui-g-6 ui-lg-6 ui-md-5 ui-sm-12 ui-fluid'></div>
            <div className='ui-g-2 ui-lg-2 ui-md-3 ui-sm-12 ui-fluid'>
              <Button id="UpdateBtnID" style={{ display: 'none' }} onClick={this.props.onUpdate} icon='ui-icon-autorenew' label='Update'></Button>
            </div>
          </div>
        </Panel>
        <div class="loaderDiv" style={{display: 'none'}}>
            <img className="sticky" src="https://loading.io/spinners/harmony/lg.harmony-taiji-spinner.gif" />
          </div>
        </AppPrivateLayout>
      </div>
    );
  }
}

StudentUpdateBasicInformation.propTypes = {
  stdBasicInfo: PropTypes.any,
  onSubmitForm: PropTypes.func,
  onEditorValueChange: PropTypes.func,
  value: PropTypes.any,
  onUpdate: PropTypes.func,
  setMessage: PropTypes.any,
  setErrMessage: PropTypes.any,
  changeMessage: PropTypes.func,
  sectionList: PropTypes.any,
  onChangeSectionList: PropTypes.func,
  sectionname: PropTypes.any,
  loaderOn: PropTypes.any,
};

const mapStateToProps = createStructuredSelector({
  stdBasicInfo: makeSelectStdBasicInfo(),
  selectedStudent: makeSelectSelectedStudent(),
  value: makeSelectEditorStudent(),
  setMessage: makeSelectSetMessage(),
  setErrMessage: makeSelectSetErrMessage(),
  sectionList: makeSelectSectionList(),
  sectionname: makeSelectSectionName(),
  loaderOn: makeSelectLoaderOn(),
});

function mapDispatchToProps(dispatch) {
  return {
    changeMessage: (evt) => {
      dispatch(getMessage());
      dispatch(getErrMessage());
      dispatch(getLoaderOn(evt));
    },

    onSubmitForm: (evt) => {
      if (evt !== undefined && evt.preventDefault)
        evt.preventDefault();
      dispatch(submitForm());
    },
    onSelectionChange: (evt) => dispatch(selectStudent(evt.data)),
    onEditorValueChange: (row, value) => {
      dispatch(setEditorData(row, value));
    },
    onUpdate: (evt) => dispatch(submitUpdate()),
    onChangeSectionList: (evt) => dispatch(changeSectionName(evt.value)),
  };
}

const withConnect = connect(mapStateToProps, mapDispatchToProps);
const withReducer = injectReducer({ key: 'studentUpdateBasicInformation', reducer });
const withSaga = injectSaga({ key: 'studentUpdateBasicInformation', saga });

export default compose(
  withReducer,
  withSaga,
  withConnect,
)(StudentUpdateBasicInformation);

constants.js

export const DEFAULT_ACTION = 'app/StudentUpdateBasicInformation/DEFAULT_ACTION';

export const SUBMIT_FORM = 'app/StudentUpdateBasicInformation/SUBMIT_FORM';
export const SET_STD_BASIC_INFO = 'app/StudentUpdateBasicInformation/SET_STD_BASIC_INFO';
export const SELECT_STUDENT = 'app/StudentUpdateBasicInformation/SELECT_STUDENT';
export const SET_EDITOR_DATA = 'app/StudentUpdateBasicInformation/SET_EDITOR_DATA';
export const GET_MESSAGE = 'app/StudentUpdateStudentId/GET_MESSAGE';
export const GET_ERR_MESSAGE = 'app/StudentUpdateStudentId/GET_ERR_MESSAGE';
export const SUBMIT_UPDATE = 'app/StudentUpdateBasicInformation/SUBMIT_UPDATE';
export const SET_SECTION_LIST = 'app/StudentUpdateBasicInformation/SET_SECTION_LIST';
export const CHANGE_SECTIONNAME = 'app/StudentUpdateBasicInformation/CHANGE_SECTIONNAME';
export const GET_LOADER_ON = 'app/StudentUpdateBasicInformation/GET_LOADER_ON';

actions.js

import {
  DEFAULT_ACTION, SUBMIT_FORM, SET_STD_BASIC_INFO, SELECT_STUDENT, SET_EDITOR_DATA, SUBMIT_UPDATE, GET_MESSAGE, GET_ERR_MESSAGE, CHANGE_SECTIONNAME, SET_SECTION_LIST, GET_LOADER_OFF, GET_LOADER_ON
} from './constants';

export function defaultAction() {
  return {
    type: DEFAULT_ACTION,
  };
}

export function setStdBasicInfo(item) {
  return {
    type: SET_STD_BASIC_INFO,
    item,
  }
}

export function selectStudent(data) {
  return {
    type: SELECT_STUDENT,
    data,
  };
}

export function setEditorData(row, value) {
  return {
    type: SET_EDITOR_DATA,
    row, 
    value,
  };
}


export function submitForm() {
  return {
    type: SUBMIT_FORM,
  };
}

export function submitUpdate() {
  return {
    type: SUBMIT_UPDATE,
  };
}

export function getMessage(message) {
  return {
    type: GET_MESSAGE,
    message,
  }
}

export function getErrMessage(errmessage) {
  return {
    type: GET_ERR_MESSAGE,
    errmessage,
  }
}

export function setSectionList(sectionList) {
  return {
    type: SET_SECTION_LIST,
    sectionList,
  };
}
export function changeSectionName(sectionname) {
  return {
    type: CHANGE_SECTIONNAME,
    sectionname,
  };
}


export function getLoaderOn(loaderOn){
  return{
    type: GET_LOADER_ON,
    loaderOn,
  }
}

reducer.js

import { fromJS } from 'immutable';
import {
  DEFAULT_ACTION, SET_STD_BASIC_INFO, SELECT_STUDENT, SET_EDITOR_DATA, GET_MESSAGE, GET_ERR_MESSAGE, SET_SECTION_LIST, CHANGE_SECTIONNAME, GET_LOADER_ON, GET_LOADER_OFF
} from './constants';

const initialState = fromJS({
  selectedStudent: [],
  value: [],
  sectionList: {},
  sectionname: '',
});

function studentUpdateBasicInformationReducer(state = initialState, action) {
  switch (action.type) {
    case DEFAULT_ACTION:
      return state;
    case SET_STD_BASIC_INFO:
      return state.set('stdBasicInfo', action.item);
    case SELECT_STUDENT:
      return state.set('selectedStudent', action.data);
    case SET_EDITOR_DATA:
      let updatedInfost = [...action.row.value];
      updatedInfost = [...action.row.value];
      updatedInfost[action.row.rowIndex][action.row.field] = action.value;
      return state.set('stdBasicInfo', updatedInfost);
    case GET_MESSAGE:
      return state.set('setMessage', action.message);
    case GET_ERR_MESSAGE:
      return state.set('setErrMessage', action.errmessage);
    case SET_SECTION_LIST:
      return state.set('sectionList', action.sectionList);
    case CHANGE_SECTIONNAME:
      return state.set('sectionname', action.sectionname);
      case GET_LOADER_ON:
      return state.set('loaderOn', action.loaderOn);
      // case GET_LOADER_OFF:
      // return state.set('loaderOff', action.loaderOff);
    default:
      return state;
  }
}

export default studentUpdateBasicInformationReducer;

selectors.js

import { createSelector } from 'reselect';

const selectStudentUpdateBasicInformationDomain = (state) => state.get('studentUpdateBasicInformation');

const makeSelectStudentUpdateBasicInformation = () => createSelector(
  selectStudentUpdateBasicInformationDomain,
  (substate) => substate.toJS()
);

const makeSelectStdBasicInfo = () => createSelector(selectStudentUpdateBasicInformationDomain, (abc) => abc.get('stdBasicInfo'));
const makeSelectSelectedStudent = () => createSelector(selectStudentUpdateBasicInformationDomain, (substate) => substate.get('selectedStudent'));
const makeSelectEditorStudent = () => createSelector(selectStudentUpdateBasicInformationDomain, (substate) => substate.get('value'));
const makeSelectSetMessage = () => createSelector(selectStudentUpdateBasicInformationDomain, (abc) => abc.get('setMessage'));
const makeSelectSetErrMessage = () => createSelector(selectStudentUpdateBasicInformationDomain, (abc) => abc.get('setErrMessage'));
const makeSelectSectionName = () => createSelector(selectStudentUpdateBasicInformationDomain, (abc) => abc.get('sectionname'));
const makeSelectSectionList = () => createSelector(selectStudentUpdateBasicInformationDomain,(substate) => substate.get('sectionList'));
const makeSelectLoaderOn = () => createSelector(selectStudentUpdateBasicInformationDomain, (substate) => substate.get('loaderOn'));

export {
  selectStudentUpdateBasicInformationDomain,
  makeSelectStdBasicInfo,
  makeSelectSelectedStudent,
  makeSelectEditorStudent,
  makeSelectSetMessage,
  makeSelectSetErrMessage, makeSelectSectionName, makeSelectSectionList, makeSelectLoaderOn, 
  //makeSelectLoaderOff
};

saga.js

import { take, call, put, select, takeLatest } from 'redux-saga/effects';
import { BASE_URL, FETCH_BASIC_INFO_LIST, UPDATE_ID, GET_CLASS_CONFIGURATION_URL, STUDENT_CONFIG_LIST } from '../../utils/serviceUrl';
import { setStdBasicInfo, getMessage, getErrMessage, selectStudent, setSectionList, changeSectionName, getLoaderOn } from './actions';
import request from '../../utils/request';
import { SUBMIT_FORM, SUBMIT_UPDATE } from './constants';
import { makeSelectEditorStudent, makeSelectSelectedStudent, makeSelectSectionList, makeSelectSectionName  } from './selectors';
import { getTokenData } from '../../utils/authHelper';

//FOR SECTION LIST
export function* fetchSectionList() {
  const tokenData = JSON.parse(getTokenData());
  const requestUrl = BASE_URL.concat(GET_CLASS_CONFIGURATION_URL);
  const options = {
    method: 'GET',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': tokenData.token_type+" "+tokenData.access_token,
    },
  };
  try {
    const response = yield call(request, requestUrl, options);
    if (response.item) {
      yield put(setSectionList(response.item));
    }
  } catch (err) {
    console.dir(err);
  }
}

//FOR STUDENT BASIC INFO LIST
export function* fetchStudentBasicInfoList() {
  const tokenData = JSON.parse(getTokenData());
  const classConfigId = yield select(makeSelectSectionName());
  let msgg;
  if (classConfigId == '') {
    let msg = "An error has occured. Please fill up all required fields";
    yield put(getErrMessage(msg));

  }
  else {
    msgg = 'On';
    yield put(getLoaderOn(msgg));
  const requestURL = BASE_URL.concat(STUDENT_CONFIG_LIST).concat('?classConfigId=').concat(classConfigId);
  const options = {
    method: 'GET',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': tokenData.token_type+" "+tokenData.access_token,
    },
  };
  const info = yield call(request, requestURL,options);
  yield put(setStdBasicInfo(info.item));
  msgg = 'Off';
  yield put(getLoaderOn(msgg));
  }
}


//FOR UPDATE STUDENT INFORMATION
export function* updateStdBasicInfo() {
  const tokenData = JSON.parse(getTokenData());
  const selectedCheckData = yield select(makeSelectSelectedStudent());
  let selectedData = [];
  if (selectedCheckData.length === undefined || selectedCheckData.length === 0) {
    const errresult = "An error has occured. Please fill up all required fields";
    yield put(getErrMessage(errresult));
  } else {

    for (const i in selectedCheckData) {
      const DataList = selectedCheckData[i];
      selectedData.push(DataList);
    }

    const requestURL = BASE_URL.concat(UPDATE_ID);
    const options = {
      method: 'PUT',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'Authorization': tokenData.token_type+" "+tokenData.access_token,
      },

      body: JSON.stringify(selectedData),
    }

    try {
      const result = yield call(request, requestURL, options);
      yield put(selectStudent([]));
      yield fetchStudentBasicInfoList();
      yield put(getMessage(result));

    } catch (err) {
      const errresult = "Something went wrong. Please try again.";
      yield put(setStdBasicInfo(info.item));
      yield put(getErrMessage(errresult));

    }
  }
}


export default function* defaultSaga() {
  yield fetchSectionList();
  yield takeLatest(SUBMIT_FORM, fetchStudentBasicInfoList);
  yield takeLatest(SUBMIT_UPDATE, updateStdBasicInfo);

}

Ответы [ 2 ]

0 голосов
/ 09 января 2019

Ответ Душана работал для меня, но за 2 исключениями:

В onEditorKeyDown вместо

var table = this.dt.container.childNodes[1].childNodes[0].childNodes[1];

используйте

let table = this.dt.container.childNodes[0].childNodes[0].childNodes[1];

Кроме того, (event.which === 9)следует использовать вместо (event.keyCode == 9), и все функции должны быть связаны в конструкторе:

this.inputTextEditor = this.inputTextEditor.bind(this)
this.xxxEditor = this.xxxEditor.bind(this)
this.onEditorKeyDown = this.onEditorKeyDown.bind(this)
0 голосов
/ 04 июня 2018

У меня есть идея, как вы можете это сделать:

пока вы находитесь в редактируемой ячейке, вам нужно перехватить клавишу TAB, а затем смоделировать щелчок по следующей ячейке справа или, если текущая ячейка являетсяпоследний, в первой ячейке в следующем ряду.

Шаги в следующем примере основаны на показе редактируемой страницы регистра PrimeReact .Вы можете принять их для вашего конкретного случая.

ФУНКЦИОНАЛЬНЫЙ ПРИМЕР

Шаг 1 :

определить onRowClick обработчик идобавьте его к DataTable компоненту, чтобы можно было отследить нажатые в данный момент rowIndex (нам нужно знать, каков индекс строки ячейки, отредактированной в данный момент)

onRowClick(event) {
    this.rowIndex = event.index;
}
...
<DataTable ref={(el) => this.dt = el} editable={true} onRowClick={(e) => this.onRowClick(e)} ...>

Шаг 2:

Определить индекс столбца (порядковый номер) для каждого из редакторов столбцов: 1-й столбец имеет индекс 0, 2-й столбец индекс 1 и т. Д. Например,

vinEditor(props) {
    return this.inputTextEditor(props, 'vin', 0);
}

yearEditor(props) {
    return this.inputTextEditor(props, 'year', 1);
}

brandEditor(props) {
    return this.inputTextEditor(props, 'brand', 2);
}

, где inputTextEditor теперь выглядитнапример,

inputTextEditor(props, field, columnIndex) {
    return <InputText type="text" value={props.rowData.year}
     onKeyDown={(e) => this.onEditorKeyDown(e, columnIndex)} onChange={(e) => this.onEditorValueChange(props, e.target.value)} />;
}

Обратите внимание, что я добавил обработчик onKeyDown и передал ему columnIndex arg, чтобы мы могли распознать столбец, в котором введен какой-то ключ.

Шаг 3:

Наконец, мы можем определить onEditorKeyDown, чтобы творить чудеса (см. Комментарии к коду для дополнительных объяснений)

onEditorKeyDown(event, columnIndex) {
    console.log("onKeyDown", event);
    console.log("key code", event.keyCode);
    console.log("columnIndex", columnIndex);
    console.log("rowIndex", this.rowIndex);

    //change following 2 constants to to fit your case
    const columnCount = 4;
    const rowCount = 10;

    //check if TAB (key code is 9) is pressed on InputText
    if (event.keyCode == 9) {
        //prevent default behaviour on TAB press
        event.preventDefault();

        //this.dt is reference to DataTable element
        //get reference to `table` node only
        var table = this.dt.container.childNodes[1].childNodes[0].childNodes[1];

        //check if we are not in last column
        if (columnIndex < columnCount - 1) {
            //simulate click on next column
            table.childNodes[this.rowIndex].childNodes[columnIndex + 1].click();
        } else {
            //we are in the last column, check if we are not in last row
            if (this.rowIndex < rowCount - 1) {
                //we are not in the last row
                // select next row
                this.rowIndex += 1;

                //simulate click on first column in next row
                table.childNodes[this.rowIndex].childNodes[0].click();
            }
        }
    }
}
...