Use React Editor in a cell type

Tags: #<Tag:0x00007fbe87a500a8>

Hi,

I’m using Hot using React and Typescript. I have successfully managed to create specific cell types in order to provide my own editors.

For example, I can create a cell type like that :

const cellType = {
      renderer: Handsontable.renderers.NumericRenderer,
      editor: DoubleEditor,
      validator: this.getDoubleValidator(),
    };

Then I managed to create my custom DoubleEditor, following the tutorial here

I’ve seen that it’s possible to declare a custom React editor using the hotColumn attribute here. Doing so will make all cells of the column use the given editor. I would like to provide my custom React editor on my cell type so that it would only be used where I specifically want.

Is there a way of doing that? I tried to create the EditorComponent and reference it in my cellType above directly but that was not working.

Regards,

Hi @samir.hadzic

you can use the table cells method to define what cells should use your custom type/editor. Here’s an example https://jsfiddle.net/qL2vnk5z/2/ hotColumn cannot set up a type/editor for only a couple of cells.

Hi,

In your example, you are giving a normal editor whereas I want to use a React editor.

What I would want to do is that :

If you double-click on the first cell, an exception is thrown because I give a ReactEditor.

You see that I give my React editor in the cellProperties.editor but obviously this is not working. It seems that right now, the only way of giving a React Editor is through the React hotColumn property, and therefore applying the editor for the whole column.

This error seems to come from the value you define methods inside the custom editor. Here’s a fiddle that shows a bit better where the error is https://jsfiddle.net/0yceg641/

Hi Aleksandra,

Thanks for you answer but it’s not answering my question.
I’d like to know if there is any possibility to specify a React component as a cell editor.

Here is what is working:

  <HotTable settings={this.state.hotSettings}>
    <HotColumn width={250}>
      <EditorComponent hot-editor />
    </HotColumn>
  </HotTable>

or even:

  <HotTable settings={this.state.hotSettings}>
    <EditorComponent hot-editor />
  </HotTable>

This is meant to work for a full column or the whole table, but i’m looking for a way to use my custom editor (which is a react component) for a specific cell like this:

From what I can see in the source code, this is working for a regular JS component:
cellProperties.editor = EditorComponent;

But for a react component, it would require to do this like:
cellProperties.editor = <EditorComponent hot-editor />;

which is not working because editor only accepts a string or function. I tried to add the hot-editor attribute to my component root but it’s not changing anything.

Is there any way to use React as an editor for specific cells ?

Thanks,

@samir.hadzic No, it’s not possible currently.

Thanks for the reply.

I have then opened an improvement request on Github here : https://github.com/handsontable/react-handsontable/issues/201

If anyone is trying to achieve what I want. Here is what I’ve achieved that could be a work-around. The idea is to create a unique React editor (set on the whole table), and use it to dispatch edition to the different editors.

In the following example, instead of setting the editors in cellProperties.type, I set it on cellProperties.theType, and call it just like Handsontable would do. That allows me to use basic Handsontable editors.

But I can also use a React editor if I want, for example here I’m using a KeyboardDatePicker.

import React from 'react';
import NativeListener from 'react-native-listener';
import { BaseEditorComponent } from '@handsontable/react';
import Handsontable from 'handsontable';
import { KeyboardDatePicker } from '@material-ui/pickers';
import ClickAwayListener from '@material-ui/core/ClickAwayListener';

export default class EditorComponent extends BaseEditorComponent {
  // Our already created editors
  editors = new Map();
  // Current 'normal' editor we are working on
  currentEditor;

  constructor(props) {
super(props);

this.mainElementRef = React.createRef();
this.containerStyle = {
  display: 'none',
  position: 'absolute',
  left: 0,
  top: 0,
  background: '#fff',
  // border: '1px solid #000',
  // padding: '15px',
  zIndex: 999,
};

// Trigger state for the reactEditor
this.state = {
  reactEditor: undefined,
  value: undefined,
};
  }
  discardEditor(result) {
if (this.currentEditor) {
  super.discardEditor(result);
} else {
  super.discardEditor(false);
}
console.log('discard editor', result);
  }

  setValue(value, callback) {
console.log(value);
if (this.currentEditor) {
  this.currentEditor.setValue(value);
} else {
  //this.cellProperties.instance.setDataAtCell(this.row, this.col, 42);
  this.setState((state, props) => {
    return { value };
  }, callback);
}
  }

  getValue() {
if (this.currentEditor) {
  return this.currentEditor.getValue();
}
return this.state.value;
  }


  open() {
if (this.currentEditor) {
  this.currentEditor.open();
} else {
  this.mainElementRef.current.style.display = 'block';
}
  }

  close() {
if (this.currentEditor) {
  this.currentEditor.close();
  this.currentEditor = undefined;
} else {
  this.setState((state, props) => {
    return { reactEditor: undefined };
  });
  this.mainElementRef.current.style.display = 'none';
}
  }

  onClose() {
super.discardEditor(true);
this.close();
  }

  prepare(row, col, prop, td, originalValue, cellProperties) {
// We'll need to call the `prepare` method from
// the `BaseEditorComponent` class, as it provides
// the component with the information needed to use the editor
// (hotInstance, row, col, prop, TD, originalValue, cellProperties)
super.prepare(row, col, prop, td, originalValue, cellProperties);
// Here I create a normal editor (not React)
if (row % 2 === 0) {
  const TheClass = new Handsontable.cellTypes.getCellType(cellProperties.theType).editor;
  if (!this.editors.has(TheClass)) {
    this.editors.set(TheClass, new TheClass(cellProperties.instance));
  }
  this.currentEditor = this.editors.get(TheClass);
  this.currentEditor.prepare(row, col, prop, td, originalValue, cellProperties);
} else {
  this.setState((state, props) => {
    return {
      reactEditor: <KeyboardDatePicker onChange={date => this.setValue(date)} onClose={() => this.onClose()} />,
    };
  });
  const tdPosition = td.getBoundingClientRect();

  // As the `prepare` method is triggered after selecting
  // any cell, we're updating the styles for the editor element,
  // so it shows up in the correct position.
  this.mainElementRef.current.style.left = tdPosition.left + 'px';
  this.mainElementRef.current.style.top = tdPosition.top + 'px';
}
  }

  stopMousedownPropagation(e) {
e.stopPropagation();
  }

  render() {
return (
  <NativeListener onMouseDown={this.stopMousedownPropagation}>
    <div style={this.containerStyle} ref={this.mainElementRef} id="editorElement">
      {this.state.reactEditor}
    </div>
  </NativeListener>
);
  }
}

Note that this specific case is not working because opening the calendar on MUI-Pickers cancels Hot edition and makes the whole editor disappear. I have not found a work-around for that.