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.
Copy and Paste Not working
/* 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