bryntum / support

An issues-only repository for the Bryntum project management component suite which includes powerful Grid, Scheduler, Calendar, Kanban Task Board and Gantt chart components all built in pure JS / CSS / TypeScript
https://www.bryntum.com
53 stars 6 forks source link

Grid Menu not opening and flickering #8355

Closed marciogurka closed 7 months ago

marciogurka commented 8 months ago

Forum post

"Hi

I have implemented a custom menu column along with the menu event listeners. I had posted some queries regarding this in the forum viewtopic.php?t=27647 which were resolved. However, there is one issue. On the page load, if we open the actions menu for different rows in the grid a few times, the menu stops showing for those rows. For the first 5 to 6 times we open the actions menu, it shows fine. After that, sometimes it flickers i.e. shows for less than a second and then disappears or does not show up at all. This is happening on the latest bryntum grid demo as well. I have provided a basic example which can be tested here https://bryntum.com/products/grid/examples/celledit/"

import { Widget, Menu, EventHelper, Grid, DataGenerator, DateHelper } from '../../build/grid.module.js?473556';
import shared from '../_shared/shared.module.js?473556';

const menu = new Menu({
        anchor   : false,
        autoShow : false,
        align    : 't-b',
        items    : [
            {
                icon     : 'fa-thin fa-info',
                cls      : 'viewAssetDetails',
                                html : `<a>View Details<\a>`,
                onItem({source, item}) {
                                        debugger;
                    var gridObj = source.up('grid'),
                        element = source.parent.owner.element.closest('.b-grid-row'),
                        asset = gridObj.getRecordFromElement(element);
                }
            }]
});
// YesNo is a custom button that toggles between Yes and No on click
class YesNo extends Widget {

static get $name() {
    return 'YesNo';
}

// Factoryable type name
static get type() {
    return 'yesno';
}

// Hook up a click listener during construction
construct(config) {
    // Need to pass config to super (Widget) to have things set up properly
    super.construct(config);

    // Handle click on the element
    EventHelper.on({
        element : this.element,
        click   : 'onClick',
        thisObj : this
    });
}

// Always valid, this getter is required by CellEdit feature
get isValid() {
    return true;
}

// Get current value
get value() {
    return Boolean(this._value);
}

// Set current value, updating style
set value(value) {
    this._value = value;

    this.syncInputFieldValue();
}

// Required by CellEdit feature to update display value on language locale change
// Translation is added to examples/_shared/locales/*
syncInputFieldValue() {
    const
        {
            element,
            value
        } = this;

    if (element) {
        element.classList[value ? 'add' : 'remove']('yes');
        element.innerText = value ? this.L('L{Object.Yes}') : this.L('L{Object.No}');
    }
}

// Html for this widget
template() {
    return `<button class="yesno"></button>`;
}

// Click handler
onClick() {
    this.value = !this.value;
}
}

// Register this widget type with its Factory
YesNo.initClass();

let newPlayerCount = 0;

const grid = new Grid({

appendTo : 'container',

features : {
    cellEdit : true,
    sort     : 'name',
    stripe   : true
},

// Show changed cells
showDirty : true,

async validateStartDateEdit({ grid, value }) {
    if (value > DateHelper.clearTime(new Date())) {
        return grid.features.cellEdit.confirm({
            title   : 'Selected date in future',
            message : 'Update field?'
        });
    }
    return true;
},

columns : [
    { text : 'Name', field : 'name', flex : 1 },
    {
        text   : 'Birthplace',
        field  : 'city',
        width  : '8em',
        editor : { type : 'dropdown', items : DataGenerator.cities }
    },
    { text : 'Team', field : 'team', flex : 1 },
    { text : 'Score', field : 'score', editor : 'number', width : '5em' },
    {
        text             : 'Start',
        id               : 'start',
        type             : 'date',
        field            : 'start',
        width            : '9em',
        finalizeCellEdit : 'up.validateStartDateEdit'
    },
    { text : 'Finish (readonly)', type : 'date', field : 'finish', width : '9em', editor : false },
    { text : 'Time', id : 'time', type : 'time', field : 'time', width : '10em' },
    // Column using the custom widget defined above as its editor
    {
        text     : 'Custom', // `text` gets localized automatically, is added to examples/_shared/locales/*
        field    : 'done',
        editor   : 'yesno',
        width    : '5em',
        renderer : ({ value }) => value ? YesNo.L('L{Object.Yes}') : YesNo.L('L{Object.No}')
    },
    { type : 'percent', text : 'Percent', field : 'percent', flex : 1 },
    {
            type: 'widget',
            text: '',
            minWidth: 30,
            maxWidth: 50,
            htmlEncode: false,
            cls: 'text-center hidden-xs',
            cellCls: 'hidden-xs',
            hidden: false, // Set to false to make the column initially visible
            hideable: false, // Set to false to prevent the user from hiding the column
            widgets : [
                {
                    type: 'button',
                    flex : 1,
                    menu,
                }
            ]
        }
],

data : DataGenerator.generateData(50),

listeners : {
    selectionChange({ selection }) {
        removeButton.disabled = !selection.length || grid.readOnly;
    }
},

tbar : [
    {
        type        : 'button',
        ref         : 'readOnlyButton',
        text        : 'Read-only',
        tooltip     : 'Toggles read-only mode on grid',
        toggleable  : true,
        icon        : 'b-fa-square',
        pressedIcon : 'b-fa-check-square',
        onToggle    : ({ pressed }) => {
            addButton.disabled = insertButton.disabled = grid.readOnly = pressed;

            removeButton.disabled = pressed || !grid.selectedRecords.length;
        }
    },
    {
        type  : 'buttongroup',
        items : [
            {
                type     : 'button',
                ref      : 'addButton',
                icon     : 'b-fa-plus-circle',
                text     : 'Add',
                tooltip  : 'Adds a new row (at bottom)',
                onAction : () => {
                    const
                        counter = ++newPlayerCount,
                        added   = grid.store.add({
                            name : `New player ${counter}`,
                            cls  : `new_player_${counter}`
                        });

                    grid.selectedRecord = added[0];
                }
            },
            {
                type     : 'button',
                ref      : 'insertButton',
                icon     : 'b-fa-plus-square',
                text     : 'Insert',
                tooltip  : 'Inserts a new row (at top)',
                onAction : () => {
                    const
                        counter = ++newPlayerCount,
                        added   = grid.store.insert(0, {
                            name : `New player ${counter}`,
                            cls  : `new_player_${counter}`
                        });

                    grid.selectedRecord = added[0];
                }
            }
        ]
    },
    {
        type     : 'button',
        ref      : 'removeButton',
        color    : 'b-red',
        icon     : 'b-fa b-fa-trash',
        text     : 'Remove',
        tooltip  : 'Removes selected record(s)',
        disabled : true,
        onAction : () => {
            const selected = grid.selectedRecords;

            if (selected && selected.length) {
                const
                    store      = grid.store,
                    nextRecord = store.getNext(selected[selected.length - 1]),
                    prevRecord = store.getPrev(selected[0]);

                store.remove(selected);
                grid.selectedRecord = nextRecord || prevRecord;
            }
        }
    }
]
});

const { addButton, removeButton, insertButton } = grid.widgetMap;

// Show the dirty marker
grid.store.getAt(0).score = 200;

https://github.com/bryntum/support/assets/16693227/ca8d437a-f2b6-45c6-8371-21996f40d88b

taauntik commented 7 months ago

duplicate https://github.com/bryntum/support/issues/8062 And fixed ^