* 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:
@@ -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' : ''}`}
|
||||
>
|
||||
|
||||
@@ -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} />}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
Reference in New Issue
Block a user