<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 );
    }
  }
    
};

// DateObject to YYYY-MM-DD
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('-');
}

External CSS

  1. https://cdn.jsdelivr.net/npm/fullcalendar@5.11.2/main.min.css
  2. https://fonts.googleapis.com/icon?family=Material+Icons

External JavaScript

  1. https://cdn.jsdelivr.net/npm/fullcalendar@5.11.2/main.min.js