React class-based wrapper component for Handsontable data grid with spreadsheet-like functionality
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
The BaseEditorComponent class provides a foundation for creating React-based custom cell editors that integrate seamlessly with Handsontable's editing lifecycle. It implements the full Handsontable editor interface while allowing React component patterns.
Base class for creating custom React-based cell editors with full Handsontable editor lifecycle integration.
/**
* Base class for React-based Handsontable editors
* Implements the complete BaseEditor interface with React component lifecycle
*/
class BaseEditorComponent<P = {}, S = {}, SS = any>
extends React.Component<P & BaseEditorProps, S, SS>
implements Handsontable.editors.BaseEditor;
interface BaseEditorProps extends HotEditorProps {
/** Editor scope identifier - column index or 'global' */
editorColumnScope?: EditorScopeIdentifier;
/** Callback to emit editor instance to parent */
emitEditorInstance?: (editor: BaseEditorComponent, column: EditorScopeIdentifier) => void;
}
interface HotEditorProps {
/** Editor marker property */
"hot-editor": any;
/** Editor DOM element ID */
id?: string;
/** CSS class name */
className?: string;
/** Inline styles */
style?: React.CSSProperties;
}
type EditorScopeIdentifier = 'global' | number;Usage Examples:
import React, { Component } from 'react';
import { BaseEditorComponent } from '@handsontable/react';
// Simple text editor with validation
class CustomTextEditor extends BaseEditorComponent {
constructor(props) {
super(props);
this.state = {
value: ''
};
}
componentDidMount() {
super.componentDidMount();
this.setState({ value: this.originalValue || '' });
}
handleChange = (event) => {
this.setState({ value: event.target.value });
};
handleKeyDown = (event) => {
if (event.key === 'Enter') {
this.finishEditing();
} else if (event.key === 'Escape') {
this.cancelChanges();
}
};
getValue() {
return this.state.value;
}
setValue(value) {
this.setState({ value: value || '' });
}
render() {
return (
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
onKeyDown={this.handleKeyDown}
autoFocus
style={{ width: '100%', border: 'none', outline: 'none' }}
/>
);
}
}
// Dropdown editor with custom options
class CustomDropdownEditor extends BaseEditorComponent {
constructor(props) {
super(props);
this.state = {
value: '',
options: ['Option 1', 'Option 2', 'Option 3']
};
}
componentDidMount() {
super.componentDidMount();
this.setState({ value: this.originalValue || '' });
}
handleChange = (event) => {
this.setState({ value: event.target.value });
this.finishEditing();
};
getValue() {
return this.state.value;
}
setValue(value) {
this.setState({ value: value || '' });
}
render() {
return (
<select
value={this.state.value}
onChange={this.handleChange}
autoFocus
style={{ width: '100%', border: 'none' }}
>
<option value="">Select...</option>
{this.state.options.map(option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
);
}
}
// Usage in HotTable
function TableWithCustomEditor() {
return (
<HotTable
data={[['Sample', 'Data']]}
colHeaders={true}
licenseKey="non-commercial-and-evaluation"
>
<HotColumn data={0}>
<CustomTextEditor hot-editor />
</HotColumn>
<HotColumn data={1}>
<CustomDropdownEditor hot-editor />
</HotColumn>
</HotTable>
);
}Properties available on BaseEditorComponent instances during the editing lifecycle:
/** Handsontable instance reference */
hotInstance: Handsontable | null;
/** Current row index being edited */
row: number | null;
/** Current column index being edited */
col: number | null;
/** Property name or column identifier */
prop: string | number | null;
/** Cell DOM element being edited */
TD: HTMLTableCellElement | null;
/** Original cell value before editing */
originalValue: any;
/** Cell properties object with metadata */
cellProperties: Handsontable.CellProperties | null;
/** Editor state (inherited from BaseEditor) */
state: any;
/** Reference to custom editor instance wrapper */
hotCustomEditorInstance: any;Methods inherited from Handsontable.editors.BaseEditor that handle the editing lifecycle:
/**
* Prepare the editor for editing a specific cell
* @param row - Row index
* @param col - Column index
* @param prop - Property name
* @param TD - Cell DOM element
* @param originalValue - Current cell value
* @param cellProperties - Cell metadata
*/
prepare(
row: number,
col: number,
prop: string | number,
TD: HTMLTableCellElement,
originalValue: any,
cellProperties: Handsontable.CellProperties
): any;
/**
* Initialize the editor (called once)
*/
init(...args: any[]): any;
/**
* Open the editor for editing
*/
open(...args: any[]): any;
/**
* Close the editor
*/
close(...args: any[]): any;
/**
* Focus the editor input
*/
focus(...args: any[]): any;
/**
* Get the current editor value
* @returns Current editor value
*/
getValue(...args: any[]): any;
/**
* Set the editor value
* @param value - Value to set
*/
setValue(...args: any[]): any;
/**
* Begin the editing process
*/
beginEditing(...args: any[]): any;
/**
* Finish editing and save the value
*/
finishEditing(...args: any[]): any;
/**
* Cancel editing and revert changes
*/
cancelChanges(...args: any[]): any;
/**
* Save the current editor value to the cell
*/
saveValue(...args: any[]): any;
/**
* Discard the editor without saving
*/
discardEditor(...args: any[]): any;
/**
* Enable full edit mode
*/
enableFullEditMode(...args: any[]): any;
/**
* Check if editor is in full edit mode
* @returns True if in full edit mode
*/
isInFullEditMode(...args: any[]): boolean;
/**
* Check if editor is open
* @returns True if editor is open
*/
isOpened(...args: any[]): boolean;
/**
* Check if editor is waiting
* @returns True if editor is waiting
*/
isWaiting(...args: any[]): boolean;Methods for managing Handsontable hooks within custom editors:
/**
* Add a hook callback
* @param key - Hook name
* @param callback - Hook callback function
*/
addHook(...args: any[]): any;
/**
* Remove hooks by key
* @param key - Hook name to remove
*/
removeHooksByKey(...args: any[]): any;
/**
* Clear all hooks
*/
clearHooks(...args: any[]): any;Methods for getting information about the cell being edited:
/**
* Get the edited cell element
* @returns Cell DOM element
*/
getEditedCell(...args: any[]): HTMLTableCellElement;
/**
* Get the edited cell rectangle
* @returns Cell bounding rectangle
*/
getEditedCellRect(...args: any[]): DOMRect;
/**
* Get the edited cell's z-index
* @returns Z-index value
*/
getEditedCellsZIndex(...args: any[]): number;To use custom editors, they must be provided as children of HotColumn or HotTable components:
// Column-specific editor
<HotColumn data="description">
<CustomEditor hot-editor />
</HotColumn>
// Global editor for entire table
<HotTable data={data}>
<CustomEditor hot-editor />
<HotColumn data={0} />
<HotColumn data={1} />
</HotTable>The hot-editor prop is required to identify React components as editor components. The component will be automatically integrated into Handsontable's editor system.
Editors can be scoped to specific columns or applied globally:
The editorColumnScope prop identifies the scope and is managed automatically by the parent components.