Simple keyboard steals focus of editor

This issue has been tracked since 2021-09-22.

I want to use Simple Keyboard together with DevExtreme's dxDataGrid in cell edit mode. The issue is, that Simple Keyboard is stealing the focus of the editor control after each key stroke. The dxDataGrid control, however, destroys the editor as soon as the focus gets lost, and stores the modified value to the server. Is there a simple way to prevent that loss of focus?

Here is the statement of DevExtreme support:

I tested the Keyboard component on their demo page and noticed that every button press steals focus from the target input element. DataGrid in-place editors post their values and DataGrid closes them when they lose focus. This is what happens when you use this Keyboard.
It is necessary [...] to customize this Keyboard to not steal focus from input elements [...].

How can I prevent the keyboard from stealing the focus of the text editor element?

Here's my keyboard preparation code:

editorPrepared(options) {
    if (!this.isRaspy) {
        return;
    }

    const isSearchPanel = options.parentType === 'searchPanel';
    const isTextBox = options.editorName === 'dxTextBox';
    const isNumberBox = options.editorName === 'dxNumberBox';
    const isRelevantEditorType = isTextBox || isNumberBox;
    const isRelevantFilterRow = isRelevantEditorType && options.parentType === 'filterRow';
    const isUserEditableField = isRelevantEditorType && options.parentType === 'dataRow';

    // Don't show on screen keyboard, if not required
    if (!isSearchPanel && !isRelevantFilterRow && !isUserEditableField) {
        return;
    }

    // Prepare keyboard
    const elem = document.createElement('div');
    elem.className = 'simple-keyboard';

    let keyboardLayout;
    let keyboardLayoutClass;
    let inputControl;

    if (isNumberBox) {
        keyboardLayout = KeyboardNumpad;
        keyboardLayoutClass = 'hg-layout-numeric';
        inputControl = NumberBox;
        elem.classList.add('numeric-layout');
    }
    else if (isTextBox) {
        keyboardLayout = KeyboardLayout;
        keyboardLayoutClass = 'hg-layout-default';
        inputControl = TextBox;
    }
    else {
        throw 'Invalid editor:', options;
    }

    // Get editor
    const editor = inputControl.getInstance(options.editorElement);

    // Prepare keyboard
    let isLocked = false;
    let isShifted = false;

    const keyboard = new Keyboard(elem, {
        onChange: input => {
            editor.option({ value: input });
        },
        onKeyPress: key => {
            let updateLayout = false;

            switch (key) {
                case '{enter}':
                    if (isUserEditableField) {
                        keyboard.destroy();
                        elem.remove();
                        popover.dispose();
                    }
                    else {
                        popover.hide();
                    }

                    return;
                case '{lock}':
                    isLocked = !isLocked;
                    updateLayout = true;
                    break;
                case '{shift}':
                    isShifted = !isShifted;
                    updateLayout = true;
                    break;
                default:
                    if (isShifted) {
                        isShifted = false;
                        updateLayout = true;
                    }
            }

            if (updateLayout) {
                keyboard.setOptions({
                    layoutName: isLocked & !isShifted || !isLocked & isShifted ? 'shift' : 'default'
                });
            }
        },
        theme: `hg-theme-default dplTheme ${keyboardLayoutClass}`,
        display: {
            '{bksp}': '← Löschen',
            '{enter}': '↵ Enter',
            '{shift}': '⇧ Umschalt',
            '{tab}': '↹ Tab',
            '{lock}': '⇩ Feststellen',
            '{space}': 'Leertaste',
        },
        ...keyboardLayout,
    });

    keyboard.setInput(options.value);

    // Prepare popover
    const popover = new Popover(options.editorElement.parentElement, {
        trigger: 'manual',
        content: elem,
        html: true,
        container: this.$refs['mainContainer'],
    });

    if (isUserEditableField) {
        popover.show();
    }
    else {
        editor.option({
            onFocusIn: () => popover.show(),
        });
    }

},
<DxDataGrid :allowColumnReordering="true"
            :allowColumnResizing="true"
            :columnAutoWidth="true"
            :columns="clientSettings.Columns"
            :dataSource="gridDataSource"
            :highlight-changes="true"
            :repaint-changes-only="true"
            :rowAlternationEnabled="true"
            :showBorders="true"
            :summary="clientSettings.summaries"
            @editorPrepared="editorPrepared"
            height="100%"
            id="prod-table"
            ref="dataGridProdTable">

    <DxGroupPanel :visible="true" />

    <DxScrolling mode="virtual" />

    <DxSorting mode="multiple" />

    <DxEditing :allow-updating="true"
               mode="cell"
               refreshMode="reshape" />

    <DxFilterRow :visible="true" />

    <DxSearchPanel :visible="true" />

    <template #dataEntryNumber="{ data }">
        <a href="#" @click="openWorkerCockpit(data)">{{data.value}}</a>
    </template>

    <template #jobDetails="{ data }">
        <a href="#" @click="openJobDetails(data)">{{data.value}}</a>
    </template>

</DxDataGrid>
AnReZa wrote this answer on 2021-09-22

By the way, adding preventMouseDownDefault: true didn't help.

hodgef wrote this answer on 2021-09-22

Hello @AnReZa,

The demo page does not have preventMouseDownDefault enabled. If the dev was expecting constant input focus on the demo page, it was not going to work...

Here's a basic sandbox: https://codesandbox.io/s/flamboyant-microservice-ekfvp?file=/src/index.js

This sandbox shows that when you enable preventMouseDownDefault, simple-keyboard does not take focus away from the input. This is why I cannot repro your issue.

If you can please provide a reproducible (runnable/complete) example showing your issue happening I will be glad to take a look. That can be in the form of a sandbox link or a test git repository, I just need to be able to run it and see the error you're describing.

Thank you,
Francisco Hodge

AnReZa wrote this answer on 2021-09-23

Hello @hodgef,

I already found a solution to the problem. It is vital, that the popover which contains the keyboard (in my case) is attached to the editor. If I attach it to the main container, it will steal the focus of DevExtreme's grid control, even though I set the preventMouseDownDefault option to true.

// Prepare popover
const popover = new Popover(options.editorElement.parentElement, {
    trigger: 'manual',
    placement: 'auto',
    content: elem,
    html: true,
    container: isUserEditableField ? options.editorElement : this.$refs['mainContainer'], //in case of a user-editable field, attach it to the editor
});

Thank you!

More Details About Repo
Owner Name hodgef
Repo Name simple-keyboard
Full Name hodgef/simple-keyboard
Language JavaScript
Created Date 2018-03-02
Updated Date 2022-12-06
Star Count 1738
Watcher Count 20
Fork Count 130
Issue Count 0

YOU MAY BE INTERESTED

Issue Title Created Date Updated Date