[REACT] Context and shortcut don't do the same thing

Tags: #<Tag:0x00007efc6507cc78>

Hi Handsontable team,

I have a problem with my context menu in handsontable. This is my code for cutting some rows:

   shortCutCut = () => {
    const range = this.hotTableComponent.current.hotInstance.getSelectedRange()[0]
    this.hotTableComponent.current.hotInstance.selectCells([range])
    window.document.execCommand('copy')
    const amount = (range.to.row - range.from.row) + 1


    this.hotTableComponent.current.hotInstance.alter('remove_row', range.from.row, amount)
    this.hotTableComponent.current.hotInstance.render()
    this.sumCols()

}

When I use a short-cut ctrl + shift + x it works perfectly (custom listener). However when I use the context menu it does not copy the data properly. In fact it copies nothing. So I taught it must be because nothing is selected once you press on the context menu but that is not it since I made sure it selects those rows again before copying. Is there something I am not doing correctly? The same exact code runs in both cases.

"Cut": {
                        name: 'Rijen knippen (CTRL + shift + x)',
                        callback: () => {
                            this.shortCutCut()
                        }
                    },

    // Cut control + shift + x
    if (event.ctrlKey === true && event.shiftKey === true && event.keyCode === 88) {
        event.preventDefault();
        this.shortCutCut()
    }

Any help would be appreciated otherwise I will just remove it from the context menu

Hi @omerkati

Can you please send also your configuration object? That would be helpful. We’ll look into it and get back to you after the weekend.

It has two parts because the second half can be updated based on the configuration and I removed the license key.

