diff --git a/dist/calendar.css b/dist/calendar.css index 6f41911..2ba10fc 100644 --- a/dist/calendar.css +++ b/dist/calendar.css @@ -1 +1 @@ -.calendar *{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.calendar{width:100%;border:.1rem solid #cbcbcb;border-spacing:0;border-collapse:collapse;table-layout:fixed;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif}.calendar thead th{padding:.5rem}.calendar thead tr:first-child{background-color:#fcfcfc}.calendar td,.calendar th{height:1rem;border:1px solid #37474f;text-align:center}.calendar thead tr>th:first-child{width:4rem}.calendar td::-moz-selection,.calendar th::-moz-selection{background-color:transparent}.calendar td::selection,.calendar th::selection{background-color:transparent}.calendar tbody tr:hover{background-color:#eceff1}.calendar td{cursor:pointer}.calendar th::-moz-selection{background:0 0}.calendar th::selection{background:0 0}.calendar tr:last-of-type{display:none}.calendar-bulk{display:none}[data-mode=locked] .calendar-bulk{display:table-cell}.calendar-bulk button{margin:0;padding:0 2rem;outline:0;background-color:transparent}.calendar-bulk button svg{width:1.5rem;height:1.5rem;pointer-events:none;fill:#212121}.calendar-event{background-color:#28a69b;color:#fff;cursor:pointer}.calendar .calendar-splited{border-top-width:5px}.calendar-locked{background-color:#b0bec5}.calendar-selection--allowed{background-color:#4caf50;cursor:pointer}.calendar-selection--forbidden{background-color:#e53935;cursor:not-allowed}.calendar-mode{position:fixed;right:0;bottom:0;left:0;padding:1.5rem;border-top:1px solid #39434c;background-color:#eef2c0;text-align:center;font-size:1.2rem;transition:-webkit-transform .5s ease;transition:transform .5s ease;transition:transform .5s ease,-webkit-transform .5s ease;-webkit-transform:translateY(100%);-ms-transform:translateY(100%);transform:translateY(100%)}.calendar-mode--active{-webkit-transform:translate(0);-ms-transform:translate(0);transform:translate(0)}.calendar-mode button{margin-left:1rem;background-color:#3b444b;color:#fff;font-size:1rem} \ No newline at end of file +.calendar *{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.calendar{width:100%;border:.1rem solid #cbcbcb;border-spacing:0;border-collapse:collapse;table-layout:fixed;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif}.calendar thead th{padding:.5rem}.calendar thead tr:first-child{background-color:#fcfcfc}.calendar td,.calendar th{height:1rem;border:1px solid #37474f;text-align:center}.calendar thead tr>th:first-child{width:4rem}.calendar td::-moz-selection,.calendar th::-moz-selection{background-color:transparent}.calendar td::selection,.calendar th::selection{background-color:transparent}.calendar tbody tr:hover{background-color:#eceff1}.calendar td{cursor:pointer}.calendar th::-moz-selection{background:0 0}.calendar th::selection{background:0 0}.calendar tr:last-of-type{display:none}.calendar-bulk{display:none}[data-mode=locked] .calendar-bulk{display:table-cell}.calendar-bulk button{margin:0;padding:0 2rem;outline:0;background-color:transparent}.calendar-bulk button svg{width:1.5rem;height:1.5rem;pointer-events:none;fill:#212121}.calendar-event{background-color:#28a69b;color:#fff;cursor:pointer}.calendar .calendar-splited{border-top-width:5px}.calendar-locked{background-color:#b0bec5}.calendar-selection--allowed{background-color:#4caf50;cursor:pointer}.calendar-selection--forbidden{background-color:#e53935;cursor:not-allowed}.calendar-mode{position:fixed;right:0;bottom:0;left:0;padding:1.5rem;border-top:1px solid #39434c;background-color:#eef2c0;text-align:center;font-size:1.2rem;-webkit-transition:-webkit-transform .5s ease;transition:-webkit-transform .5s ease;transition:transform .5s ease;transition:transform .5s ease,-webkit-transform .5s ease;-webkit-transform:translateY(100%);-ms-transform:translateY(100%);transform:translateY(100%)}.calendar-mode--active{-webkit-transform:translate(0);-ms-transform:translate(0);transform:translate(0)}.calendar-mode button{margin-left:1rem;background-color:#3b444b;color:#fff;font-size:1rem} \ No newline at end of file diff --git a/dist/calendar.js b/dist/calendar.js index 9195f1b..a24eef4 100644 --- a/dist/calendar.js +++ b/dist/calendar.js @@ -1,1081 +1,1020 @@ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Calendar = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 1) { - return; + } + } + } + + // TODO: refactoring + if (this.mode.LOCKED_MODE.stack[this.mode.LOCKED_MODE.stack.length - 1].start === null) { + this.mode.LOCKED_MODE.stack.splice(-1, 1); + } + + // callback + this.options.onCommitLocked(this.mode.LOCKED_MODE.stack); + this.mode.LOCKED_MODE.stack = [{ + start: null, + end: null, + column: null + }]; + this.resetMode(); + } + }, { + key: "startAddEventMode", + value: function startAddEventMode(duration, callback) { + // switch to, add mode + this._switchMode(ADD_MODE); + this.mode.ADD_MODE.dropAllowed = true; + this.mode.ADD_MODE.slotsToTake = Math.floor(duration / this.options.slotDuration); + this.mode.ADD_MODE.callback = callback; + if (!this.mode.ADD_MODE.slotsToTake >= 1) { + return; + } + } + }, { + key: "_attachClickEvent", + value: function _attachClickEvent(el) { + var _this4 = this; + el.addEventListener('click', function (event) { + event.stopPropagation(); + if (_this4.mode.current === VIEW_MODE) { + _this4.mergedEvents.forEach(function (e) { + if (e.id == event.target.dataset.eventId) { + var baseEvents = e.mergedEvents || [e]; // Retrieve original events + _this4.options.onEventClicked(baseEvents); // Pass the array of merged events } + }); } - }, { - key: '_attachClickEvent', - value: function _attachClickEvent(el) { - var _this4 = this; - - el.addEventListener('click', function (event) { - event.stopPropagation(); - if (_this4.mode.current == VIEW_MODE) { - _this4.events.forEach(function (e) { - if (e.id == event.target.dataset.eventId) { - _this4.options.onEventClicked(e); - } - }); - } - }); - } - }, { - key: '_bindControlls', - value: function _bindControlls() { - var _this5 = this; - - if (this.options.ui.next) { - this.options.ui.next.addEventListener('click', function () { - var newDate = DateManager.addToDate(_this5.options.currentDay, _this5.options.numberOfDays); - _this5.options.currentDay = newDate; - _this5.build(); - _this5.options.onPeriodChange.bind(_this5)(newDate, DateManager.addToDate(newDate, _this5.options.numberOfDays)); - }); - } - - if (this.options.ui.prev) { - this.options.ui.prev.addEventListener('click', function () { - var newDate = DateManager.addToDate(_this5.options.currentDay, -_this5.options.numberOfDays); - _this5.options.currentDay = newDate; - _this5.build(); - _this5.options.onPeriodChange.bind(_this5)(newDate, DateManager.addToDate(newDate, _this5.options.numberOfDays)); - }); - } - - if (this.options.ui.today) { - this.options.ui.today.addEventListener('click', function () { - var newDate = new Date(); - _this5.options.currentDay = newDate; - _this5.build(); - _this5.options.onPeriodChange.bind(_this5)(newDate, DateManager.addToDate(newDate, _this5.options.numberOfDays)); - }); + }); + } + }, { + key: "_bindControlls", + value: function _bindControlls() { + var _this5 = this; + if (this.options.ui.next) { + this.options.ui.next.addEventListener('click', function () { + var newDate = DateManager.addToDate(_this5.options.currentDay, _this5.options.numberOfDays); + _this5.options.currentDay = newDate; + _this5.build(); + _this5.options.onPeriodChange.bind(_this5)(newDate, DateManager.addToDate(newDate, _this5.options.numberOfDays)); + }); + } + if (this.options.ui.prev) { + this.options.ui.prev.addEventListener('click', function () { + var newDate = DateManager.addToDate(_this5.options.currentDay, -_this5.options.numberOfDays); + _this5.options.currentDay = newDate; + _this5.build(); + _this5.options.onPeriodChange.bind(_this5)(newDate, DateManager.addToDate(newDate, _this5.options.numberOfDays)); + }); + } + if (this.options.ui.today) { + this.options.ui.today.addEventListener('click', function () { + var newDate = new Date(); + _this5.options.currentDay = newDate; + _this5.build(); + _this5.options.onPeriodChange.bind(_this5)(newDate, DateManager.addToDate(newDate, _this5.options.numberOfDays)); + }); + } + } + }, { + key: "goToDate", + value: function goToDate(date) { + this.options.currentDay = date; + this.build(); + this.options.onPeriodChange.bind(this)(date, DateManager.addToDate(date, this.options.numberOfDays)); + } + }, { + key: "bulk", + value: function bulk(event) { + if (!event.target.dataset.bulk) return; + if (event.target.dataset.bulk === 'locked') { + for (var i = 1; i <= this.dateManager.hours.length; i++) { + var cell = document.querySelector('[data-coordinate="' + i + '#' + event.target.dataset.col + '"]'); + if (cell.dataset.type !== 'event') { + cell.classList.add('calendar-locked'); + cell.dataset.type = 'locked'; + } + } + event.target.innerHTML = UIManager.getIcon('unlocked'); + event.target.dataset.bulk = 'unlocked'; + } else { + for (var _i = 1; _i <= this.dateManager.hours.length; _i++) { + var _cell = document.querySelector('[data-coordinate="' + _i + '#' + event.target.dataset.col + '"]'); + console.log('[data-coordinate="' + _i + '#' + event.target.dataset.col + '"]'); + console.log(_cell); + if (_cell.dataset.type !== 'event') { + _cell.classList.remove('calendar-locked'); + _cell.removeAttribute('data-type'); + } + } + event.target.innerHTML = UIManager.getIcon('locked'); + event.target.dataset.bulk = 'locked'; + } + } + }, { + key: "_bindEvents", + value: function _bindEvents() { + var _this6 = this; + // footer + this.uiManager.ui.footer.btn.addEventListener('click', function (event) { + _this6.uiManager.currentFooterCallback(); + _this6.uiManager.hideFooter(); + }); + + // bulk actions + [].forEach.call(document.querySelectorAll('[data-bulk]'), function (el) { + el.addEventListener('click', _this6.bulk.bind(_this6)); + }); + [].forEach.call(document.querySelectorAll('[data-type="event"]'), function (el) { + // click on event + _this6._attachClickEvent(el); + }); + [].forEach.call(document.querySelectorAll('[data-id]'), function (el) { + // click on cell + el.addEventListener('click', function (event) { + event.stopPropagation(); + switch (_this6.mode.current) { + case ADD_MODE: + var id = event.target.dataset.id.split('#'); + if (!_this6.mode.ADD_MODE.dropAllowed) { + alert("Ce créneau n'est pas disponible"); + } else { + // call back method with date and column + var date = new Date(parseInt(id[0])); + _this6.mode.ADD_MODE.callback(date, id[1]); + _this6.resetMode(); + } + break; + default: + } + }); + + // mouse down + el.addEventListener('mousedown', function (event) { + if (_this6.mode.current == LOCKED_MODE) { + if (event.target.dataset.type !== 'event') { + if (event.target.dataset.type === 'locked') { + _this6.mode.LOCKED_MODE.locking = false; + _this6.mode.LOCKED_MODE.unlocking = true; + event.target.classList.remove('calendar-locked'); + event.target.removeAttribute('data-type'); + } else { + _this6.mode.LOCKED_MODE.locking = true; + _this6.mode.LOCKED_MODE.unlocking = false; + event.target.classList.add('calendar-locked'); + event.target.dataset.type = 'locked'; + } + ; } - } - }, { - key: 'goToDate', - value: function goToDate(date) { - this.options.currentDay = date; - this.build(); - this.options.onPeriodChange.bind(this)(date, DateManager.addToDate(date, this.options.numberOfDays)); - } - }, { - key: 'bulk', - value: function bulk(event) { - if (!event.target.dataset.bulk) return; - - if (event.target.dataset.bulk === 'locked') { - for (var i = 1; i <= this.dateManager.hours.length; i++) { - var cell = document.querySelector('[data-coordinate="' + i + '#' + event.target.dataset.col + '"]'); - if (cell.dataset.type !== 'event') { - cell.classList.add('calendar-locked'); - cell.dataset.type = 'locked'; - } + } + }); + + // mouse up + el.addEventListener('mouseup', function (event) { + if (_this6.mode.current == LOCKED_MODE) { + _this6.mode.LOCKED_MODE.locking = false; + _this6.mode.LOCKED_MODE.unlocking = false; + } + }); + + // hovering a cell + el.addEventListener('mouseover', function (event) { + switch (_this6.mode.current) { + case ADD_MODE: + [].forEach.call(document.querySelectorAll('[data-id]'), function (cell) { + cell.classList.remove('calendar-selection--allowed'); + cell.classList.remove('calendar-selection--forbidden'); + }); + var cellAdress = event.target.dataset.coordinate.split('#'); + var currentRow = cellAdress[0]; + var cells = []; + var cssClass = 'calendar-selection--allowed'; + var dropAllowed = true; + for (var i = 0; i < _this6.mode.ADD_MODE.slotsToTake; i++) { + var currentCell = document.querySelector('[data-coordinate="' + currentRow + '#' + cellAdress[1] + '"]'); + cells.push(currentCell); + var isOverSplitedCell = currentCell.dataset.type === 'splited' && i > 0; + var excludeCurrentEvent = !_this6.mode.EDIT_MODE || !_this6.mode.EDIT_MODE.event || !_this6.mode.EDIT_MODE.event.id || currentCell.dataset.eventId != _this6.mode.EDIT_MODE.event.id; + if (currentCell.dataset.type === 'locked' || isOverSplitedCell || currentCell.dataset.type === 'event' && excludeCurrentEvent) { + cssClass = 'calendar-selection--forbidden'; + dropAllowed = false; } - event.target.innerHTML = UIManager.getIcon('unlocked'); - event.target.dataset.bulk = 'unlocked'; - } else { - for (var _i = 1; _i <= this.dateManager.hours.length; _i++) { - var _cell = document.querySelector('[data-coordinate="' + _i + '#' + event.target.dataset.col + '"]'); - console.log('[data-coordinate="' + _i + '#' + event.target.dataset.col + '"]'); - console.log(_cell); - if (_cell.dataset.type !== 'event') { - _cell.classList.remove('calendar-locked'); - _cell.removeAttribute('data-type'); - } + currentRow++; + } + if (dropAllowed) { + _this6.mode.ADD_MODE.dropAllowed = true; + } else { + _this6.mode.ADD_MODE.dropAllowed = false; + } + cells.forEach(function (cell) { + cell.classList.add(cssClass); + }); + break; + case LOCKED_MODE: + if (event.target.dataset.type !== 'event') { + if (_this6.mode.LOCKED_MODE.locking) { + event.target.classList.add('calendar-locked'); + event.target.dataset.type = 'locked'; + } else if (_this6.mode.LOCKED_MODE.unlocking) { + event.target.classList.remove('calendar-locked'); + event.target.removeAttribute('data-type'); } - event.target.innerHTML = UIManager.getIcon('locked'); - event.target.dataset.bulk = 'locked'; - } - } - }, { - key: '_bindEvents', - value: function _bindEvents() { - var _this6 = this; - - // footer - this.uiManager.ui.footer.btn.addEventListener('click', function (event) { - _this6.uiManager.currentFooterCallback(); - _this6.uiManager.hideFooter(); - }); - - // bulk actions - [].forEach.call(document.querySelectorAll('[data-bulk]'), function (el) { - el.addEventListener('click', _this6.bulk.bind(_this6)); - }); - - [].forEach.call(document.querySelectorAll('[data-type="event"]'), function (el) { - // click on event - _this6._attachClickEvent(el); - }); - - [].forEach.call(document.querySelectorAll('[data-id]'), function (el) { - - // click on cell - el.addEventListener('click', function (event) { - event.stopPropagation(); - - switch (_this6.mode.current) { - case ADD_MODE: - var id = event.target.dataset.id.split('#'); - if (!_this6.mode.ADD_MODE.dropAllowed) { - alert("Ce créneau n'est pas disponible"); - } else { - // call back method with date and column - var date = new Date(parseInt(id[0])); - _this6.mode.ADD_MODE.callback(date, id[1]); - _this6.resetMode(); - } - break; - default: - - } - }); - - // mouse down - el.addEventListener('mousedown', function (event) { - if (_this6.mode.current == LOCKED_MODE) { - if (event.target.dataset.type !== 'event') { - if (event.target.dataset.type === 'locked') { - _this6.mode.LOCKED_MODE.locking = false; - _this6.mode.LOCKED_MODE.unlocking = true; - event.target.classList.remove('calendar-locked'); - event.target.removeAttribute('data-type'); - } else { - _this6.mode.LOCKED_MODE.locking = true; - _this6.mode.LOCKED_MODE.unlocking = false; - event.target.classList.add('calendar-locked'); - event.target.dataset.type = 'locked'; - }; - } - } - }); - - // mouse up - el.addEventListener('mouseup', function (event) { - if (_this6.mode.current == LOCKED_MODE) { - _this6.mode.LOCKED_MODE.locking = false; - _this6.mode.LOCKED_MODE.unlocking = false; - } - }); - - // hovering a cell - el.addEventListener('mouseover', function (event) { - - switch (_this6.mode.current) { - case ADD_MODE: - - [].forEach.call(document.querySelectorAll('[data-id]'), function (cell) { - cell.classList.remove('calendar-selection--allowed'); - cell.classList.remove('calendar-selection--forbidden'); - }); - - var cellAdress = event.target.dataset.coordinate.split('#'); - var currentRow = cellAdress[0]; - var cells = []; - - var cssClass = 'calendar-selection--allowed'; - var dropAllowed = true; - - for (var i = 0; i < _this6.mode.ADD_MODE.slotsToTake; i++) { - var currentCell = document.querySelector('[data-coordinate="' + currentRow + '#' + cellAdress[1] + '"]'); - cells.push(currentCell); - var isOverSplitedCell = currentCell.dataset.type === 'splited' && i > 0; - var excludeCurrentEvent = !_this6.mode.EDIT_MODE || !_this6.mode.EDIT_MODE.event || !_this6.mode.EDIT_MODE.event.id || currentCell.dataset.eventId != _this6.mode.EDIT_MODE.event.id; - if (currentCell.dataset.type === 'locked' || isOverSplitedCell || currentCell.dataset.type === 'event' && excludeCurrentEvent) { - cssClass = 'calendar-selection--forbidden'; - dropAllowed = false; - } - currentRow++; - } - - if (dropAllowed) { - _this6.mode.ADD_MODE.dropAllowed = true; - } else { - _this6.mode.ADD_MODE.dropAllowed = false; - } - - cells.forEach(function (cell) { - cell.classList.add(cssClass); - }); - break; - case LOCKED_MODE: - if (event.target.dataset.type !== 'event') { - if (_this6.mode.LOCKED_MODE.locking) { - event.target.classList.add('calendar-locked'); - event.target.dataset.type = 'locked'; - } else if (_this6.mode.LOCKED_MODE.unlocking) { - event.target.classList.remove('calendar-locked'); - event.target.removeAttribute('data-type'); - } - } - default: - - } - }); - }); - } - }]); - - return Calendar; + } + default: + } + }); + }); + } + }]); }(); - module.exports = Calendar; -},{"./cellMatrix":2,"./dateManager":3,"./eventDispatcher":4,"./eventsManager":5,"./lockedEventDispatcher":6,"./uiManager":7}],2:[function(require,module,exports){ +},{"./cellMatrix":2,"./dateManager":3,"./eventDispatcher":4,"./lockedEventDispatcher":5,"./uiManager":6}],2:[function(require,module,exports){ "use strict"; -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -var CellMatrix = function () { - function CellMatrix(days, nbColumns, hours, slotDuration) { - _classCallCheck(this, CellMatrix); - - this.days = days; - this.nbColumns = nbColumns; - this.hours = hours; - this.slotDuration = slotDuration; - this.matrix = []; +function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } +function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } +function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } } +function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +var CellMatrix = /*#__PURE__*/function () { + function CellMatrix(days, nbColumns, hours, slotDuration) { + _classCallCheck(this, CellMatrix); + this.days = days; + this.nbColumns = nbColumns; + this.hours = hours; + this.slotDuration = slotDuration; + this.matrix = []; + } + return _createClass(CellMatrix, [{ + key: "loadEvents", + value: function loadEvents(events) { + this.events = events; + this.resetMatrix(); + this.eventsToMatix(); } - - _createClass(CellMatrix, [{ - key: "loadEvents", - value: function loadEvents(events) { - this.events = events; - this.resetMatrix(); - this.eventsToMatix(); - } - }, { - key: "resetMatrix", - value: function resetMatrix() { - var _this = this; - - var nbColumns = this.days.length * this.nbColumns; - - var _loop = function _loop(i) { - _this.matrix[i] = []; - _this.hours.forEach(function (el, j) { - _this.matrix[i][j + 1] = 0; - }); - }; - - for (var i = 1; i <= nbColumns; i++) { - _loop(i); - } - } - }, { - key: "eventsToMatix", - value: function eventsToMatix() { - var _this2 = this; - - this.events.forEach(function (el) { - var slotsToTake = Math.floor(el.duration / _this2.slotDuration); - }); - } - }]); - - return CellMatrix; + }, { + key: "resetMatrix", + value: function resetMatrix() { + var _this = this; + var nbColumns = this.days.length * this.nbColumns; + var _loop = function _loop(i) { + _this.matrix[i] = []; + _this.hours.forEach(function (el, j) { + _this.matrix[i][j + 1] = 0; + }); + }; + for (var i = 1; i <= nbColumns; i++) { + _loop(i); + } + } + }, { + key: "eventsToMatix", + value: function eventsToMatix() { + var _this2 = this; + this.events.forEach(function (el) { + var slotsToTake = Math.floor(el.duration / _this2.slotDuration); + }); + } + }]); }(); - module.exports = CellMatrix; },{}],3:[function(require,module,exports){ -'use strict'; - -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -var DateManager = function () { - function DateManager(startDate) { - _classCallCheck(this, DateManager); - - this.startDate = startDate; - - this.startDate.setHours(0); - this.startDate.setMinutes(0); - this.startDate.setSeconds(0); - this.startDate.setMilliseconds(0); +"use strict"; - this.days = []; - this.hours = []; +function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } +function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } +function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } } +function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +var DateManager = /*#__PURE__*/function () { + function DateManager(startDate) { + _classCallCheck(this, DateManager); + this.startDate = startDate; + this.startDate.setHours(0); + this.startDate.setMinutes(0); + this.startDate.setSeconds(0); + this.startDate.setMilliseconds(0); + this.days = []; + this.hours = []; + } + return _createClass(DateManager, [{ + key: "generateDays", + value: function generateDays(number) { + // push first day + var currentDate = this.startDate; + this.days.push(currentDate); + + // then calculate other + for (var i = 1; i < number; i++) { + currentDate = this.constructor.addToDate(currentDate, 1, 0, 0, 0); + this.days.push(currentDate); + } } - - _createClass(DateManager, [{ - key: 'generateDays', - value: function generateDays(number) { - // push first day - var currentDate = this.startDate; - this.days.push(currentDate); - - // then calculate other - for (var i = 1; i < number; i++) { - currentDate = this.constructor.addToDate(currentDate, 1, 0, 0, 0); - this.days.push(currentDate); - } - } - }, { - key: 'generateHours', - value: function generateHours(start, end, slotDuration) { - - // parse start time and cast string to int - var startTime = start.split(':').map(function (item) { - return parseInt(item); - }); - - // parse end time and cast string to int - var endTime = end.split(':').map(function (item) { - return parseInt(item); - }); - - // use a date object to calculate minutes and hours (exact date is not important) - var startDateObject = new Date(); - startDateObject.setHours(startTime[0]); - startDateObject.setMinutes(startTime[1]); - startDateObject.setSeconds(0); - startDateObject.setMilliseconds(0); - - // TODO: handle day change - var endDateObject = new Date(); - endDateObject.setHours(endTime[0]); - endDateObject.setMinutes(endTime[1]); - endDateObject.setSeconds(0); - endDateObject.setMilliseconds(0); - - //console.log(startDateObject); - //console.log(endDateObject); - - var currentDateObject = startDateObject; - - // push first occurence - this.hours.push(currentDateObject); - - while (currentDateObject.getTime() < endDateObject.getTime()) { - //console.log(currentDateObject); - currentDateObject = this.constructor.addToDate(currentDateObject, 0, 0, slotDuration, 0); - this.hours.push(currentDateObject); - } - } - }, { - key: 'getDayLabel', - value: function getDayLabel(index) { - return this.days[index - 1].toLocaleDateString(); - } - }, { - key: 'getDayName', - value: function getDayName(index) { - return this.numberToDay(this.days[index - 1].getDay()); - } - }, { - key: 'numberToDay', - value: function numberToDay(number) { - var frDays = ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi']; - return frDays[number]; - } - }, { - key: 'getHoursLabel', - value: function getHoursLabel(index) { - return this.formatLeadingZero(this.hours[index].getHours()) + ':' + this.formatLeadingZero(this.hours[index].getMinutes()); - } - }, { - key: 'formatLeadingZero', - value: function formatLeadingZero(value) { - return ('0' + value).slice(-2); - } - }], [{ - key: 'addToDate', - value: function addToDate(date) { - var days = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; - var hours = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; - var minutes = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; - var seconds = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0; - - var returnDate = new Date(date.getFullYear(), date.getMonth(), date.getDate() + days, date.getHours() + hours, date.getMinutes() + minutes, date.getSeconds() + seconds, 0); - - return returnDate; - } - }]); - - return DateManager; + }, { + key: "generateHours", + value: function generateHours(start, end, slotDuration) { + // parse start time and cast string to int + var startTime = start.split(':').map(function (item) { + return parseInt(item); + }); + + // parse end time and cast string to int + var endTime = end.split(':').map(function (item) { + return parseInt(item); + }); + + // use a date object to calculate minutes and hours (exact date is not important) + var startDateObject = new Date(); + startDateObject.setHours(startTime[0]); + startDateObject.setMinutes(startTime[1]); + startDateObject.setSeconds(0); + startDateObject.setMilliseconds(0); + + // TODO: handle day change + var endDateObject = new Date(); + endDateObject.setHours(endTime[0]); + endDateObject.setMinutes(endTime[1]); + endDateObject.setSeconds(0); + endDateObject.setMilliseconds(0); + + //console.log(startDateObject); + //console.log(endDateObject); + + var currentDateObject = startDateObject; + + // push first occurence + this.hours.push(currentDateObject); + while (currentDateObject.getTime() < endDateObject.getTime()) { + //console.log(currentDateObject); + currentDateObject = this.constructor.addToDate(currentDateObject, 0, 0, slotDuration, 0); + this.hours.push(currentDateObject); + } + } + }, { + key: "getDayLabel", + value: function getDayLabel(index) { + return this.days[index - 1].toLocaleDateString(); + } + }, { + key: "getDayName", + value: function getDayName(index) { + return this.numberToDay(this.days[index - 1].getDay()); + } + }, { + key: "numberToDay", + value: function numberToDay(number) { + var frDays = ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi']; + return frDays[number]; + } + }, { + key: "getHoursLabel", + value: function getHoursLabel(index) { + return this.formatLeadingZero(this.hours[index].getHours()) + ':' + this.formatLeadingZero(this.hours[index].getMinutes()); + } + }, { + key: "formatLeadingZero", + value: function formatLeadingZero(value) { + return ('0' + value).slice(-2); + } + }], [{ + key: "addToDate", + value: function addToDate(date) { + var days = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + var hours = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; + var minutes = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; + var seconds = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0; + var returnDate = new Date(date.getFullYear(), date.getMonth(), date.getDate() + days, date.getHours() + hours, date.getMinutes() + minutes, date.getSeconds() + seconds, 0); + return returnDate; + } + }]); }(); - module.exports = DateManager; },{}],4:[function(require,module,exports){ "use strict"; -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -var EventDispatcher = function () { - function EventDispatcher(slotDuration) { - _classCallCheck(this, EventDispatcher); - - this.slotDuration = slotDuration; +function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } +function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } +function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } } +function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +var EventDispatcher = /*#__PURE__*/function () { + function EventDispatcher(slotDuration) { + _classCallCheck(this, EventDispatcher); + this.slotDuration = slotDuration; + this.mergedEvents = []; + } + return _createClass(EventDispatcher, [{ + key: "loadEvents", + value: function loadEvents(events) { + var _this = this; + this.events = events; + this.mergedEvents = []; + this.mergeOverlappingEvents(); + this.mergedEvents.forEach(function (event) { + _this.addEvent(event); + }); } - - _createClass(EventDispatcher, [{ - key: 'loadEvents', - value: function loadEvents(events) { - var _this = this; - - this.events = events; - - this.events.forEach(function (event) { - _this.addEvent(event); - }); - } - }, { - key: 'addEvent', - value: function addEvent(event) { - var id = event.date.getTime() + '#' + event.column; - - // TODO: use caching - var cell = document.querySelector('[data-id="' + id + '"]'); - - if (!cell) { - return; - } - - cell.dataset.type = 'event'; - cell.dataset.eventId = event.id; - cell.classList.add('calendar-event'); - cell.classList.remove('calendar-locked'); - - // calulcate rowspan - var slotsToTake = Math.floor(event.duration / this.slotDuration); - - if (slotsToTake >= 1) { - // get coordinate - var cellAdress = cell.dataset.coordinate.split('#'); - // iterate over next cell - var currentRow = cellAdress[0]; - for (var i = 1; i < slotsToTake; i++) { - currentRow++; - - // TODO: use caching - var currentCell = document.querySelector('[data-coordinate="' + currentRow + '#' + cellAdress[1] + '"]'); - //currentCell.style['background-color'] = 'red'; - if (currentCell) { - currentCell.dataset.originId = event.id; - currentCell.style.display = 'none'; - } - } + }, { + key: "mergeOverlappingEvents", + value: function mergeOverlappingEvents() { + var _this2 = this; + // Group events by column + var eventsByColumn = {}; + this.events.forEach(function (event) { + if (!eventsByColumn[event.column]) { + eventsByColumn[event.column] = []; + } + eventsByColumn[event.column].push(event); + }); + + // Detect and merge overlaps within each column + Object.keys(eventsByColumn).forEach(function (column) { + var _this2$mergedEvents; + var columnEvents = eventsByColumn[column]; + columnEvents.sort(function (a, b) { + return a.date - b.date; + }); // Sort by start time + var merged = []; + columnEvents.forEach(function (event) { + if (merged.length === 0) { + // Initialize the first event + event.mergedEvents = [event]; // Start tracking merged events + merged.push(event); + } else { + var lastMergedEvent = merged[merged.length - 1]; + var lastEndTime = new Date(lastMergedEvent.date.getTime() + lastMergedEvent.duration * 60000); + var currentStartTime = event.date; + if (currentStartTime < lastEndTime) { + // Overlap detected - merge events + var earliestStart = lastMergedEvent.date < event.date ? lastMergedEvent.date : event.date; + var latestEnd = new Date(Math.max(lastMergedEvent.date.getTime() + lastMergedEvent.duration * 60000, event.date.getTime() + event.duration * 60000)); + lastMergedEvent.date = earliestStart; + lastMergedEvent.duration = (latestEnd - earliestStart) / 60000; // Duration in minutes + lastMergedEvent.title += " | " + event.title; + + // Append to mergedEvents array + lastMergedEvent.mergedEvents.push(event); + } else { + // Start a new non-overlapping event + event.mergedEvents = [event]; // Initialize mergedEvents for this new event + merged.push(event); } - cell.rowSpan = slotsToTake; - cell.innerHTML = event.title; - - return cell; - } - }]); - - return EventDispatcher; + } + }); + (_this2$mergedEvents = _this2.mergedEvents).push.apply(_this2$mergedEvents, merged); + }); + } + }, { + key: "isOverlapping", + value: function isOverlapping(event1, event2) { + var event1End = event1.date.getTime() + event1.duration * 60000; + var event2Start = event2.date.getTime(); + return event2Start < event1End; + } + }, { + key: "addEvent", + value: function addEvent(event) { + var id = event.date.getTime() + '#' + event.column; + + // TODO: use caching + var cell = document.querySelector('[data-id="' + id + '"]'); + if (!cell) { + return; + } + cell.dataset.type = 'event'; + cell.dataset.eventId = event.id; + cell.classList.add('calendar-event'); + cell.classList.remove('calendar-locked'); + + // calulcate rowspan + var slotsToTake = Math.floor(event.duration / this.slotDuration); + if (slotsToTake >= 1) { + // get coordinate + var cellAdress = cell.dataset.coordinate.split('#'); + // iterate over next cell + var currentRow = cellAdress[0]; + for (var i = 1; i < slotsToTake; i++) { + currentRow++; + + // TODO: use caching + var currentCell = document.querySelector('[data-coordinate="' + currentRow + '#' + cellAdress[1] + '"]'); + //currentCell.style['background-color'] = 'red'; + if (currentCell) { + currentCell.dataset.originId = event.id; + currentCell.style.display = 'none'; + } + } + } + cell.rowSpan = slotsToTake; + cell.innerHTML = event.title; + return cell; + } + }]); }(); - module.exports = EventDispatcher; },{}],5:[function(require,module,exports){ "use strict"; -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -var EventsManager = function EventsManager() { - _classCallCheck(this, EventsManager); - - console.log('New event manager'); -}; - -module.exports = EventsManager; - -},{}],6:[function(require,module,exports){ -"use strict"; - -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -var LockedEventDispatcher = function () { - function LockedEventDispatcher(slotDuration) { - _classCallCheck(this, LockedEventDispatcher); - - this.slotDuration = slotDuration; +function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } +function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } +function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } } +function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +var LockedEventDispatcher = /*#__PURE__*/function () { + function LockedEventDispatcher(slotDuration) { + _classCallCheck(this, LockedEventDispatcher); + this.slotDuration = slotDuration; + } + return _createClass(LockedEventDispatcher, [{ + key: "loadEvents", + value: function loadEvents(lockedEvents) { + var _this = this; + this.lockedEvents = lockedEvents; + this.lockedEvents.forEach(function (lockedEvent) { + _this.addEvent(lockedEvent); + }); } - - _createClass(LockedEventDispatcher, [{ - key: 'loadEvents', - value: function loadEvents(lockedEvents) { - var _this = this; - - this.lockedEvents = lockedEvents; - - this.lockedEvents.forEach(function (lockedEvent) { - _this.addEvent(lockedEvent); - }); - } - }, { - key: 'addEvent', - value: function addEvent(lockedEvent) { - var id = lockedEvent.start.getTime() + '#' + lockedEvent.column; - - // TODO: use caching - var cell = document.querySelector('[data-id="' + id + '"]'); - - if (!cell) { - return; - } - - var duration = (lockedEvent.end.getTime() - lockedEvent.start.getTime()) / 60 / 1000; - var slotsToTake = Math.floor(duration / this.slotDuration); - - if (slotsToTake >= 1) { - // get coordinate - var cellAdress = cell.dataset.coordinate.split('#'); - // iterate over next cell - var currentRow = cellAdress[0]; - for (var i = 0; i < slotsToTake; i++) { - // TODO: use caching - var currentCell = document.querySelector('[data-coordinate="' + currentRow + '#' + cellAdress[1] + '"]'); - if (currentCell) { - currentCell.classList.add('calendar-locked'); - currentCell.dataset.type = 'locked'; - } - currentRow++; - } - } - } - }]); - - return LockedEventDispatcher; + }, { + key: "addEvent", + value: function addEvent(lockedEvent) { + var id = lockedEvent.start.getTime() + '#' + lockedEvent.column; + + // TODO: use caching + var cell = document.querySelector('[data-id="' + id + '"]'); + if (!cell) { + return; + } + var duration = (lockedEvent.end.getTime() - lockedEvent.start.getTime()) / 60 / 1000; + var slotsToTake = Math.floor(duration / this.slotDuration); + if (slotsToTake >= 1) { + // get coordinate + var cellAdress = cell.dataset.coordinate.split('#'); + // iterate over next cell + var currentRow = cellAdress[0]; + for (var i = 0; i < slotsToTake; i++) { + // TODO: use caching + var currentCell = document.querySelector('[data-coordinate="' + currentRow + '#' + cellAdress[1] + '"]'); + if (currentCell) { + currentCell.classList.add('calendar-locked'); + currentCell.dataset.type = 'locked'; + } + currentRow++; + } + } + } + }]); }(); - module.exports = LockedEventDispatcher; -},{}],7:[function(require,module,exports){ -'use strict'; - -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -var UIManager = function () { - function UIManager(target, options, events, dateManager) { - _classCallCheck(this, UIManager); - - this.target = target; - this.options = options; - this.events = events; - this.dateManager = dateManager; - this.currentFooterCallback = null; - this.ui = { - footer: this._buildFooter() - }; +},{}],6:[function(require,module,exports){ +"use strict"; - this.listener = { - footerBtn: null - }; +function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } +function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } +function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } } +function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +var UIManager = /*#__PURE__*/function () { + function UIManager(target, options, events, dateManager) { + _classCallCheck(this, UIManager); + this.target = target; + this.options = options; + this.events = events; + this.dateManager = dateManager; + this.currentFooterCallback = null; + this.ui = { + footer: this._buildFooter() + }; + this.listener = { + footerBtn: null + }; + } + return _createClass(UIManager, [{ + key: "clean", + value: function clean() { + // clean footer + var footer = document.querySelector('.calendar-mode'); + if (footer) { + footer.parentNode.removeChild(footer); + } + + // clean table + if (this.target.querySelector('table')) { + this.target.removeChild(this.target.querySelector('table')); + } + this.target.innerHTML = ''; } - - _createClass(UIManager, [{ - key: 'clean', - value: function clean() { - // clean footer - var footer = document.querySelector('.calendar-mode'); - - if (footer) { - footer.parentNode.removeChild(footer); - } - - // clean table - if (this.target.querySelector('table')) { - this.target.removeChild(this.target.querySelector('table')); - } - this.target.innerHTML = ''; - } - }, { - key: 'build', - value: function build() { - - this.clean(); - - var table = this._buildTable(); - var dayLabels = this._buildDayLabel(); - var header = this._buildHeader(); - var body = this._buildDays(); - var footer = this._buildFooter(); - - header.appendChild(dayLabels); - - var bulkActions = this._buildBulkActions(); - header.appendChild(bulkActions); - - table.appendChild(header); - table.appendChild(body); - - this.target.appendChild(table); - - // append footer - document.body.append(this.ui.footer.wrapper); - } - }, { - key: 'showFooter', - value: function showFooter(text) { - var _this = this; - - var btnText = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'annuler'; - var callback = arguments[2]; - - this.currentFooterCallback = callback; - - this.ui.footer.message.innerHTML = text; - setTimeout(function () { - _this.ui.footer.wrapper.classList.add('calendar-mode--active'); - }, 10); - - this.ui.footer.btn.innerText = btnText; - } - }, { - key: 'hideFooter', - value: function hideFooter() { - this.ui.footer.wrapper.classList.remove('calendar-mode--active'); - } - }, { - key: '_buildTable', - value: function _buildTable() { - var table = document.createElement('table'); - table.classList.add(this.options.cssClass); - return table; - } - }, { - key: '_buildHeader', - value: function _buildHeader() { - return document.createElement('thead'); - } - }, { - key: '_buildDayLabel', - value: function _buildDayLabel() { - var daysLine = document.createElement('tr'); - - var firstIteration = true; - for (var i = 0; i <= this.options.numberOfDays; i++) { - var th = document.createElement('th'); - if (firstIteration) { - firstIteration = false; - th.innerHTML = 'Heure'; - } else { - th.innerHTML = this.dateManager.getDayName(i) + '
' + this.dateManager.getDayLabel(i); - - th.colSpan = this.options.columnsPerDay; - } - daysLine.appendChild(th); - } - - return daysLine; - } - }, { - key: '_buildBulkActions', - value: function _buildBulkActions() { - - var bulkActionsLine = document.createElement('tr'); - - var firstIteration = true; - - for (var i = 0; i <= this.options.columnsPerDay * this.options.numberOfDays; i++) { - var th = document.createElement('th'); - - if (firstIteration) { - firstIteration = false; - th.innerHTML = ''; - th.classList.add('calendar-bulk'); - } else { - var button = document.createElement('button'); - button.innerHTML = UIManager.getIcon('locked'); - button.dataset.col = i; - button.dataset.bulk = 'locked'; - - th.classList.add('calendar-bulk'); - th.appendChild(button); - } - - bulkActionsLine.appendChild(th); + }, { + key: "build", + value: function build() { + this.clean(); + var table = this._buildTable(); + var dayLabels = this._buildDayLabel(); + var header = this._buildHeader(); + var body = this._buildDays(); + var footer = this._buildFooter(); + header.appendChild(dayLabels); + var bulkActions = this._buildBulkActions(); + header.appendChild(bulkActions); + table.appendChild(header); + table.appendChild(body); + this.target.appendChild(table); + + // append footer + document.body.append(this.ui.footer.wrapper); + } + }, { + key: "showFooter", + value: function showFooter(text) { + var _this = this; + var btnText = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'annuler'; + var callback = arguments.length > 2 ? arguments[2] : undefined; + this.currentFooterCallback = callback; + this.ui.footer.message.innerHTML = text; + setTimeout(function () { + _this.ui.footer.wrapper.classList.add('calendar-mode--active'); + }, 10); + this.ui.footer.btn.innerText = btnText; + } + }, { + key: "hideFooter", + value: function hideFooter() { + this.ui.footer.wrapper.classList.remove('calendar-mode--active'); + } + }, { + key: "_buildTable", + value: function _buildTable() { + var table = document.createElement('table'); + table.classList.add(this.options.cssClass); + return table; + } + }, { + key: "_buildHeader", + value: function _buildHeader() { + return document.createElement('thead'); + } + }, { + key: "_buildDayLabel", + value: function _buildDayLabel() { + var daysLine = document.createElement('tr'); + var firstIteration = true; + for (var i = 0; i <= this.options.numberOfDays; i++) { + var th = document.createElement('th'); + if (firstIteration) { + firstIteration = false; + th.innerHTML = 'Heure'; + } else { + th.innerHTML = this.dateManager.getDayName(i) + '
' + this.dateManager.getDayLabel(i); + th.colSpan = this.options.columnsPerDay; + } + daysLine.appendChild(th); + } + return daysLine; + } + }, { + key: "_buildBulkActions", + value: function _buildBulkActions() { + var bulkActionsLine = document.createElement('tr'); + var firstIteration = true; + for (var i = 0; i <= this.options.columnsPerDay * this.options.numberOfDays; i++) { + var th = document.createElement('th'); + if (firstIteration) { + firstIteration = false; + th.innerHTML = ''; + th.classList.add('calendar-bulk'); + } else { + var button = document.createElement('button'); + button.innerHTML = UIManager.getIcon('locked'); + button.dataset.col = i; + button.dataset.bulk = 'locked'; + th.classList.add('calendar-bulk'); + th.appendChild(button); + } + bulkActionsLine.appendChild(th); + } + return bulkActionsLine; + } + }, { + key: "_buildDays", + value: function _buildDays() { + var _this2 = this; + var numberOfcol = this.options.columnsPerDay * this.options.numberOfDays + 1; + var tbody = document.createElement('tbody'); + this.dateManager.hours.forEach(function (el, index) { + var line = document.createElement('tr'); + var firstIteration = true; + for (var j = 1; j <= numberOfcol; j++) { + var td = document.createElement('td'); + + // hours label + if (firstIteration) { + td.innerHTML = _this2.dateManager.getHoursLabel(index); + firstIteration = false; + } else { + var column = j - 1; + var collId = _this2.getCollId(column); + var cellTimestamp = _this2.getCellTimestamp(index, column); + td.dataset.id = cellTimestamp + '#' + collId; + td.dataset.coordinate = _this2.getCellCoordinate(index, j - 1); + if (_this2.options.isCellBlocked(cellTimestamp)) { + td.classList.add('calendar-locked'); + td.dataset.type = 'locked'; + } else if (_this2.options.isCellSplited instanceof Function && _this2.options.isCellSplited(cellTimestamp)) { + td.classList.add('calendar-splited'); + td.dataset.type = 'splited'; } - return bulkActionsLine; - } - }, { - key: '_buildDays', - value: function _buildDays() { - var _this2 = this; - - var numberOfcol = this.options.columnsPerDay * this.options.numberOfDays + 1; - var tbody = document.createElement('tbody'); - - this.dateManager.hours.forEach(function (el, index) { - var line = document.createElement('tr'); - - var firstIteration = true; - for (var j = 1; j <= numberOfcol; j++) { - var td = document.createElement('td'); - - // hours label - if (firstIteration) { - td.innerHTML = _this2.dateManager.getHoursLabel(index); - firstIteration = false; - } else { - var column = j - 1; - var collId = _this2.getCollId(column); - var cellTimestamp = _this2.getCellTimestamp(index, column); - - td.dataset.id = cellTimestamp + '#' + collId; - td.dataset.coordinate = _this2.getCellCoordinate(index, j - 1); - - if (_this2.options.isCellBlocked(cellTimestamp)) { - td.classList.add('calendar-locked'); - td.dataset.type = 'locked'; - } else if (_this2.options.isCellSplited instanceof Function && _this2.options.isCellSplited(cellTimestamp)) { - td.classList.add('calendar-splited'); - td.dataset.type = 'splited'; - } - - //td.innerHTML = this.getCellId(index, j - 1); - //td.innerHTML = this.dateManager.hours[index]; - //td.innerHTML = this.getCellCoordinate(index, j - 1); - } - - line.appendChild(td); - } - - tbody.appendChild(line); - }); - - return tbody; - } - }, { - key: '_buildFooter', - value: function _buildFooter(text) { - var footer = document.createElement('div'); - footer.classList.add('calendar-mode'); - footer.innerHTML = ''; - - var message = document.createElement('span'); - message.innerHTML = ''; - - var footerBtn = document.createElement('button'); - footerBtn.innerText = 'Annuler'; - - footer.appendChild(message); - footer.appendChild(footerBtn); - - return { - message: message, - wrapper: footer, - btn: footerBtn - }; - } - }, { - key: 'getCellTimestamp', - value: function getCellTimestamp(index, column) { - var day = Math.ceil(column / this.options.columnsPerDay); - - var date = this.dateManager.days[day - 1]; - date.setHours(this.dateManager.hours[index].getHours()); - date.setMinutes(this.dateManager.hours[index].getMinutes()); - date.setSeconds(0); - date.setMilliseconds(0); - - return date.getTime(); - } - }, { - key: 'getCollId', - value: function getCollId(column) { - return column % this.options.columnsPerDay == 0 ? this.options.columnsPerDay : column % this.options.columnsPerDay; - } - }, { - key: 'getCellCoordinate', - value: function getCellCoordinate(index, column) { - return index + 1 + '#' + column; - } - }], [{ - key: 'getIcon', - value: function getIcon(icon) { - switch (icon) { - case 'locked': - return '\n '; - break; - case 'unlocked': - return ''; - break; - default: - } + //td.innerHTML = this.getCellId(index, j - 1); + //td.innerHTML = this.dateManager.hours[index]; + //td.innerHTML = this.getCellCoordinate(index, j - 1); + } + line.appendChild(td); } - }]); - - return UIManager; + tbody.appendChild(line); + }); + return tbody; + } + }, { + key: "_buildFooter", + value: function _buildFooter(text) { + var footer = document.createElement('div'); + footer.classList.add('calendar-mode'); + footer.innerHTML = ''; + var message = document.createElement('span'); + message.innerHTML = ''; + var footerBtn = document.createElement('button'); + footerBtn.innerText = 'Annuler'; + footer.appendChild(message); + footer.appendChild(footerBtn); + return { + message: message, + wrapper: footer, + btn: footerBtn + }; + } + }, { + key: "getCellTimestamp", + value: function getCellTimestamp(index, column) { + var day = Math.ceil(column / this.options.columnsPerDay); + var date = this.dateManager.days[day - 1]; + date.setHours(this.dateManager.hours[index].getHours()); + date.setMinutes(this.dateManager.hours[index].getMinutes()); + date.setSeconds(0); + date.setMilliseconds(0); + return date.getTime(); + } + }, { + key: "getCollId", + value: function getCollId(column) { + return column % this.options.columnsPerDay == 0 ? this.options.columnsPerDay : column % this.options.columnsPerDay; + } + }, { + key: "getCellCoordinate", + value: function getCellCoordinate(index, column) { + return index + 1 + '#' + column; + } + }], [{ + key: "getIcon", + value: function getIcon(icon) { + switch (icon) { + case 'locked': + return "\n "; + break; + case 'unlocked': + return ''; + break; + default: + } + } + }]); }(); - module.exports = UIManager; },{}]},{},[1])(1) diff --git a/dist/index.html b/dist/index.html index dacc993..ecd45d3 100644 --- a/dist/index.html +++ b/dist/index.html @@ -76,25 +76,32 @@ title: 'Event 1', date: new Date(today.getFullYear(), today.getMonth(), today.getDate(), 10, 0, 0, 0), column: 2, - duration: 80 + duration: 70 }, { id: 2, title: 'Event 2', - date: new Date(today.getFullYear(), today.getMonth(), today.getDate(), 9, 0, 0, 0), - column: 1, + date: new Date(today.getFullYear(), today.getMonth(), today.getDate(), 11, 0, 0, 0), + column: 2, duration: 40 }, { id: 3, title: 'Event 3', - date: new Date(tomorow.getFullYear(), tomorow.getMonth(), tomorow.getDate(), 12, 0, 0, 0), + date: new Date(today.getFullYear(), today.getMonth(), today.getDate(), 9, 0, 0, 0), column: 1, - duration: 69 + duration: 40 }, { id: 4, title: 'Event 4', + date: new Date(tomorow.getFullYear(), tomorow.getMonth(), tomorow.getDate(), 12, 0, 0, 0), + column: 1, + duration: 69 + }, + { + id: 5, + title: 'Event 5', date: new Date(tomorow.getFullYear(), tomorow.getMonth(), tomorow.getDate(), 10, 0, 0, 0), column: 3, duration: 120 @@ -135,9 +142,10 @@ isCellSplited: function(timestamp) { return splitedSlots.includes(timestamp); }, - onEventClicked: function(event) { - alert(event.id); - console.log(event) + onEventClicked: (events) => { + const eventIds = events.map(event => event.id); + alert(`Clicked events IDs: ${eventIds.join(', ')}`); + console.log(events); }, onPeriodChange: function(start, end) { //console.log(start + ' ->', end); diff --git a/src/js/calendar.js b/src/js/calendar.js index 1f8f8d4..0c5d317 100644 --- a/src/js/calendar.js +++ b/src/js/calendar.js @@ -2,7 +2,6 @@ const DateManager = require('./dateManager'); const UIManager = require('./uiManager'); -const EventsManager = require('./eventsManager'); const CellMatrix = require('./cellMatrix'); const EventDispatcher = require('./eventDispatcher'); const LockedEventDispatcher = require('./lockedEventDispatcher'); @@ -18,6 +17,7 @@ class Calendar { constructor(target, options) { this.target = document.querySelector(target); this.options = options; + this.mergedEvents = []; this.mode = { current: VIEW_MODE, ADD_MODE: { @@ -67,17 +67,21 @@ class Calendar { loadEvents(events, blockedEvents) { this.events = events; this.blockedEvents = blockedEvents; - + // event dispatcher this.eventDispatcher = new EventDispatcher(this.options.slotDuration); this.eventDispatcher.loadEvents(this.events); - + + // assign merged events to the calendar instance + this.mergedEvents = this.eventDispatcher.mergedEvents; + // lockedEvent dispatcher this.lockedEventDispatcher = new LockedEventDispatcher(this.options.slotDuration); this.lockedEventDispatcher.loadEvents(this.blockedEvents); - + this._bindEvents(); } + addEvent(event) { let cell = this.eventDispatcher.addEvent(event); @@ -248,10 +252,11 @@ class Calendar { _attachClickEvent(el) { el.addEventListener('click', (event) => { event.stopPropagation(); - if (this.mode.current == VIEW_MODE) { - this.events.forEach((e) => { - if(e.id == event.target.dataset.eventId) { - this.options.onEventClicked(e); + if (this.mode.current === VIEW_MODE) { + this.mergedEvents.forEach((e) => { + if (e.id == event.target.dataset.eventId) { + const baseEvents = e.mergedEvents || [e]; // Retrieve original events + this.options.onEventClicked(baseEvents); // Pass the array of merged events } }); } diff --git a/src/js/eventDispatcher.js b/src/js/eventDispatcher.js index 50fd583..e02c17d 100644 --- a/src/js/eventDispatcher.js +++ b/src/js/eventDispatcher.js @@ -4,18 +4,80 @@ class EventDispatcher { constructor(slotDuration) { this.slotDuration = slotDuration; + this.mergedEvents = []; } loadEvents(events) { - this.events = events; - - this.events.forEach((event) => { + this.mergedEvents = []; + this.mergeOverlappingEvents(); + this.mergedEvents.forEach(event => { this.addEvent(event); }); + } + + mergeOverlappingEvents() { + // Group events by column + const eventsByColumn = {}; + this.events.forEach(event => { + if (!eventsByColumn[event.column]) { + eventsByColumn[event.column] = []; + } + eventsByColumn[event.column].push(event); + }); + + // Detect and merge overlaps within each column + Object.keys(eventsByColumn).forEach(column => { + const columnEvents = eventsByColumn[column]; + columnEvents.sort((a, b) => a.date - b.date); // Sort by start time + let merged = []; + + columnEvents.forEach(event => { + if (merged.length === 0) { + // Initialize the first event + event.mergedEvents = [event]; // Start tracking merged events + merged.push(event); + } else { + const lastMergedEvent = merged[merged.length - 1]; + const lastEndTime = new Date(lastMergedEvent.date.getTime() + lastMergedEvent.duration * 60000); + const currentStartTime = event.date; + + if (currentStartTime < lastEndTime) { + // Overlap detected - merge events + const earliestStart = lastMergedEvent.date < event.date ? lastMergedEvent.date : event.date; + const latestEnd = new Date( + Math.max( + lastMergedEvent.date.getTime() + lastMergedEvent.duration * 60000, + event.date.getTime() + event.duration * 60000 + ) + ); + + lastMergedEvent.date = earliestStart; + lastMergedEvent.duration = (latestEnd - earliestStart) / 60000; // Duration in minutes + lastMergedEvent.title += " | " + event.title; + + // Append to mergedEvents array + lastMergedEvent.mergedEvents.push(event); + } else { + // Start a new non-overlapping event + event.mergedEvents = [event]; // Initialize mergedEvents for this new event + merged.push(event); + } + } + }); + + this.mergedEvents.push(...merged); + }); + } + + isOverlapping(event1, event2) { + const event1End = event1.date.getTime() + event1.duration * 60000; + const event2Start = event2.date.getTime(); + return event2Start < event1End; } + addEvent(event) { let id = event.date.getTime() + '#' + event.column; diff --git a/src/js/eventsManager.js b/src/js/eventsManager.js deleted file mode 100644 index bfa92a2..0000000 --- a/src/js/eventsManager.js +++ /dev/null @@ -1,11 +0,0 @@ -"use strict"; - -class EventsManager { - - constructor() { - console.log('New event manager'); - } - -} - -module.exports = EventsManager;