I’ve implemented a custom editor using the Handsontable.editors.BaseEditor. I’m able to copy and paste to another cell which is the standard text cell but I can’t seem to copy and paste to the cell of the same custom editor. It seems that the custom editor is not registering the paste into event.
/* eslint-disable no-param-reassign /
/ eslint-disable no-underscore-dangle /
/ eslint-disable class-methods-use-this */
import Handsontable from ‘handsontable’;
import { CONST_COL, CONST_ROW } from ‘…/…/constants/config’;
import { getIndexFromUUID } from ‘…/…/utility/table_functions’;
export default class TableRelationEditor extends Handsontable.editors.BaseEditor {
inputText: any = {};
filter: any = ‘’;
tableRef: any = {};
relationList: any = [];
currRow: any = 0;
currCol: any = 0;
state: any = { filter: ‘’ };
constructor(hotInstance, row, col, prop, TD, cellProperties) {
super(hotInstance, row, col, prop, TD, cellProperties);
this.tableRef = hotInstance.rootElement;
}
init() {
// super.init()
}
// When user selects a cell that use this editor
prepare(row, col, prop, td, originalValue, cellProperties) {
super.prepare(row, col, prop, td, originalValue, cellProperties);
this.inputText = originalValue;
this.currRow = row;
this.currCol = col;
this.relationList = cellProperties.relationList;
}
// When editor should be displayed
open() {
// Create Modal
const modal = document.createElement(‘div’);
modal.setAttribute(‘class’, ‘cell-modal z-20-imp’);
modal.innerHTML = ‘’;
// Modal Content
const modalContent = document.createElement('div');
modalContent.setAttribute('class', 'cell-modal-content rounded-2xl');
// Choose options container
const filterHeader = document.createElement('div');
const header = document.createElement('h4');
header.innerHTML = this.relationList[0].name || '';
const inputFilter = document.createElement('input');
inputFilter.type = 'text';
inputFilter.setAttribute('id', 'search-filter');
inputFilter.setAttribute('class', 'my-4 p-1 w-3/12 shadow-sm focus:ring-indigo-500 border focus:border-indigo-500 block sm:text-sm border border-gray-300 rounded-md');
inputFilter.setAttribute('placeholder', 'Search...');
filterHeader.appendChild(header);
filterHeader.appendChild(inputFilter);
const chooseFieldDom = document.createElement('div');
let optionsDom1 = '';
if (this.relationList.length > 0) {
optionsDom1 = '<div class="grid grid-cols-3 gap-2 search-list">';
const rows = this.relationList[0].firstCols;
const options = rows.map((row) => `<div class="col-span-1 cell-link"><input class="mr-2" type="checkbox" data-ident=${row.ident} id=${row.ident} ${this.inputText.includes(row.ident) ? 'checked' : ''} /><label for=${row.ident}>${row.value ? row.value : 'NULL'}</label></div>`);
optionsDom1 += `${options.join('')}</div>`;
}
chooseFieldDom.innerHTML = optionsDom1;
// Submit Button
const btnSubmit = document.createElement('button');
btnSubmit.setAttribute('class', 'ml-2 btn btn-primary px-3 py-1');
btnSubmit.innerHTML = 'Submit';
// Clear Button
const btnClear = document.createElement('button');
btnClear.setAttribute('class', 'btn btn-white px-3 py-1');
btnClear.innerHTML = 'Cancel';
const modalActions = document.createElement('div');
modalActions.setAttribute('class', 'mt-4 flex justify-end');
modalActions.appendChild(btnClear);
modalActions.appendChild(btnSubmit);
// Append
modalContent.appendChild(filterHeader);
modalContent.appendChild(chooseFieldDom);
modalContent.appendChild(modalActions);
modal.appendChild(modalContent);
modal.style.display = 'block';
btnSubmit.onclick = () => {
const inputs = [].filter.call(chooseFieldDom.getElementsByTagName('input'), (el) => el.checked).map((el) => el.dataset.ident);
this.inputText = inputs.length > 0 ? inputs : '';
this.hot.setDataAtCell(this.currRow, this.currCol, this.inputText);
modal.style.display = 'none';
};
btnClear.onclick = () => {
modal.style.display = 'none';
};
modal.onclick = (e) => { if (e.target === modal) { modal.style.display = 'none'; } };
this.tableRef.appendChild(modal);
const items = modal.querySelector('.search-list').getElementsByClassName('cell-link');
inputFilter.addEventListener('keyup', (el) => {
const textInput = el.target as HTMLInputElement;
const text = textInput.value;
const pat = new RegExp(text, 'i');
Array.from(items).forEach((item) => {
const checkbox = item as HTMLInputElement;
const { innerText } = checkbox;
if (pat.test(innerText)) {
checkbox.classList.remove('hidden');
} else {
checkbox.classList.add('hidden');
}
});
});
}
focus() {
}
close() {
}
getValue() {
return this.inputText;
}
setValue(value) {
this.inputText = value;
}
}
export function RelationRenderer(instance, td, row, col, prop, value, cellProperties) {
const documentFragment = document.createDocumentFragment();
const current = cellProperties.relationList[0];
if (value && !Array.isArray(value)) value = value.split(’,’);
if (Array.isArray(value)) {
value.forEach((link) => {
const block = document.createElement(‘div’);
block.setAttribute(‘class’, ‘rel-block’);
block.setAttribute(‘data-content’, link);
// const content = document.createElement(‘span’);
// content.setAttribute(‘class’, ‘dropdown-item’);
const hover = document.createElement(‘div’);
let hoverClass = 'rel-dropdown-content ';
const relCoord = cellProperties.openRelationCoord;
if (cellProperties.openRelation === link && relCoord.row === row && relCoord.col === col) {
hoverClass += ‘block’;
} else {
hoverClass += ‘hidden’;
}
hover.setAttribute(‘class’, hoverClass);
if (!current) return;
const linkRow = current.firstCols.filter((r) => r && Object.prototype.hasOwnProperty.call(r, 'ident') && r.ident === link)[0];
if (!linkRow) return;
block.innerHTML = linkRow.value || 'NULL';
const thValues = current.headers.map((header) => `<td class="visible-show cbg-gray-200 w-32">${header.name}</td>`);
const tdValues = current.headers.map((header) => {
const rowId = getIndexFromUUID(CONST_ROW, linkRow.ident.split('-')[1], current.rows);
const colId = getIndexFromUUID(CONST_COL, header._id, current.headers);
const cellVal = current.data[rowId][colId];
let val = '';
if (cellVal) {
if (Object.prototype.hasOwnProperty.call(cellVal, 'relation') && cellVal.relation.length > 0) {
val = '';
} else {
val = cellVal;
}
}
return `<td class="w-32">${val}</td>`;
});
const tableHeaders = thValues ? thValues.join('') : '';
const tableBody = tdValues ? tdValues.join('') : '';
const hoverContent = document.createElement('div');
hoverContent.setAttribute('class', 'w-full overflow-x-scroll');
hoverContent.innerHTML = `<table class="block overflow-x-auto table-fixed"><caption class="text-left mb-2 whitespace-nowrap"><b>${current.name}</b></caption><thead><tr>${tableHeaders}</tr></thead><tbody>${tableBody}</tbody></table>`;
hover.appendChild(hoverContent);
block.appendChild(hover);
Handsontable.dom.empty(td);
if (value) {
documentFragment.appendChild(block);
}
});
td.appendChild(documentFragment);
if (value.length === 0) {
td.innerHTML = '';
}
} else {
td.innerHTML = ‘’;
}
}
Hi @andrew3
we provide code reviews of custom implementations based on the scope of the support plan. Please send us your license ID (or license holder email) at support@handsontable.com