<div id="calendar"></div>
<div class="modal" id="modal-template">
<div class="modal__title">
<label>タイトル: <input type="text" id="title"></label>
</div>
<div class="modal__color">
<label>背景色: <input type="color" id="color"></label>
</div>
<div class="modal__times">
<label>開始: <input type="date" id="start"></label>
<label>終了: <input type="date" id="end"></label>
</div>
<div class="modal-action-buttons">
<button class="modal-action-buttons__button save" id="save">保存</button>
<button class="modal-action-buttons__button delete" id="delete">削除</button>
<button class="modal-action-buttons close material-icons" id="cancel">cancel</button>
</div>
</div>
.modal {
display: none;
flex-direction: column;
align-items: center;
padding: 1em;
max-width: 250px;
position: relative;
background-color: #fff;
border: 3px solid #f0f0f0;
border-radius: 4px;
&__title {
margin-bottom: .5em;
align-self: flex-start;
input[type="text"] {
width: 100px;
}
label {
display: flex;
align-items: center;
}
}
&__color {
margin-bottom: .5em;
align-self: flex-start;
}
&__times {
margin-bottom: 2em;
& > * {
margin-bottom: .5em;
&:last-child {
margin-bottom: 0;
}
}
label {
display: flex;
align-items: center;
}
}
}
.modal-action-buttons {
&__button {
border: 2px solid #000;
padding: .5em 1em;
border-radius: 4px;
background-color: transparent;
cursor: pointer;
transition: .25s;
}
}
@mixin button( $color ) {
color: $color;
border-color: $color;
&:hover {
color: #fff;
background-color: $color;
}
}
.save {
@include button( #2c75ff );
}
.delete {
@include button( #dd1155 );
}
.close {
cursor: pointer;
border: none;
background-color: transparent;
position: absolute;
top: 0;
right: 0;
transform: translate( 50%, -50% );
transition: .15s;
&:hover {
opacity: .85;
background-color: transparent ;
}
}
input[type="date"] {
padding: .5em 1em;
border: 1px solid #ddd;
border-radius: 4px;
font-weight: bold;
}
View Compiled
let calendar;
document.addEventListener( 'DOMContentLoaded', () => {
const calendarEl = document.getElementById( 'calendar' );
calendar = new FullCalendar.Calendar( calendarEl, {
initialView: 'dayGridMonth',
locale: 'local',
timeZone: 'local',
eventDisplay: 'block',
displayEventTime: false,
selectable: true,
select: arg => {
initEditModal( arg );
},
eventClick: arg => {
console.log( arg );
initEditModal( arg );
},
} );
calendar.render();
} );
const initEditModal = data => {
removeAlreadyModal();
const defModal = document.getElementById( 'modal-template' );
const modal = defModal.cloneNode( true );
modal.id = 'modal';
setupModalPosition( modal, data.jsEvent );
document.body.appendChild( modal );
if ( data.event === undefined ) {
document.querySelector( '#modal .delete' ).remove();
}
setupModalData( modal, data );
registerEditModalEvent( modal, data );
};
const setupModalPosition = ( modal, e ) => {
modal.style.display = 'flex';
modal.style.position = 'absolute';
modal.style.zIndex = 9999;
const position = calcModalPosition( e );
modal.style.left = `${position.x}px`;
modal.style.top = `${position.y}px`;
};
const calcModalPosition = e => {
const windowWidth = window.outerWidth;
const y = e.pageY + 16;
let x = e.pageX;
if ( e.pageX <= 125 ) {
x = e.pageX;
} else if ( e.pageX > 125 && windowWidth - e.pageX > 125 ) {
x = e.pageX - 125;
} else if ( windowWidth - e.pageX <= 125 ) {
x = e.pageX - 250;
}
return {
x: x,
y: y
};
};
const removeAlreadyModal = () => {
const modal = document.getElementById( 'modal' );
if ( modal ) {
modal.remove();
}
};
const registerEditModalEvent = ( modal, arg ) => {
const start = modal.querySelector( '#start' );
const end = modal.querySelector( '#end' );
const title = modal.querySelector( '#title' );
const color = modal.querySelector( '#color' );
const saveButton = modal.querySelector( '#save' );
if ( saveButton ) {
saveButton.addEventListener( 'click', e => {
e.preventDefault();
if ( arg.event !== undefined ) {
const endStrings = end.value && start.value !== end.value ? end.value.split( '-' ) : start.value.split( '-' );
const endDate = new Date( endStrings[0], parseInt( endStrings[1] ) - 1, endStrings[2], 23, 59, 59 );
arg.event.setStart( start.value );
arg.event.setEnd( endDate );
arg.event.setProp( 'title', title.value );
arg.event.setProp( 'backgroundColor', color.value );
} else {
const endStrings = end.value && start.value !== end.value ? end.value.split( '-' ) : start.value.split( '-' );
const endDate = new Date( endStrings[0], parseInt( endStrings[1] ) - 1, endStrings[2], 23, 59, 59 );
calendar.addEvent( {
start: start.value,
end: endDate,
title: title.value,
backgroundColor: color.value
} );
}
calendar.unselect();
modal.remove();
} );
}
const cancelButton = modal.querySelector( '#cancel' );
cancelButton.addEventListener( 'click', e => {
e.preventDefault();
calendar.unselect();
modal.remove();
} );
const deleteButton = modal.querySelector( '#delete' );
if ( deleteButton ) {
deleteButton.addEventListener( 'click', e => {
e.preventDefault();
arg.event.remove();
calendar.unselect();
modal.remove();
} );
}
};
const setupModalData = ( modal, data ) => {
const start = modal.querySelector( '#start' );
const end = modal.querySelector( '#end' );
const title = modal.querySelector( '#title' );
const color = modal.querySelector( '#color' );
console.log( data );
if ( data.event !== undefined ) {
start.value = /T/.test( data.event.startStr ) ? data.event.startStr.split( 'T' )[0] : data.event.startStr;
end.value = /T/.test( data.event.endStr ) ? data.event.endStr.split( 'T' )[0] : data.event.endStr;
title.value = data.event.title;
color.value = data.event.backgroundColor;
} else {
start.value = data.startStr;
const diffTime = Math.abs( data.end - data.start );
const diffDays = Math.ceil( diffTime / ( 1000 * 60 * 60 * 24 ) );
if ( 1 < diffDays ) {
const endDate = data.end;
endDate.setDate( endDate.getDate() - 1 );
end.value = formatDate( endDate );
}
}
};
function formatDate(date) {
var d = new Date(date),
month = '' + (d.getMonth() + 1),
day = '' + d.getDate(),
year = d.getFullYear();
if (month.length < 2)
month = '0' + month;
if (day.length < 2)
day = '0' + day;
return [year, month, day].join('-');
}