{
        // After control+shift+f is pressed the search tool will open and other shortcuts
        afterDocumentKeyDown: (event) => {
            clearTimeout(this.timeout);
            this.timeout = setTimeout(this.shortCutHandler(event), 2000);
        },
        // This prevents delete key from working and allows us to use a new method when del is pressed
        beforeKeyDown: (event) => {
            clearTimeout(this.timeout);
            if (event.key === 'Delete') this.timeout = setTimeout(event.stopImmediatePropagation(), 2000);

        },
        // After a cell is pressed the row is saved to the state
        afterOnCellMouseDown: (event, coords) => {
            clearTimeout(this.timeout);
            this.timeout = setTimeout(this.toolSelectCurrentRow(coords), 2000);
        },
        // After a cell is changed this will run to control that change
        afterChange: (event, src) => {
            clearTimeout(this.timeout);
            this.timeout = setTimeout(this.afterCellChangeHandler(event, src), 2000);
        },
        // Before copying this takes the data and makes it more appropriate for use in handsOnTable
        beforeCopy: (data, coords) => {
            navigator.clipboard.writeText(this.beforeCopyHandler(data, coords));
        },
        // This code runs before paste is applied, it pastes the saved data from before copy
        beforePaste: (data, coords) => {
            clearTimeout(this.timeout);
            if (IsJsonString(data)) {
                this.timeout = setTimeout(this.beforePasteHandler(data, coords), 2000);
            } else {
                return true;
            }
            return false;
        },
        // Allows for the copy paste plugin to work
        copyPaste: true,
        // Custom context menu (right-click)
        contextMenu: {
            callback: function (key, selection, clickEvent) {
                // Common callback for all options
            },
            items: {
                // Standard actions
                "copy": {},
                "row_above": {},
                "row_below": {},

                // seperator
                "sp1": '---------',

                // Custom actions
                "PasteAbove": {
                    name: 'Boven invoegen (CTRL + shift + v)',
                    callback: () => {
                        this.shortCutInsertAbove()
                    }
                },
                // "Cut": {
                //     name: 'Rijen knippen (CTRL + shift + x)',
                //     callback: () => {
                //         this.shortCutCut()
                //     }
                // },
                "Delete": { // Own custom option
                    name: 'Rijen verwijderen (CTRL + shift + d)',
                    callback: () => {
                        this.shortCutDelete()
                    }
                },
                "Underline": {
                    name: 'Onderstreept (CTRL + u)',
                    callback: () => {
                        this.shortCutUnderline()
                    }
                },
                "Bold": {
                    name: 'Dik gedrukt (CTRL + b)',
                    callback: () => {
                        this.shortCutBold()
                    }
                },


            }
        },
        // Sets the culture to dutch for euro symbols
        culture: "nl-NL",
        // Allows for the use of filters
        filters: true,
        // Formulas are allowed and is using the better HyperFormula engine
        formulas: {
            engine: HyperFormula
        },
        height: 850,
        // Sets the language the dutch
        language: "nl-NL",
        // Shows row numbers
        rowHeaders: true,
        width: "100%",
        // The amount of rows which are rendered offscreen
        viewportRowRenderingOffset: 50,
    }
{
colWidths: [190, 50, 200, 270, 90, 100, 100, 100, 30, 30, 30, 30, 30, 30, 30, 30, 45, 200],
className: "",

colHeaders: [
    "Sectie",
    "#",
    "Artikelnummer",
    "Omschrijving",
    "Netto/st",
    "Netto",
    "Bruto/st",
    "Bruto",
    "DI",
    "AI",
    "DO",
    "AO",
    "DIs",
    "AIs",
    "DOs",
    "AOs",
    "DP",
    "Toelichting",
],
columns: [
    {
        //Sectie

        type: "text",
        fontWeight: "bold",
        className: "cell-underline",
    },
    {
        //aantal
        formulas: true,
        type: "numeric",
    },
    {
        //artikelnummer
    },
    {
        //Omschrijving
    },

    {
        //Netto/st
        type: "numeric",
        renderer: euroRendererDecimal,
    },
    {
        //Netto
        formulas: true,
        readOnly: true,
        type: "numeric",
        renderer: euroRendererDecimal,
        className: "cell-bg-lightgray"

    },
    {
        //Bruto Prijs stuk(Euro)
        formulas: true,
        type: "numeric",

        renderer: euroRendererDecimal,

    },
    {
        //Bruto Prijs totaal(Euro)
        formulas: true,
        readOnly: true,

        type: "numeric",
        renderer: euroRendererDecimal,
        className: "cell-bg-lightgray"


    },
    {
        //DI
        formulas: true,
        className: "cell-bg-darkgray"

    },
    {
        //AI
        formulas: true,
        className: "cell-bg-darkgray"

    },
    {
        //DO
        formulas: true,
        className: "cell-bg-darkgray"

    },
    {
        //AO
        formulas: true,
        className: "cell-bg-darkgray"

    },
    {
        //DIs
        formulas: true,
        readOnly: true,
        className: "cell-bg-lightgray",

    },
    {
        //AIs
        formulas: true,
        readOnly: true,
        className: "cell-bg-lightgray"

    },
    {
        //DOs
        formulas: true,
        readOnly: true,
        className: "cell-bg-lightgray"

    },
    {
        //AOs
        formulas: true,
        readOnly: true,
        className: "cell-bg-lightgray"

    },
    {
        //Datapunten
        formulas: true,
    },
    {
        //toelichting
    },
],

};

Hi @omerkati

It seems that the behaviour from contextMenu is correct as it’s not allowed pasting data from system clipboard due to security reasons. You can read about it in our documentation: https://handsontable.com/docs/basic-clipboard/#context-menu-2

    shortCutInsertAbove = () => {
    const selectedRow = this.hotTableComponent.current.hotInstance.getSelectedRange()[0].from.row
    let amountRows

    navigator.clipboard.readText().then(data => {
        data = JSON.parse(data)
        amountRows = data.coords.endRow - data.coords.startRow + 1
        this.hotTableComponent.current.hotInstance.alter('insert_row', selectedRow, amountRows)
        this.beforePasteHandler(JSON.stringify(data), selectedRow)
    })

}

This works for me it is also in my contextMenu. And it is not about reading but about writing to the clipboard. The browser asks for permission first and then it works. So that does not explain this behavior right?

Hi @omerkati

I was also checking this subject. Could you please share a demo with your logic? We generally require the support plan to be confirmed before doing a review of custom implementations. But let’s check how it works first and then we can decide what’s next.