Fullcalendarで現在の時間にスクロールし、スクロール位置を保持する(timeGridWeek, timeGridDay)

技術備忘録

FullcalendarでtimeGridViewを使用するとデフォルトではscrollTimeによって6:00に自動的にスクロールされてしまいます。scrollTimeで値をセットしたとしても、別の位置にスクロールした後にprevボタンやbackボタンで表示範囲を切り替えると、スクロールした分は無視され、セットした位置で再度表示されてしまいます。

それらの不自由さを解決するために、All-Day Render Hooksを用いて以下の実装をします。

  • 初回アクセス時は現在時刻までスクロール
  • 表示範囲などの切り替え時には自動的にスクロールされないようにscrollTimeを無効化する

スポンサーリンク

allDayDidMountで実装

  • allDayDidMount内で初回は現在時刻にスクロール位置を取得
  • scrollTime={false}でscrollTimeを無効化
import React, { useEffect, useState, createRef } from 'react';
import moment from 'moment';
import FullCalendar from '@fullcalendar/react';
import dayGridPlugin from '@fullcalendar/daygrid';
import timeGridPlugin from '@fullcalendar/timegrid';
import interactionPlugin from '@fullcalendar/interaction';
import '@fullcalendar/daygrid/main.css';
import '@fullcalendar/timegrid/main.css';
import '../css/custom_fullcalendar.css';
const week = const week = ['(日)', '(月)', '(火)', '(水)', '(木)', '(金)', '(土)']
// 現在時刻が何番目のslotに当たるか計算(単位はslotDurationで設定)
const nowSlotNumber = moment().hours()*4 + Math.floor(moment().minutes()/15) // 15分slot

const Calendar = (props) => {
    const [events, setEvents] = useState(props.events)
    const [scrollPosition, setScrollPosition] = useState(null)
    const calendarRef = createRef()
    const allDayDidMount = () => {
        // それまでのスクロール位置を取得
        let sp = document.querySelector('.fc-timegrid-slots').parentNode?.parentNode?.scrollTop
        if(scrollPosition===null){ // 初回アクセス時は現在時刻にスクロール位置をセット
            sp = 15*nowSlotNumber
            // スクロール実行
            setTimeout(() => document.querySelector('.fc-timegrid-slots').parentNode.parentNode.scrollTop = sp, 0)
        }
        setScrollPosition(sp) // 一応スクロール位置を保存
    }

    useEffect(()=>{
        let calendarApi = calendarRef.current.getApi();
        calendarApi.changeView(props.viewStyle, props.start.toDate())
        setEvents(props.events)
        // 月表示に切り替えた際にscroll位置を初期化
        if (props.viewStyle==='dayGridMonth'){
            setScrollPosition(null)
        }
    }, [props.viewStyle, props.events])

    return (
        <FullCalendar
            ref={calendarRef}
            plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin]}
            timezone={'Asia/Tokyo'}
            views={{
                timeGridWeek: {
                    dayHeaderFormat: function (date) {
                        return date.date.day+week[date.date.marker.getDay()]
                    }
                },
                timeGridDay: {
                    dayHeaderFormat: function (date) {
                        return date.date.day+week[date.date.marker.getDay()]
                    }
                }
            }}
            slotLabelFormat={{
                hour: 'numeric',
                minute: '2-digit',
                omitZeroMinute: false,
                meridiem: 'short'
            }}
            locale={'ja'}
            firstDay={1}
            initialView={props.viewStyle}
            initialDate={props.start.toDate()}
            allDayText={''}
            fixedWeekCount={false}
            events={events}
            nowIndicator={true}
            scrollTime={false} // scrollTimeを無効にする
            allDayDidMount={allDayDidMount} // scrollの設定をallDayDidMountで行う
            timeFormat={'H:mm'}
            slotDuration={'00:15'}
            slotLabelInterval={'01:00'}
            height={props.windowSize.height}
        />
    )
}
初回アクセス時
スクロール後に表示範囲を切り替え

コメント

タイトルとURLをコピーしました