updates
This commit is contained in:
+293
-109
@@ -1,12 +1,10 @@
|
||||
import './scheduler.css';
|
||||
|
||||
import { El } from '../../utils/dom';
|
||||
import { Boot } from '../../core/Boot';
|
||||
|
||||
export class Scheduler {
|
||||
|
||||
/**
|
||||
* Scheduler, Constructor
|
||||
*/
|
||||
constructor(target, opts) {
|
||||
|
||||
this.target = target;
|
||||
@@ -33,9 +31,19 @@ export class Scheduler {
|
||||
|
||||
this.target.dxData('scheduler', this);
|
||||
|
||||
this.data = {};
|
||||
this.group = [];
|
||||
|
||||
this.ui = {};
|
||||
|
||||
this.eventList = [];
|
||||
this.enabled = true;
|
||||
this.busy = false;
|
||||
|
||||
this.widget = Boot.intData?.widget?.scheduler?.children;
|
||||
|
||||
if (!this.widget) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.resetSelection();
|
||||
|
||||
@@ -44,9 +52,6 @@ export class Scheduler {
|
||||
this.setUiEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scheduler, Render Container
|
||||
*/
|
||||
renderContainer() {
|
||||
|
||||
this.ui.wrapper = this.target.appendChild( El('div', { className: 'sp-scheduler' }) );
|
||||
@@ -115,18 +120,16 @@ export class Scheduler {
|
||||
|
||||
uiSlot = El('div', { className: 'time-slot', style: 'height: '+ this.opts.cellSize + 'px;' });
|
||||
|
||||
|
||||
uiSlot.dataset.time = hours.toString().padStart(2, '0') + ':' + mins.toString().padStart(2, '0');
|
||||
|
||||
mins += this.opts.unitTime;
|
||||
|
||||
this.ui.events.append( uiSlot );
|
||||
}
|
||||
|
||||
this.trigger('onInit');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scheduler, Set UI Events
|
||||
*/
|
||||
setUiEvents() {
|
||||
|
||||
this.ui.events.dxOn('mousedown', '.time-slot', (e, t) => !e.button && this.mouseDownHandler(e, t));
|
||||
@@ -144,7 +147,7 @@ export class Scheduler {
|
||||
|
||||
this.ui.label.classList.add('hide');
|
||||
|
||||
this.trigger('slotSelected');
|
||||
this.trigger('onSlotSelect');
|
||||
|
||||
this.selTarget = null;
|
||||
}
|
||||
@@ -259,6 +262,10 @@ export class Scheduler {
|
||||
}
|
||||
}
|
||||
|
||||
getFormattedTime(hour, min) {
|
||||
return hour.toString().padStart(2, '0') + ':' + min.toString().padStart(2, '0')
|
||||
}
|
||||
|
||||
setSelectedData() {
|
||||
|
||||
this.selectedTime = {
|
||||
@@ -269,18 +276,18 @@ export class Scheduler {
|
||||
this.selectedTime.end = this.slotEnd.dataset.time
|
||||
}
|
||||
else {
|
||||
this.selectedTime.end = this.opts.endTime[0].toString().padStart(2, '0') + ':' + this.opts.endTime[1].toString().padStart(2, '0')
|
||||
this.selectedTime.end = this.getFormattedTime(this.opts.endTime[0], this.opts.endTime[1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
showLabel() {
|
||||
|
||||
if (!this.ui.label) {
|
||||
this.ui.label = this.ui.events.appendChild( El('div', { className: 'time-label' }) );
|
||||
this.ui.label = this.ui.events.appendChild( El('div', { class: 'time-label' }) );
|
||||
}
|
||||
|
||||
this.ui.label.textContent = this.selectedTime.start + ' - ' + this.selectedTime.end + ' ( ' + this.getTimeFormat() + ' )';
|
||||
|
||||
this.ui.label.textContent = this.selectedTime.start + ' - ' + this.selectedTime.end + ' ( ' + this.getFormattedLabel() + ' )';
|
||||
|
||||
this.ui.label.classList.remove('hide');
|
||||
}
|
||||
|
||||
@@ -290,7 +297,7 @@ export class Scheduler {
|
||||
this.ui.label.style.left = (e.pageX - this.ui.events.dxOffset().left + 14) + 'px';
|
||||
}
|
||||
|
||||
getTimeFormat() {
|
||||
getFormattedLabel() {
|
||||
|
||||
let raw = '';
|
||||
|
||||
@@ -301,118 +308,95 @@ export class Scheduler {
|
||||
let hours = total >= 60 ? Math.floor(total / 60) : 0;
|
||||
let mins = total % 60;
|
||||
|
||||
if (hours > 0) {
|
||||
|
||||
if (hours == 1) {
|
||||
raw += 'o oră';
|
||||
}
|
||||
else {
|
||||
raw += hours + ' ore';
|
||||
}
|
||||
|
||||
if (mins) {
|
||||
raw += ' și ';
|
||||
}
|
||||
if (!hours) {
|
||||
raw = this.widget.label.minutes.replace('%minutes', mins);
|
||||
}
|
||||
|
||||
if (mins) {
|
||||
raw += mins + ' minute';
|
||||
else if (hours == 1) {
|
||||
raw = !mins ? this.widget.label.one_hour : this.widget.label.one_hour_minutes.replace('%minutes', mins);
|
||||
}
|
||||
else {
|
||||
raw = !mins ? this.widget.label.hours.replace('%hours', hours) : this.widget.label.hours_minutes.replace('%hours', hours).replace('%minutes', mins);
|
||||
}
|
||||
|
||||
return raw;
|
||||
}
|
||||
|
||||
addEvent(content) {
|
||||
|
||||
let count = 0;
|
||||
this.findSelection(_ => count++);
|
||||
|
||||
let selStart = this.selectedTime.start;
|
||||
let selEnd = this.selectedTime.end;
|
||||
let first = null;
|
||||
|
||||
this.ui.events.dxChildren('.ev-box', t => {
|
||||
|
||||
if ((selStart <= t.dataset.start && selEnd > t.dataset.start)
|
||||
|| (selStart < t.dataset.end && selEnd > t.dataset.end)
|
||||
|| (selStart >= t.dataset.start && selEnd <= t.dataset.end)
|
||||
) {
|
||||
first = t;
|
||||
}
|
||||
});
|
||||
addEventBox(id) {
|
||||
|
||||
let evBox = this.ui.events.appendChild(
|
||||
El('div', { className: 'ev-box' },
|
||||
El('div', { className: 'ev-content' }, content)
|
||||
El('div', { class: 'ev-box' },
|
||||
El('div', { class: 'ev-content' }, ... [...arguments].slice(1) )
|
||||
)
|
||||
);
|
||||
|
||||
let index = first ? first.dataset.index : Date.now();
|
||||
evBox.dataset.id = id;
|
||||
|
||||
evBox.dataset.index = index;
|
||||
evBox.dataset.start = this.selectedTime.start;
|
||||
evBox.dataset.end = this.selectedTime.end;
|
||||
|
||||
evBox.style.top = this.slotStart.offsetTop + 'px';
|
||||
evBox.style.height = (this.opts.cellSize * count - 1) + 'px';
|
||||
|
||||
this.resizeGroup(index);
|
||||
this.resizeEvents();
|
||||
|
||||
this.clearSelection();
|
||||
}
|
||||
|
||||
addEvent(id, startTime, endTime, data) {
|
||||
|
||||
this.data.push([ id, startTime, endTime, data ]);
|
||||
|
||||
this.addEventBox(id,
|
||||
El('span', { class: 'ev-time text-semibold' }, startTime + ' - ' + endTime),
|
||||
' - ',
|
||||
El('span', { class: 'ev-data' }, data)
|
||||
);
|
||||
}
|
||||
|
||||
updateEvent(id, startTime, endTime, data) {
|
||||
|
||||
let index = this.data.findIndex(item => id == item[0]);
|
||||
|
||||
if (index == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.data[ index ] = [ id, startTime, endTime, data ];
|
||||
|
||||
this.resizeEvents();
|
||||
}
|
||||
|
||||
deleteEvent(ev) {
|
||||
|
||||
let index = this.data.findIndex(item => ev.id == item[0]);
|
||||
|
||||
if (index == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.data.splice(index, 1);
|
||||
|
||||
ev.container.remove();
|
||||
|
||||
this.resizeEvents();
|
||||
|
||||
this.trigger('onEventDelete');
|
||||
}
|
||||
|
||||
hasOverlap(target) {
|
||||
|
||||
if (this.opts.maxEvents == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
let time = target.dataset.time;
|
||||
|
||||
let found = 0;
|
||||
|
||||
this.ui.events.dxChildren('.ev-box', t => {
|
||||
if (time >= t.dataset.start && time < t.dataset.end ) {
|
||||
for (const item of this.data) {
|
||||
|
||||
if (time >= item[1] && time < item[2]) {
|
||||
found++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return found >= this.opts.maxEvents;
|
||||
}
|
||||
|
||||
resizeGroup(index) {
|
||||
|
||||
let cols = this.ui.events.dxChildren('.ev-box[data-index="'+ index +'"]', true);
|
||||
|
||||
cols = cols.sort((a, b) => {
|
||||
|
||||
if (a.dataset.start < b.dataset.start) {
|
||||
return -1;
|
||||
}
|
||||
else if (a.dataset.start > b.dataset.start) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
let unit = 100 / cols.length;
|
||||
let space = 5 / cols.length;
|
||||
|
||||
cols.forEach((t, i) => {
|
||||
|
||||
t.style.width = (unit - space - 0.5) + '%'
|
||||
t.style.left = (i * (unit - space) ) + '%';
|
||||
});
|
||||
}
|
||||
|
||||
deleteEvent(ref) {
|
||||
|
||||
ref.container.remove();
|
||||
this.resizeGroup(ref.index);
|
||||
|
||||
this.trigger('eventDeleted');
|
||||
}
|
||||
|
||||
findSelection(callback) {
|
||||
|
||||
@@ -436,14 +420,25 @@ export class Scheduler {
|
||||
|
||||
eventClickHandler(t) {
|
||||
|
||||
this.trigger('eventClick',
|
||||
let id = t.dataset.id;
|
||||
|
||||
let index = this.data.findIndex(item => id == item[0]);
|
||||
|
||||
if (index == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
let item = this.data[ index ];
|
||||
|
||||
this.trigger('onEventClick',
|
||||
this, {
|
||||
container: t,
|
||||
content: t.dxChildren('.ev-content'),
|
||||
index: t.dataset.index,
|
||||
scheduler: this,
|
||||
container: t,
|
||||
content: t.dxChildren('.ev-content'),
|
||||
id: id,
|
||||
interval: {
|
||||
start: t.dataset.start,
|
||||
end: t.dataset.end
|
||||
start: item[1],
|
||||
end: item[2]
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -457,6 +452,10 @@ export class Scheduler {
|
||||
return this.selectedTime.end;
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
leaveSelection() {
|
||||
|
||||
this.ui.events.dxOff('mouseover');
|
||||
@@ -480,16 +479,201 @@ export class Scheduler {
|
||||
}
|
||||
|
||||
clearSelection() {
|
||||
|
||||
|
||||
if (!this.slotStart) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.findSelection(t => t.classList.remove('selected', 'main-selected'));
|
||||
//this.findSelection(t => t.classList.remove('selected', 'main-selected'));
|
||||
this.ui.events.dxChildren('.time-slot', t => t.classList.remove('selected', 'main-selected'));
|
||||
|
||||
this.resetSelection();
|
||||
|
||||
this.trigger('slotCleared');
|
||||
this.trigger('onSlotClear');
|
||||
}
|
||||
|
||||
getOverlapIndex(startTime, endTime, id) {
|
||||
|
||||
let index = -1;
|
||||
|
||||
for (let i = 0; i < this.group.length; i++) {
|
||||
|
||||
for (const item of this.group[i]) {
|
||||
|
||||
if (id == item[0]) {
|
||||
index = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((startTime <= item[1] && endTime > item[2])
|
||||
|| (startTime < item[2] && endTime > item[2])
|
||||
|| (startTime >= item[1] && endTime <= item[2])
|
||||
) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return index != -1 ? index : this.group.length;
|
||||
}
|
||||
|
||||
|
||||
disable() {
|
||||
|
||||
if (this.enabled) {
|
||||
this.enabled = false;
|
||||
this.ui.events.classList.add('scale-disabled');
|
||||
}
|
||||
}
|
||||
|
||||
enable() {
|
||||
|
||||
if (!this.enabled) {
|
||||
this.enabled = true;
|
||||
this.ui.events.classList.remove('scale-disabled');
|
||||
}
|
||||
}
|
||||
|
||||
load() {
|
||||
|
||||
if (!this.busy) {
|
||||
this.busy = true;
|
||||
this.ui.wrapper.append(
|
||||
El('div', { class: 'scheduler-modal' },
|
||||
El('div', { class: 'sp-loader' })
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
unload() {
|
||||
|
||||
if (this.busy) {
|
||||
this.busy = false;
|
||||
this.ui.wrapper.dxChildren('.scheduler-modal').remove();
|
||||
}
|
||||
}
|
||||
|
||||
sortData() {
|
||||
|
||||
this.data = this.data.sort((a, b) => {
|
||||
|
||||
if (a[1] < b[1]) {
|
||||
return -1;
|
||||
}
|
||||
else if (a[1] > b[1]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
update(data) {
|
||||
|
||||
this.data = data;
|
||||
this.group = [];
|
||||
|
||||
this.sortData();
|
||||
|
||||
this.data.forEach(item => {
|
||||
|
||||
let index = this.getOverlapIndex(item[1], item[2], null);
|
||||
|
||||
if (!this.group[ index ]) {
|
||||
this.group[ index ] = [];
|
||||
}
|
||||
|
||||
this.group[ index ].push(item);
|
||||
|
||||
}, this);
|
||||
|
||||
for (const group of this.group) {
|
||||
|
||||
let unit = 100 / group.length;
|
||||
let space = 5 / group.length;
|
||||
let index = 0;
|
||||
|
||||
for (const item of group) {
|
||||
|
||||
let evBox = this.ui.events.appendChild(
|
||||
El('div', { class: 'ev-box' },
|
||||
El('div', { class: 'ev-content' },
|
||||
El('span', { class: 'ev-time text-semibold' }, item[1] + ' - ' + item[2]),
|
||||
' - ',
|
||||
El('span', { class: 'ev-data' }, item[3])
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
evBox.dataset.id = item[0];
|
||||
|
||||
const slotStart = this.ui.events.dxChildren('.time-slot[data-time="'+ item[1] +'"]');
|
||||
const slotEnd = this.ui.events.dxChildren('.time-slot[data-time="'+ item[2] +'"]');
|
||||
|
||||
evBox.style.top = slotStart.offsetTop + 'px';
|
||||
evBox.style.left = (index * (unit - space) ) + '%';
|
||||
evBox.style.width = (unit - space - 0.5) + '%'
|
||||
|
||||
evBox.style.height = (slotEnd.offsetTop - slotStart.offsetTop - 1) + 'px';
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
|
||||
this.data = [];
|
||||
this.group = [];
|
||||
|
||||
this.ui.events.dxChildren('.ev-box', t => t.remove());
|
||||
}
|
||||
|
||||
resizeEvents() {
|
||||
|
||||
this.group = [];
|
||||
|
||||
this.sortData();
|
||||
|
||||
this.data.forEach(item => {
|
||||
|
||||
let index = this.getOverlapIndex(item[1], item[2], null);
|
||||
|
||||
if (!this.group[ index ]) {
|
||||
this.group[ index ] = [];
|
||||
}
|
||||
|
||||
this.group[ index ].push(item);
|
||||
|
||||
}, this);
|
||||
|
||||
for (const group of this.group) {
|
||||
|
||||
let unit = 100 / group.length;
|
||||
let space = 5 / group.length;
|
||||
let index = 0;
|
||||
|
||||
for (const item of group) {
|
||||
|
||||
let evBox = this.ui.events.dxChildren('.ev-box[data-id="'+ item[0] +'"]')
|
||||
|
||||
if (!evBox) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let slotStart = this.ui.events.dxChildren('.time-slot[data-time="'+ item[1] +'"]');
|
||||
let slotEnd = this.ui.events.dxChildren('.time-slot[data-time="'+ item[2] +'"]');
|
||||
|
||||
evBox.style.top = slotStart.offsetTop + 'px';
|
||||
evBox.style.left = (index * (unit - space) ) + '%';
|
||||
evBox.style.width = (unit - space - 0.5) + '%'
|
||||
|
||||
evBox.style.height = (slotEnd.offsetTop - slotStart.offsetTop - 1) + 'px';
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trigger(event) {
|
||||
@@ -505,7 +689,7 @@ export class Scheduler {
|
||||
if (typeof handler === 'function') {
|
||||
handler(... args);
|
||||
}
|
||||
|
||||
|
||||
this.target.dxTrigger(event, { scheduler: this, args: args });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user