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
55 stars 6 forks source link

Column drag validation is inconsistent #10363

Open taauntik opened 6 days ago

taauntik commented 6 days ago

Video :

https://github.com/user-attachments/assets/cab0b17b-9177-44e8-8b3e-c62819e288df

Forum post

With 6.1.3, we found an issue with the identification of insertBefore on the columnDrag event. We have configured this example such that you should not be able to drag any columns to the left of the name (Room) column. We see the proper validation happen when you drag a column to the left, however as you slide the column back to the right, it becomes valid about 20-30 pixels before it should, still targeting before the name column where we had intended an invalid drag operation.

https://bryntum.com/products/schedulerpro/examples-scheduler/columns/

import { Column, ColumnStore, Combo, Scheduler, StringHelper } from '../../build/schedulerpro.module.js?481090';
import shared from '../_shared/shared.module.js?481090';
//region "lib/RoomThemeColumn.js"

class RoomThemeColumn extends Column {
    static $name    = 'RoomThemeColumn';
    static type     = 'roomthemecolumn';
    static defaults = {
        // Set your default instance config properties here
        field   : 'theme',
        text    : 'Theme',
        cellCls : 'b-room-theme-column-cell',
        editor  : { type : 'roomthemecombo' }
    };

//endregion

renderer({ column, value }) {
    const
        { store }  = column.editor,
        theme = store.getById(value);

    return theme ? [{
        tag       : 'i',
        className : theme.iconCls
    }, theme.text] : '';
}
}

ColumnStore.registerColumnType(RoomThemeColumn);

//endregion

//region "lib/RoomThemeCombo.js"

class RoomThemeCombo extends Combo {
    static type          = 'roomthemecombo';
    static defaultConfig = {
        items : [
            { value : 1, text : 'Beachfront Bliss', iconCls : 'b-fa b-fa-umbrella-beach' },
            { value : 2, text : 'Cityscape Retreat', iconCls : 'b-fa b-fa-city' },
            { value : 3, text : 'Jungle Hideaway', iconCls : 'b-fa b-fa-leaf' },
            { value : 4, text : 'Artistic Haven', iconCls : 'b-fa b-fa-paint-brush' },
            { value : 5, text : 'Alpine Getaway', iconCls : 'b-fa b-fa-snowflake' }
        ],
        picker : {
            minWidth : '11em'
        },
        listItemTpl : ({ text, iconCls }) => `
            <div>
                <i style="margin-inline-end: 0.5em" class="${iconCls}"></i>
                <small>${text}</small>
            </div>
        `
    };

syncInputFieldValue(...args) {
    const theme = this.store.getById(this.value);
    this.icon.className = `${theme?.iconCls}`;
    super.syncInputFieldValue(...args);
}

get innerElements() {
    return [
        {
            reference : 'icon',
            tag       : 'i',
            style     : {
                marginInlineStart : '.8em',
                marginInlineEnd   : '-.3em'
            }
        },
        ...super.innerElements
    ];
}
}

// Register class to be able to create widget by type
RoomThemeCombo.initClass();

//endregion

const scheduler = new Scheduler({
    appendTo   : 'container',
    startDate  : new Date(2019, 1, 19, 6),
    endDate    : new Date(2019, 1, 19, 20),
    viewPreset : 'hourAndDay',
    rowHeight  : 50,
    barMargin  : 5,
    eventStyle : 'colored',

listeners: {
    columnDrag: (event) => {
    console.log(event.insertBefore?.field);
    if (event.insertBefore?.field === 'name') {
      event.context.valid = false;
    }
  },
},

crudManager : {
    autoLoad : true,

    // This config enables response validation and dumping of found errors to the browser console.
    // It's meant to be used as a development stage helper only so please set it to false for production systems.
    validateResponse : true,

    resourceStore : {
        // Additional fields for resources
        fields : [
            'capacity',
            'condition',
            'color',
            'floor',
            { name : 'theme', type : 'number' },
            { name : 'redecorated', type : 'date' }
        ]
    },

    transport : {
        load : {
            url : 'data/data.json'
        }
    }
},

columns : [
    {
        text       : 'Room',
        field      : 'name',
        width      : 130,
        region     : 'left',
        htmlEncode : false,
        draggable   : false,
        renderer   : ({ value, record }) => StringHelper.xss`<div class="box b-sch-${record.color}"></div>${value}`
    },
    { text : 'Floor', field : 'floor', width : 100, region : 'left' },
    {
        text       : 'Capacity',
        field      : 'capacity',
        width      : 80,
        region     : 'left',
        type       : 'number',
        align      : 'right',
        htmlEncode : false,
        renderer({ value }) {
            const icon = value < 25 ? 'user' : value < 200 ? 'user-friends' : 'users';
            return StringHelper.xss`${value}<div class="capacity b-fa b-fa-${icon}"></div>`;
        }
    },
    { type : 'roomthemecolumn', region : 'left', minWidth : '13em' },
    { text : 'Redecorated', field : 'redecorated', width : 115, region : 'right', type : 'date' },
    { text : 'Condition', field : 'condition', region : 'right', type : 'rating' }
],

eventRenderer({ eventRecord, renderData }) {
    const hours = eventRecord.duration * 24;
    if (hours > 8) {
        renderData.eventColor = 'red';
    }
    else if (hours > 4) {
        renderData.eventColor = 'orange';
    }
    else if (hours > 2) {
        renderData.eventColor = 'lime';
    }

    return StringHelper.xss`${eventRecord.name}<span>(${hours} hour${hours > 1 ? 's' : ''})</span>`;
},

features : {
    columnDragToolbar: false,
    columnReorder : {
        //stretchedDragProxy : true
    },
    eventEdit : {
        items : {
            resourceField : {
                label : 'Room'
            }
        }
    }
},
});