[#668] Tablet view (#716)

* draft for tablet view
* adapt sidebar to tablet view
* split menubar to handle tablet and desktop separatly
* split sidebar to handle tablet and desktop separatly
* prettier day navigationin tablet view
* added missing translations
* coderabbit comments
* fix UI of view switcher on tablet, remove hard text
* fix style of selected view option
* fix wrong I18n key
* extracted menubar recurring components to own files
* adjust test case of calendar component
* refactor function change calendar view

Co-authored-by: Camille Moussu <cmoussu@linagora.com>
Co-authored-by: lethemanh <lethemanh@lethemanhs-MacBook-Pro.local>
This commit is contained in:
Camille Moussu
2026-04-03 08:49:37 +02:00
committed by GitHub
parent c1d3cf5f3d
commit 4e57f6b342
36 changed files with 1549 additions and 576 deletions
+76 -76
View File
@@ -5,6 +5,7 @@ import { CalendarEvent } from '@/features/Events/EventsTypes'
import ImportAlert from '@/features/Events/ImportAlert'
import SearchResultsPage from '@/features/Search/SearchResultsPage'
import { setTimeZone } from '@/features/Settings/SettingsSlice'
import { useScreenSizeDetection } from '@/useScreenSizeDetection'
import { setDisplayedDateAndRange } from '@/utils/CalendarRangeManager'
import { extractEventBaseUuid } from '@/utils/extractEventBaseUuid'
import { setSelectedCalendars as setSelectedCalendarsToStorage } from '@/utils/storage/setSelectedCalendars'
@@ -20,7 +21,7 @@ import interactionPlugin from '@fullcalendar/interaction'
import momentTimezonePlugin from '@fullcalendar/moment-timezone'
import FullCalendar from '@fullcalendar/react'
import timeGridPlugin from '@fullcalendar/timegrid'
import { Box, Button, radius } from '@linagora/twake-mui'
import { Fab } from '@linagora/twake-mui'
import AddIcon from '@mui/icons-material/Add'
import moment from 'moment-timezone'
import { MutableRefObject, useEffect, useMemo, useRef, useState } from 'react'
@@ -32,18 +33,17 @@ import { EventErrorHandler } from '../Error/EventErrorHandler'
import { EditModeDialog } from '../Event/EditModeDialog'
import { Menubar, MenubarProps } from '../Menubar/Menubar'
import './Calendar.styl'
import CalendarSelection from './CalendarSelection'
import './CustomCalendar.styl'
import { useCalendarEventHandlers } from './hooks/useCalendarEventHandlers'
import { useCalendarViewHandlers } from './hooks/useCalendarViewHandlers'
import { MiniCalendar } from './MiniCalendar'
import { TempCalendarsInput } from './TempCalendarsInput'
import Sidebar from './Sidebar/SideBar'
import { TimezoneSelector } from './TimezoneSelector'
import {
eventToFullCalendarFormat,
extractEvents,
updateSlotLabelVisibility
} from './utils/calendarUtils'
import { CALENDAR_VIEWS } from './utils/constants'
const localeMap: Record<string, LocaleInput | undefined> = {
fr: frLocale,
@@ -55,16 +55,24 @@ const localeMap: Record<string, LocaleInput | undefined> = {
interface CalendarAppProps {
calendarRef: MutableRefObject<CalendarApi | null>
onDateChange?: (date: Date) => void
onViewChange?: (view: string) => void
onViewChange: (view: string) => void
menubarProps?: MenubarProps
openSidebar: boolean
onCloseSidebar: () => void
setCurrentView: (view: string) => void
currentView: string
}
export default function CalendarApp({
calendarRef,
onDateChange,
onViewChange,
menubarProps
}: CalendarAppProps) {
menubarProps,
openSidebar,
onCloseSidebar,
setCurrentView,
currentView
}: CalendarAppProps): JSX.Element {
const [selectedDate, setSelectedDate] = useState(new Date())
const [debouncedDate, setDebouncedDate] = useState(new Date())
useEffect(() => {
@@ -84,6 +92,9 @@ export default function CalendarApp({
const hideDeclinedEvents = useAppSelector(
state => state.settings.hideDeclinedEvents
)
const { isTablet } = useScreenSizeDetection()
const hiddenDays = useMemo(() => {
if (!hideWorkingDays || !workingDays || workingDays.length === 0) return []
const validWorkingDays = workingDays.filter(d => d >= 0 && d <= 6)
@@ -113,7 +124,6 @@ export default function CalendarApp({
[calendarIdsString]
)
const [currentView, setCurrentView] = useState('timeGridWeek')
const timezone =
useAppSelector(state => state.settings.timeZone) ?? browserDefaultTimeZone
@@ -297,6 +307,20 @@ export default function CalendarApp({
const [tempUsers, setTempUsers] = useState<User[]>([])
const [tempEvent, setTempEvent] = useState<CalendarEvent>({} as CalendarEvent)
useEffect(() => {
if (view !== 'calendar') return
const targetView =
currentView ||
(isTablet ? CALENDAR_VIEWS.timeGridDay : CALENDAR_VIEWS.timeGridWeek)
if (calendarRef.current?.view.type === targetView) return
const id = requestAnimationFrame(() => {
if (calendarRef.current?.view.type !== targetView) {
calendarRef.current?.changeView(targetView)
}
})
return () => cancelAnimationFrame(id)
}, [view, isTablet, currentView, calendarRef])
// Event handlers
const eventHandlers = useCalendarEventHandlers({
setSelectedRange,
@@ -321,7 +345,7 @@ export default function CalendarApp({
calendarRef,
setSelectedDate,
setSelectedMiniDate,
onViewChange,
onViewChange: setCurrentView,
calendars,
tempcalendars,
// Note: To preserve current logic, this will temporarily disable eslint for react-hooks/refs
@@ -341,68 +365,40 @@ export default function CalendarApp({
<main
className={`main-layout calendar-layout ${menubarProps?.isIframe ? ' isInIframe' : ''}`}
>
<Box
className="sidebar"
sx={{
paddingTop: 0,
paddingBottom: 3,
paddingLeft: 3,
paddingRight: 2,
width: '270px'
}}
>
<Box
sx={{
position: 'sticky',
top: 0,
zIndex: 10,
backgroundColor: '#fff',
paddingTop: menubarProps?.isIframe ? '10px' : 3
}}
>
<Button
size="medium"
variant="contained"
fullWidth
onClick={() =>
eventHandlers.handleDateSelect(null as unknown as DateSelectArg)
}
sx={{
borderRadius: radius.lg,
fontSize: '16px',
fontWeight: 500,
lineHeight: 'normal'
}}
>
<AddIcon sx={{ marginRight: 0.5, fontSize: '20px' }} />{' '}
{t('event.createEvent')}
</Button>
</Box>
<MiniCalendar
calendarRef={calendarRef}
selectedDate={selectedMiniDate}
setSelectedMiniDate={setSelectedMiniDate}
/>
<Box sx={{ mb: 3, mt: 2 }}>
<TempCalendarsInput
tempUsers={tempUsers}
setTempUsers={setTempUsers}
handleToggleEventPreview={() => {
eventHandlers.handleDateSelect(null as unknown as DateSelectArg)
}}
/>
</Box>
<div className="calendarList">
<CalendarSelection
selectedCalendars={selectedCalendars}
setSelectedCalendars={setSelectedCalendars}
/>
</div>
</Box>
<Sidebar
open={openSidebar}
onClose={onCloseSidebar}
calendarRef={calendarRef}
isIframe={menubarProps?.isIframe}
onCreateEvent={() => eventHandlers.handleDateSelect(null)}
onViewChange={onViewChange}
selectedMiniDate={selectedMiniDate}
setSelectedMiniDate={setSelectedMiniDate}
selectedCalendars={selectedCalendars}
setSelectedCalendars={setSelectedCalendars}
tempUsers={tempUsers}
setTempUsers={setTempUsers}
currentView={currentView}
/>
<div className="calendar">
<ImportAlert />
{menubarProps?.isIframe && <Menubar {...menubarProps} />}
{isTablet && (
<Fab
color="primary"
aria-label={t('event.createEvent')}
onClick={() => eventHandlers.handleDateSelect(null)}
sx={{
position: 'fixed',
bottom: 24,
right: 24,
zIndex: 20,
borderRadius: '16px'
}}
>
<AddIcon />
</Fab>
)}
{view === 'calendar' && (
<FullCalendar
key={hiddenDays.join(',')}
@@ -417,7 +413,12 @@ export default function CalendarApp({
interactionPlugin,
momentTimezonePlugin
]}
initialView="timeGridWeek"
initialView={
currentView ||
(isTablet
? CALENDAR_VIEWS.timeGridDay
: CALENDAR_VIEWS.timeGridWeek)
}
firstDay={1}
editable={true}
locale={localeMap[lang]}
@@ -448,7 +449,8 @@ export default function CalendarApp({
a.extendedProps.priority - b.extendedProps.priority
}
weekNumbers={
currentView === 'timeGridWeek' || currentView === 'timeGridDay'
currentView === CALENDAR_VIEWS.timeGridWeek ||
currentView === CALENDAR_VIEWS.timeGridDay
}
weekNumberFormat={{ week: 'long' }}
weekNumberContent={arg => {
@@ -474,7 +476,7 @@ export default function CalendarApp({
month: 'short',
timeZone: timezone
})
if (arg.view.type === 'dayGridMonth') {
if (arg.view.type === CALENDAR_VIEWS.dayGridMonth) {
return (
<span
className={`fc-daygrid-day-number ${
@@ -502,7 +504,7 @@ export default function CalendarApp({
calendarRef.current?.getDate() || new Date(arg.start)
setDisplayedDateAndRange(calendarCurrentDate)
if (arg.view.type === 'dayGridMonth') {
if (arg.view.type === CALENDAR_VIEWS.dayGridMonth) {
const start = new Date(arg.start).getTime()
const end = new Date(arg.end).getTime()
const middle = start + (end - start) / 2
@@ -519,9 +521,7 @@ export default function CalendarApp({
}
// Notify parent about view change
if (onViewChange) {
onViewChange(arg.view.type)
}
setCurrentView(arg.view.type)
// Update slot label visibility when view changes
setTimeout(() => {
@@ -543,7 +543,7 @@ export default function CalendarApp({
return (
<div className="fc-daygrid-day-top">
<small>{weekDay}</small>
{arg.view.type !== 'dayGridMonth' && (
{arg.view.type !== CALENDAR_VIEWS.dayGridMonth && (
<span
className={`fc-daygrid-day-number ${arg.isToday ? 'current-date' : ''}`}
>
+44 -3
View File
@@ -1,5 +1,6 @@
import { useAppDispatch, useAppSelector } from '@/app/hooks'
import SettingsPage from '@/features/Settings/SettingsPage'
import { useScreenSizeDetection } from '@/useScreenSizeDetection'
import { getViewRange } from '@/utils/dateUtils'
import type { CalendarApi } from '@fullcalendar/core'
import CozyBridge from 'cozy-external-bridge'
@@ -8,6 +9,8 @@ import { ErrorSnackbar } from '../Error/ErrorSnackbar'
import { refreshCalendars } from '../Event/utils/eventUtils'
import { Menubar, MenubarProps } from '../Menubar/Menubar'
import CalendarApp from './Calendar'
import { CALENDAR_VIEWS } from './utils/constants'
import { setView } from '@/features/Settings/SettingsSlice'
export default function CalendarLayout() {
const calendarRef = useRef<CalendarApi | null>(null)
@@ -16,8 +19,31 @@ export default function CalendarLayout() {
const selectedCalendars = useAppSelector(state => state.calendars.list)
const tempcalendars = useAppSelector(state => state.calendars.templist)
const view = useAppSelector(state => state.settings.view)
const { isTablet } = useScreenSizeDetection()
const [openSidebar, setOpenSideBar] = useState(false)
const [currentDate, setCurrentDate] = useState<Date>(new Date())
const [currentView, setCurrentView] = useState<string>('timeGridWeek')
const [currentView, setCurrentView] = useState<string>(
isTablet ? CALENDAR_VIEWS.timeGridDay : CALENDAR_VIEWS.timeGridWeek
)
useEffect(() => {
const setView = () =>
setCurrentView(prev => {
if (
prev !== CALENDAR_VIEWS.timeGridDay &&
prev !== CALENDAR_VIEWS.timeGridWeek
) {
return prev
}
return isTablet
? CALENDAR_VIEWS.timeGridDay
: CALENDAR_VIEWS.timeGridWeek
})
setView()
}, [isTablet])
const isInIframe = useMemo(() => new CozyBridge().isInIframe(), [])
const handleRefresh = async () => {
@@ -47,8 +73,18 @@ export default function CalendarLayout() {
setCurrentDate(date)
}
const handleViewChange = (view: string) => {
const handleViewChange = (view: string): void => {
if (!calendarRef.current) return
dispatch(setView('calendar'))
calendarRef.current.changeView(view)
// Notify parent about view change
setCurrentView(view)
// Notify parent about date change after view change
const newDate = calendarRef.current.getDate()
handleDateChange(newDate)
}
// Hide topbar navigation elements when in settings view (same as fullscreen dialog mode)
@@ -72,7 +108,8 @@ export default function CalendarLayout() {
onDateChange: handleDateChange,
currentView,
onViewChange: handleViewChange,
isIframe: isInIframe
isIframe: isInIframe,
onToggleSidebar: () => setOpenSideBar(true)
}
return (
@@ -84,6 +121,10 @@ export default function CalendarLayout() {
onDateChange={handleDateChange}
onViewChange={handleViewChange}
menubarProps={menubarProps}
openSidebar={openSidebar}
onCloseSidebar={() => setOpenSideBar(false)}
setCurrentView={setCurrentView}
currentView={currentView}
/>
)}
{view === 'settings' && <SettingsPage isInIframe={isInIframe} />}
+3 -2
View File
@@ -9,6 +9,7 @@ import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'
import moment from 'moment'
import { useEffect, useState } from 'react'
import { useI18n } from 'twake-i18n'
import { CALENDAR_VIEWS } from './utils/constants'
export function MiniCalendar({
calendarRef,
@@ -70,11 +71,11 @@ export function MiniCalendar({
const isToday = date.getTime() === today.getTime()
const isSelectedDay =
calendarRef.current?.view.type === 'timeGridDay' &&
calendarRef.current?.view.type === CALENDAR_VIEWS.timeGridDay &&
date.getTime() === selected.getTime()
const isInSelectedWeek =
calendarRef.current?.view.type === 'timeGridWeek' ||
calendarRef.current?.view.type === CALENDAR_VIEWS.timeGridWeek ||
calendarRef.current?.view.type === undefined
? (() => {
const startOfWeek = computeStartOfTheWeek(selected)
+148
View File
@@ -0,0 +1,148 @@
import { CalendarApi } from '@fullcalendar/core'
import { Box, Button, Drawer, radius } from '@linagora/twake-mui'
import AddIcon from '@mui/icons-material/Add'
import CalendarViewDayOutlinedIcon from '@mui/icons-material/CalendarViewDayOutlined'
import CalendarViewMonthOutlinedIcon from '@mui/icons-material/CalendarViewMonthOutlined'
import CalendarViewWeekOutlinedIcon from '@mui/icons-material/CalendarViewWeekOutlined'
import { Dispatch, MutableRefObject, SetStateAction } from 'react'
import { useI18n } from 'twake-i18n'
import { User } from '../Attendees/PeopleSearch'
import { FieldWithLabel } from '../Event/components/FieldWithLabel'
import CalendarSelection from './CalendarSelection'
import { MiniCalendar } from './MiniCalendar'
import { TempCalendarsInput } from './TempCalendarsInput'
interface CalendarSidebarProps {
isTablet: boolean
open: boolean
onClose: () => void
calendarRef: MutableRefObject<CalendarApi | null>
isIframe?: boolean
onCreateEvent: () => void
onViewChange: (view: string) => void
selectedMiniDate: Date
setSelectedMiniDate: (date: Date) => void
selectedCalendars: string[]
setSelectedCalendars: Dispatch<SetStateAction<string[]>>
tempUsers: User[]
setTempUsers: Dispatch<SetStateAction<User[]>>
}
export default function Sidebar({
isTablet,
open,
onClose,
calendarRef,
isIframe,
onCreateEvent,
onViewChange,
selectedMiniDate,
setSelectedMiniDate,
selectedCalendars,
setSelectedCalendars,
tempUsers,
setTempUsers
}: CalendarSidebarProps) {
const { t } = useI18n()
return (
<Drawer
variant={isTablet ? 'temporary' : 'permanent'}
open={isTablet ? open : true}
onClose={onClose}
className="sidebar"
sx={{
[`& .MuiDrawer-paper`]: {
paddingTop: isTablet ? 2 : 0,
paddingBottom: 3,
paddingLeft: 3,
paddingRight: 2,
width: '270px',
marginTop: isTablet ? 0 : '70px'
},
zIndex: isTablet ? 3000 : 5
}}
slotProps={{ paper: { className: 'sidebar' } }}
>
{!isTablet && (
<>
<Box
sx={{
position: 'sticky',
top: 0,
zIndex: 10,
backgroundColor: '#fff',
paddingTop: isIframe ? '10px' : 3
}}
>
<Button
size="medium"
variant="contained"
fullWidth
onClick={onCreateEvent}
sx={{
borderRadius: radius.lg,
fontSize: '16px',
fontWeight: 500,
lineHeight: 'normal'
}}
>
<AddIcon sx={{ marginRight: 0.5, fontSize: '20px' }} />{' '}
{t('event.createEvent')}
</Button>
</Box>
<Box>
<MiniCalendar
calendarRef={calendarRef}
selectedDate={selectedMiniDate}
setSelectedMiniDate={setSelectedMiniDate}
/>
</Box>
</>
)}
{isTablet && (
<FieldWithLabel label={t('sidebar.displayMode')} isExpanded={false}>
<Button
variant="text"
onClick={() => onViewChange('timeGridDay')}
startIcon={<CalendarViewDayOutlinedIcon />}
>
{t('menubar.views.day')}
</Button>
<Button
variant="text"
onClick={() => {
onViewChange('timeGridWeek')
}}
startIcon={<CalendarViewWeekOutlinedIcon />}
>
{t('menubar.views.week')}
</Button>
<Button
variant="text"
onClick={() => onViewChange('dayGridMonth')}
startIcon={<CalendarViewMonthOutlinedIcon />}
>
{t('menubar.views.month')}
</Button>
</FieldWithLabel>
)}
<Box sx={{ mb: 3, mt: 2 }}>
<TempCalendarsInput
tempUsers={tempUsers}
setTempUsers={setTempUsers}
handleToggleEventPreview={onCreateEvent}
/>
</Box>
<Box className="calendarList">
<CalendarSelection
selectedCalendars={selectedCalendars}
setSelectedCalendars={setSelectedCalendars}
/>
</Box>
</Drawer>
)
}
@@ -0,0 +1,82 @@
import { Box, Button, Drawer, radius } from '@linagora/twake-mui'
import AddIcon from '@mui/icons-material/Add'
import { useI18n } from 'twake-i18n'
import { MiniCalendar } from '../MiniCalendar'
import { CalendarSidebarProps } from './SideBar'
import { SidebarCommonContent } from './SidebarCommonContent'
export function DesktopSidebar({
calendarRef,
isIframe,
onCreateEvent,
selectedMiniDate,
setSelectedMiniDate,
tempUsers,
setTempUsers,
selectedCalendars,
setSelectedCalendars
}: CalendarSidebarProps) {
const { t } = useI18n()
return (
<Drawer
variant="permanent"
open
className="sidebar"
sx={{
[`& .MuiDrawer-paper`]: {
paddingTop: 0,
paddingBottom: 3,
paddingLeft: 3,
paddingRight: 2,
width: '270px',
marginTop: '70px'
},
zIndex: 5
}}
slotProps={{ paper: { className: 'sidebar' } }}
>
<Box
sx={{
position: 'sticky',
top: 0,
zIndex: 10,
backgroundColor: '#fff',
paddingTop: isIframe ? '10px' : 3
}}
>
<Button
size="medium"
variant="contained"
fullWidth
onClick={onCreateEvent}
sx={{
borderRadius: radius.lg,
fontSize: '16px',
fontWeight: 500,
lineHeight: 'normal'
}}
>
<AddIcon sx={{ marginRight: 0.5, fontSize: '20px' }} />{' '}
{t('event.createEvent')}
</Button>
</Box>
<Box>
<MiniCalendar
calendarRef={calendarRef}
selectedDate={selectedMiniDate}
setSelectedMiniDate={setSelectedMiniDate}
/>
</Box>
<SidebarCommonContent
onCreateEvent={onCreateEvent}
tempUsers={tempUsers}
setTempUsers={setTempUsers}
selectedCalendars={selectedCalendars}
setSelectedCalendars={setSelectedCalendars}
/>
</Drawer>
)
}
@@ -0,0 +1,32 @@
import { useScreenSizeDetection } from '@/useScreenSizeDetection'
import { CalendarApi } from '@fullcalendar/core'
import { Dispatch, MutableRefObject, SetStateAction } from 'react'
import { User } from '../../Attendees/PeopleSearch'
import { DesktopSidebar } from './DesktopSidebar'
import { TabletSidebar } from './TabletSidebar'
export interface CalendarSidebarProps {
open: boolean
onClose: () => void
calendarRef: MutableRefObject<CalendarApi | null>
isIframe?: boolean
onCreateEvent: () => void
onViewChange: (view: string) => void
selectedMiniDate: Date
setSelectedMiniDate: (date: Date) => void
selectedCalendars: string[]
setSelectedCalendars: Dispatch<SetStateAction<string[]>>
tempUsers: User[]
setTempUsers: Dispatch<SetStateAction<User[]>>
currentView: string
}
export default function Sidebar(sharedProps: CalendarSidebarProps) {
const { isTablet } = useScreenSizeDetection()
return isTablet ? (
<TabletSidebar {...sharedProps} />
) : (
<DesktopSidebar {...sharedProps} />
)
}
@@ -0,0 +1,37 @@
import { Box } from '@linagora/twake-mui'
import CalendarSelection from '../CalendarSelection'
import { TempCalendarsInput } from '../TempCalendarsInput'
import { CalendarSidebarProps } from './SideBar'
export function SidebarCommonContent({
onCreateEvent,
tempUsers,
setTempUsers,
selectedCalendars,
setSelectedCalendars
}: Pick<
CalendarSidebarProps,
| 'onCreateEvent'
| 'tempUsers'
| 'setTempUsers'
| 'selectedCalendars'
| 'setSelectedCalendars'
>) {
return (
<>
<Box sx={{ mb: 3, mt: 2 }}>
<TempCalendarsInput
tempUsers={tempUsers}
setTempUsers={setTempUsers}
handleToggleEventPreview={onCreateEvent}
/>
</Box>
<Box className="calendarList">
<CalendarSelection
selectedCalendars={selectedCalendars}
setSelectedCalendars={setSelectedCalendars}
/>
</Box>
</>
)
}
@@ -0,0 +1,121 @@
import {
Drawer,
ListItemIcon,
ListItemText,
MenuItem,
MenuList,
useTheme
} from '@linagora/twake-mui'
import CalendarViewDayOutlinedIcon from '@mui/icons-material/CalendarViewDayOutlined'
import CalendarViewMonthOutlinedIcon from '@mui/icons-material/CalendarViewMonthOutlined'
import CalendarViewWeekOutlinedIcon from '@mui/icons-material/CalendarViewWeekOutlined'
import { useI18n } from 'twake-i18n'
import { FieldWithLabel } from '../../Event/components/FieldWithLabel'
import { CALENDAR_VIEWS } from '../utils/constants'
import { CalendarSidebarProps } from './SideBar'
import { SidebarCommonContent } from './SidebarCommonContent'
const VIEW_OPTIONS = [
{
label: 'menubar.views.day',
value: CALENDAR_VIEWS.timeGridDay,
icon: <CalendarViewDayOutlinedIcon />
},
{
label: 'menubar.views.week',
value: CALENDAR_VIEWS.timeGridWeek,
icon: <CalendarViewWeekOutlinedIcon />
},
{
label: 'menubar.views.month',
value: CALENDAR_VIEWS.dayGridMonth,
icon: <CalendarViewMonthOutlinedIcon />
}
]
export function TabletSidebar({
open,
onClose,
onViewChange,
onCreateEvent,
tempUsers,
setTempUsers,
selectedCalendars,
setSelectedCalendars,
currentView
}: CalendarSidebarProps) {
const { t } = useI18n()
const theme = useTheme()
const changeViewAndClose = (view: string): void => {
onViewChange(view)
onClose()
}
return (
<Drawer
variant="temporary"
open={open}
onClose={onClose}
className="sidebar"
sx={{
[`& .MuiDrawer-paper`]: {
paddingTop: 2,
paddingBottom: 3,
paddingLeft: 3,
paddingRight: 2,
width: '270px',
marginTop: 0
},
zIndex: 3000
}}
slotProps={{ paper: { className: 'sidebar' } }}
>
<FieldWithLabel label={t('sidebar.displayMode')} isExpanded={false}>
<MenuList>
{VIEW_OPTIONS.map(option => {
const isSelected = option.value === currentView
return (
<MenuItem
key={option.value}
selected={isSelected}
onClick={() => changeViewAndClose(option.value)}
>
<ListItemIcon
sx={{
color: isSelected
? theme.palette.primary.main
: theme.palette.text.primary
}}
>
{option.icon}
</ListItemIcon>
<ListItemText
primary={t(option.label)}
primaryTypographyProps={{
sx: {
fontSize: '14px',
fontWeight: 500,
lineHeight: '20px',
color: isSelected
? theme.palette.primary.main
: theme.palette.text.primary
}
}}
/>
</MenuItem>
)
})}
</MenuList>
</FieldWithLabel>
<SidebarCommonContent
onCreateEvent={onCreateEvent}
tempUsers={tempUsers}
setTempUsers={setTempUsers}
selectedCalendars={selectedCalendars}
setSelectedCalendars={setSelectedCalendars}
/>
</Drawer>
)
}
@@ -63,7 +63,7 @@ export const createEventHandlers = (props: EventHandlersProps) => {
timezone
} = props
const handleDateSelect = (selectInfo: DateSelectArg) => {
const handleDateSelect = (selectInfo: DateSelectArg | null) => {
setSelectedRange(selectInfo)
if (tempUsers) {
const newEvent: CalendarEvent = {
@@ -11,6 +11,7 @@ import {
ViewMountArg
} from '@fullcalendar/core'
import React from 'react'
import { CALENDAR_VIEWS } from '../utils/constants'
import { createMouseHandlers } from './mouseHandlers'
export interface ViewHandlersProps {
@@ -54,16 +55,16 @@ export const createViewHandlers = (props: ViewHandlersProps) => {
}
const handleDayHeaderDidMount = (arg: DayHeaderMountArg) => {
if (arg.view.type === 'timeGridWeek') {
if (arg.view.type === CALENDAR_VIEWS.timeGridWeek) {
const headerEl = arg.el
const handleDayHeaderClick = () => {
calendarRef.current?.changeView('timeGridDay', arg.date)
calendarRef.current?.changeView(CALENDAR_VIEWS.timeGridDay, arg.date)
setSelectedDate(new Date(arg.date))
setSelectedMiniDate(new Date(arg.date))
if (onViewChange) {
onViewChange('timeGridDay')
onViewChange(CALENDAR_VIEWS.timeGridDay)
}
}
@@ -81,7 +82,10 @@ export const createViewHandlers = (props: ViewHandlersProps) => {
}
const handleViewDidMount = (arg: ViewMountArg) => {
if (arg.view.type === 'timeGridWeek' || arg.view.type === 'timeGridDay') {
if (
arg.view.type === CALENDAR_VIEWS.timeGridWeek ||
arg.view.type === CALENDAR_VIEWS.timeGridDay
) {
const calendarEl = document.querySelector('.fc') as HTMLElement
if (calendarEl) {
const mouseHandlers = createMouseHandlers({ calendarEl })
@@ -0,0 +1,5 @@
export const CALENDAR_VIEWS = {
dayGridMonth: 'dayGridMonth',
timeGridWeek: 'timeGridWeek',
timeGridDay: 'timeGridDay'
}