$(() => {
$('#scheduler').dxScheduler({
dataSource: data,
views: ['workWeek', 'month'],
currentView: 'workWeek',
currentDate: new Date(2021, 3, 27),
firstDayOfWeek: 0,
startDayHour: 9,
endDayHour: 19,
showAllDayPanel: false,
height: 730,
dataCellTemplate(itemData, itemIndex, itemElement) {
const date = itemData.startDate;
const element = $('<div />');
if (isDisableDate(date)) {
element.addClass('disable-date');
} else if (isDinner(date)) {
element.addClass('dinner');
}
const isMonth = this.option('currentView') === 'month';
if (isMonth) {
element.text(date.getDate());
element.addClass('dx-scheduler-date-table-cell-text');
}
return itemElement.append(element);
},
dateCellTemplate(itemData, itemIndex, itemElement) {
const element = $(`<div>${itemData.text}</div>`);
const isMonth = this.option('currentView') === 'month';
const isDisabled = isMonth
? isWeekend(itemData.date)
: isDisableDate(itemData.date);
if (isDisabled) {
element.addClass('disable-date');
}
return itemElement.append(element);
},
timeCellTemplate(itemData, itemIndex, itemElement) {
const element = $(`<div>${itemData.text}</div>`);
const { date } = itemData;
if (isDinner(date)) {
element.addClass('dinner');
}
if (hasCoffeeCupIcon(date)) {
element.append('<div class="cafe" />');
}
return itemElement.append(element);
},
onAppointmentFormOpening(e) {
const startDate = new Date(e.appointmentData.startDate);
if (!isValidAppointmentDate(startDate)) {
e.cancel = true;
notifyDisableDate();
}
applyDisableDatesToDateEditors(e.form);
},
onAppointmentAdding(e) {
if (!isValidAppointment(e.component, e.appointmentData)) {
e.cancel = true;
notifyDisableDate();
}
},
onAppointmentUpdating(e) {
if (!isValidAppointment(e.component, e.newData)) {
e.cancel = true;
notifyDisableDate();
}
},
});
});
const dinnerTime = { from: 12, to: 13 };
const holidays = [
new Date(2021, 3, 29),
new Date(2021, 5, 6),
];
function notifyDisableDate() {
DevExpress.ui.notify('Cannot create or move an appointment/event to disabled time/date regions.', 'warning', 1000);
}
function isValidAppointment(component, appointmentData) {
const startDate = new Date(appointmentData.startDate);
const endDate = new Date(appointmentData.endDate);
const cellDuration = component.option('cellDuration');
return isValidAppointmentInterval(startDate, endDate, cellDuration);
}
function isValidAppointmentInterval(startDate, endDate, cellDuration) {
const edgeEndDate = new Date(endDate.getTime() - 1);
if (!isValidAppointmentDate(edgeEndDate)) {
return false;
}
const durationInMs = cellDuration * 60 * 1000;
const date = startDate;
while (date <= endDate) {
if (!isValidAppointmentDate(date)) {
return false;
}
const newDateTime = date.getTime() + durationInMs - 1;
date.setTime(newDateTime);
}
return true;
}
function isValidAppointmentDate(date) {
return !isHoliday(date) && !isDinner(date) && !isWeekend(date);
}
function isHoliday(date) {
const localeDate = date.toLocaleDateString();
return holidays.filter((holiday) => holiday.toLocaleDateString() === localeDate).length > 0;
}
function isWeekend(date) {
const day = date.getDay();
return day === 0 || day === 6;
}
function isDisableDate(date) {
return isHoliday(date) || isWeekend(date);
}
function isDinner(date) {
const hours = date.getHours();
return hours >= dinnerTime.from && hours < dinnerTime.to;
}
function hasCoffeeCupIcon(date) {
const hours = date.getHours();
const minutes = date.getMinutes();
return hours === dinnerTime.from && minutes === 0;
}
function applyDisableDatesToDateEditors(form) {
const startDateEditor = form.getEditor('startDate');
startDateEditor.option('disabledDates', holidays);
const endDateEditor = form.getEditor('endDate');
endDateEditor.option('disabledDates', holidays);
}
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<title>DevExtreme Demo</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>window.jQuery || document.write(decodeURIComponent('%3Cscript src="js/jquery.min.js"%3E%3C/script%3E'))</script>
<link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/24.1.7/css/dx.light.css" />
<script src="js/dx.all.js"></script>
<script src="data.js"></script>
<link rel="stylesheet" type="text/css" href="styles.css" />
<script src="index.js"></script>
</head>
<body class="dx-viewport">
<div class="demo-container">
<div id="scheduler"></div>
</div>
</body>
</html>
@-moz-document url-prefix() {
.dx-scheduler-work-space-month .dx-scheduler-date-table-cell {
position: relative;
}
.dx-scheduler-work-space-month .dx-scheduler-date-table-cell .disable-date {
position: absolute;
width: 100%;
height: 100%;
}
}
.disable-date,
.dinner {
height: 100%;
width: 100%;
}
.disable-date {
background-image:
repeating-linear-gradient(
135deg,
rgba(244, 67, 54, 0.1),
rgba(244, 67, 54, 0.1) 4px,
transparent 4px,
transparent 9px
);
color: #9b6467;
}
.dx-scheduler-header-panel-cell .disable-date {
display: flex;
flex-direction: column;
justify-content: center;
}
.dx-theme-fluent .dx-scheduler-header-panel-cell .disable-date,
.dx-theme-material .dx-scheduler-header-panel-cell .disable-date {
flex-direction: row;
align-items: flex-end;
justify-content: initial;
}
.dinner {
background: rgba(255, 193, 7, 0.2);
}
.dx-scheduler-time-panel-cell .dinner {
color: rgba(255, 193, 7);
font-weight: 400;
background: transparent;
}
.dx-draggable {
cursor: auto;
}
td.dx-scheduler-time-panel-cell .dinner .cafe {
height: 200%;
width: 100%;
left: 50%;
-webkit-mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M20 3H4v10c0 2.21 1.79 4 4 4h6c2.21 0 4-1.79 4-4v-3h2c1.11 0 2-.9 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3zM4 19h16v2H4z"/></svg>');
-webkit-mask-repeat: no-repeat;
-webkit-mask-position-y: 50%;
-webkit-mask-position-x: 100%;
margin-top: -4px;
background-color: #ffc107;
}
.dx-scheduler-date-table-cell {
padding: 0;
opacity: 1;
}
@media all and (-ms-high-contrast: none) {
td.dx-scheduler-time-panel-cell .dinner .cafe {
background-color: transparent;
}
}
const data = [
{
text: 'Website Re-Design Plan',
startDate: new Date(2021, 3, 26, 9, 30),
endDate: new Date(2021, 3, 26, 11, 30),
},
{
text: 'Install New Router in Dev Room',
startDate: new Date(2021, 3, 26, 13),
endDate: new Date(2021, 3, 26, 14),
},
{
text: 'Approve Personal Computer Upgrade Plan',
startDate: new Date(2021, 3, 27, 10),
endDate: new Date(2021, 3, 27, 11),
},
{
text: 'Final Budget Review',
startDate: new Date(2021, 3, 27, 13, 30),
endDate: new Date(2021, 3, 27, 15),
},
{
text: 'New Brochures',
startDate: new Date(2021, 3, 26, 15),
endDate: new Date(2021, 3, 26, 16, 15),
},
{
text: 'Install New Database',
startDate: new Date(2021, 3, 28, 9, 45),
endDate: new Date(2021, 3, 28, 12),
},
{
text: 'Approve New Online Marketing Strategy',
startDate: new Date(2021, 3, 28, 14, 30),
endDate: new Date(2021, 3, 28, 16, 30),
},
{
text: 'Upgrade Personal Computers',
startDate: new Date(2021, 3, 27, 15, 30),
endDate: new Date(2021, 3, 27, 16, 45),
},
{
text: 'Prepare 2021 Marketing Plan',
startDate: new Date(2021, 4, 3, 13),
endDate: new Date(2021, 4, 3, 15),
},
{
text: 'Brochure Design Review',
startDate: new Date(2021, 4, 4, 15, 30),
endDate: new Date(2021, 4, 5),
},
{
text: 'Create Icons for Website',
startDate: new Date(2021, 3, 30, 10),
endDate: new Date(2021, 3, 30, 12),
},
{
text: 'Upgrade Server Hardware',
startDate: new Date(2021, 3, 30, 16, 30),
endDate: new Date(2021, 3, 30, 18),
},
{
text: 'Submit New Website Design',
startDate: new Date(2021, 4, 5, 10),
endDate: new Date(2021, 4, 5, 11, 30),
},
{
text: 'Launch New Website',
startDate: new Date(2021, 3, 30, 14, 30),
endDate: new Date(2021, 3, 30, 16, 10),
},
];