Using the Drag and Drop Equipment Demo as an example, is there a way to drag an object onto the Resource and have the object be assigned to all of the events assigned to the resource? In the demo you need to drag a piece of equipment onto each event.
I tweaked it just a tiny bit using the documented API to do that.
You can paste this into the code editor to make it do the same. It was just a case of a tiny little tweak to (predictably) onDrop, and changing the dropTargetSelector to allow to drop on the grid cell in addition to dropping on tasks.
import { DragHelper, Toast, Grid, StringHelper, Scheduler, DateHelper, EventModel, AjaxStore } from '../../build/scheduler.module.js?478494';
import shared from '../_shared/shared.module.js?478494';
class Drag extends DragHelper {
static get configurable() {
return {
callOnFunctions : true,
// Don't drag the actual cell element, clone it
cloneTarget : true,
// We size the cloned element using CSS
autoSizeClonedTarget : false,
// Allow drops on scheduled tasks or just the cell
dropTargetSelector : '.b-timeaxis-cell, .b-sch-event',
// Drag only the icon inside the cell
proxySelector : 'i',
// Only allow dragging cell elements inside on the equipment grid
targetSelector : '.b-grid-row:not(.b-group-row) .b-grid-cell'
};
}
// Listening to events using the onXXX notation, similar to this.on('dragStart', () => {})
onDragStart({ event, context }) {
// save a reference to the equipment so we can access it later
context.equipment = this.grid.getRecordFromElement(context.grabbed);
// Prevent tooltips from showing while dragging
this.schedule.features.eventTooltip.disabled = true;
}
// In the onDrop, we instruct the drag helper to transition the drag proxy element to an approximate destination
// before updating the event record (done in onDropFinalized)
async onDrop({ context }) {
if (context.valid) {
const
{ target } = context,
equipmentItem = context.equipment,
resourceRecord = this.schedule.resolveResourceRecord(target);
resourceRecord.events.forEach(eventRecord => {
if (!eventRecord.equipment.includes(equipmentItem.id)) {
eventRecord.equipment = eventRecord.equipment.concat(equipmentItem.id);
}
});
Toast.show(`Added ${equipmentItem.name} to events for ${resourceRecord.name}`);
}
}
};
class EquipmentGrid extends Grid {
/**
* Original class name getter. See Widget.$name docs for the details.
* @returns {string}
*/
static get $name() {
return 'EquipmentGrid';
}
// Factoryable type name
static get type() {
return 'equipmentgrid';
}
static get configurable() {
return {
disableGridRowModelWarning : true,
features : {
filterBar : true,
cellEdit : false
},
rowHeight : 100,
columns : [{
text : '',
field : 'name',
htmlEncode : false,
cellCls : 'b-equipment',
renderer : data => StringHelper.xss`<i class="b-equipment-icon ${data.record.iconCls}"></i>${data.record.name}`
}]
};
}
};
// Register this widget type with its Factory
EquipmentGrid.initClass();
class Schedule extends Scheduler {
/**
* Original class name getter. See Widget.$name docs for the details.
* @returns {String}
*/
static get $name() {
return 'Schedule';
}
// Factoryable type name
static get type() {
return 'schedule';
}
static get configurable() {
return {
rowHeight : 100,
barMargin : 4,
eventColor : 'indigo',
eventStyle : 'colored',
resourceImagePath : '../_shared/images/users/',
features : {
eventMenu : {
items : [
// custom item with inline handler
{
text : 'Remove all equipment',
icon : 'b-fa b-fa-times',
weight : 200,
onItem : ({ eventRecord }) => eventRecord.equipment = []
}
]
},
eventEdit : {
// Add an extra combo box to the editor to select equipment
items : {
equipment : {
type : 'combo',
weight : 900, // At end
editable : false,
multiSelect : true,
valueField : 'id',
displayField : 'name',
ref : 'equipment',
name : 'equipment',
label : 'Equipment',
// Will be populated with items on first show
items : []
}
}
}
},
columns : [
{
type : 'resourceInfo',
text : 'Name',
width : 200,
showEventCount : false,
showRole : true
}
],
// The crud manager will load all its data (resource + events) in one ajax request
crudManager : {
autoLoad : true,
transport : {
load : {
url : 'data/data.json'
}
}
},
// Custom view preset with header configuration
viewPreset : {
base : 'hourAndDay',
columnLinesFor : 0,
mainHeaderLevel : 1,
headers : [
{
unit : 'd',
align : 'center',
dateFormat : 'ddd DD MMM'
},
{
unit : 'h',
align : 'center',
dateFormat : 'HH'
}
]
}
};
}
construct(config) {
const me = this;
super.construct(config);
me.on({
eventEditBeforeSetRecord : me.onBeforeRecordLoaded,
thisObj : me,
once : true
});
me.equipmentStore.on('load', me.onEquipmentStoreLoad, me);
}
// Render some extra elements for the assignment equipment items
eventRenderer({ eventRecord }) {
const equipment = eventRecord.equipment.map((itemId) => this.equipmentStore.getById(itemId) || {});
return `
<div class="b-sch-event-startdate">${DateHelper.format(eventRecord.startDate, 'LT')}</div>
<div class="b-sch-event-name">${StringHelper.encodeHtml(eventRecord.name || '')}</div>
<ul class="b-sch-event-equipment-wrap">
${equipment.map(item =>
`<li title="${StringHelper.encodeHtml(item.name)}" class="${item.iconCls}"></li>`).join('')
}
</ul>
`;
}
// Populate the equipment combo first time editor is shown
onBeforeRecordLoaded({ source : editor }) {
const equipmentCombo = editor.widgetMap.equipment;
if (!equipmentCombo.items.length) {
equipmentCombo.items = this.equipmentStore.getRange();
}
}
onEquipmentStoreLoad() {
// Setup the data for the equipment combo inside the event editor
// Since the event bars contain icons for equipment, we need to refresh rows once equipment store is available
this.refreshRows();
}
};
// Register this widget type with its Factory
Schedule.initClass();
class Task extends EventModel {
static get fields() {
return [
'equipment'
];
}
static get defaults() {
return {
// in this demo, default duration for tasks will be hours (instead of days)
durationUnit : 'h',
equipment : []
};
}
}
const equipmentStore = new AjaxStore({
readUrl : 'data/equipment.json',
fields : [
'name',
'iconCls'
],
sorters : [
{ field : 'name', ascending : true }
]
});
const schedule = new Schedule({
ref : 'schedule',
appendTo : 'bodycontainer',
startDate : new Date(2017, 11, 1, 8),
endDate : new Date(2017, 11, 1, 18),
equipmentStore,
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,
eventStore : {
modelClass : Task
},
transport : {
load : {
url : 'data/data.json'
}
}
}
});
equipmentStore.load();
// Create our list of equipment
const equipmentGrid = new EquipmentGrid({
ref : 'equipment',
appendTo : 'bodycontainer',
eventStore : schedule.eventStore,
// Use a chained Store to avoid its filtering to interfere with Scheduler's rendering
store : equipmentStore.chain()
});
const drag = new Drag({
grid : equipmentGrid,
schedule,
outerElement : equipmentGrid.element
});