diff --git a/src/components/Calendar/CalendarSelection.tsx b/src/components/Calendar/CalendarSelection.tsx index 6ee7e1b..a0637d9 100644 --- a/src/components/Calendar/CalendarSelection.tsx +++ b/src/components/Calendar/CalendarSelection.tsx @@ -10,34 +10,23 @@ import { AccordionDetails, AccordionSummary, Checkbox, - Divider, IconButton, - ListItem, - Menu, - MenuItem + ListItem } from '@linagora/twake-mui' import AddIcon from '@mui/icons-material/Add' import ExpandMoreIcon from '@mui/icons-material/ExpandMore' import MoreHorizIcon from '@mui/icons-material/MoreHoriz' -import { SetStateAction, useEffect, useMemo, useState } from 'react' +import { SetStateAction, useEffect, useMemo, useState, useRef } from 'react' import { useI18n } from 'twake-i18n' +import { useScreenSizeDetection } from '@/useScreenSizeDetection' import CalendarPopover from './CalendarModal' import CalendarSearch from './CalendarSearch' import { DeleteCalendarDialog } from './DeleteCalendarDialog' import { OwnerCaption } from './OwnerCaption' import CalendarResources from './CalendarResources' +import { CalendarSelectorMenu } from './CalendarSelectorMenu' -function CalendarAccordion({ - title, - calendars, - selectedCalendars, - handleToggle, - showAddButton = false, - onAddClick, - defaultExpanded = false, - setOpen, - hideOwner -}: { +const CalendarAccordion: React.FC<{ title: string calendars: string[] selectedCalendars: string[] @@ -47,14 +36,24 @@ function CalendarAccordion({ defaultExpanded?: boolean setOpen: (id: string) => void hideOwner?: boolean -}) { +}> = ({ + title, + calendars, + selectedCalendars, + handleToggle, + showAddButton = false, + onAddClick, + defaultExpanded = false, + setOpen, + hideOwner +}) => { const allCalendars = useAppSelector(state => state.calendars.list) const { t } = useI18n() const [expended, setExpended] = useState(defaultExpanded) useEffect(() => { - const handleExpendedChange = () => { + const handleExpendedChange = (): void => { setExpended(defaultExpanded) } handleExpendedChange() @@ -127,13 +126,10 @@ function CalendarAccordion({ ) } -export default function CalendarSelection({ - selectedCalendars, - setSelectedCalendars -}: { +const CalendarSelection: React.FC<{ selectedCalendars: string[] setSelectedCalendars: (value: SetStateAction) => void -}) { +}> = ({ selectedCalendars, setSelectedCalendars }) => { const { t } = useI18n() const userId = useAppSelector(state => state.user.userData?.openpaasId) ?? '' const calendars = useAppSelector(state => state.calendars.list) @@ -158,7 +154,7 @@ export default function CalendarSelection({ extractEventBaseUuid(id) !== userId && calendars?.[id]?.owner?.resource ) - const handleCalendarToggle = (name: string) => { + const handleCalendarToggle = (name: string): void => { setSelectedCalendars((prev: string[]) => prev.includes(name) ? prev.filter(n => n !== name) : [...prev, name] ) @@ -243,7 +239,7 @@ export default function CalendarSelection({ /> { + onClose={(newCalIds?: string[] | Record) => { setAnchorElCalOthers(null) if (newCalIds?.length) { newCalIds.forEach(id => handleCalendarToggle(id)) @@ -265,15 +261,7 @@ export default function CalendarSelection({ ) } -function CalendarSelector({ - calendars, - id, - isPersonal, - selectedCalendars, - handleCalendarToggle, - setOpen, - hideOwner -}: { +const CalendarSelector: React.FC<{ calendars: Record id: string isPersonal: boolean @@ -281,25 +269,62 @@ function CalendarSelector({ handleCalendarToggle: (name: string) => void setOpen: () => void hideOwner?: boolean -}) { +}> = ({ + calendars, + id, + isPersonal, + selectedCalendars, + handleCalendarToggle, + setOpen, + hideOwner +}) => { const { t } = useI18n() const dispatch = useAppDispatch() const calLink = useAppSelector(state => state.calendars.list[id].link) ?? '' + const { isTooSmall: isMobile } = useScreenSizeDetection() const [anchorEl, setAnchorEl] = useState(null) const open = Boolean(anchorEl) - const handleClick = (event: React.MouseEvent) => { + + const handleClick = (event: React.MouseEvent): void => { setAnchorEl(event.currentTarget) } - const handleClose = () => { + const handleClose = (): void => { setAnchorEl(null) } + + const longPressTimerRef = useRef | null>(null) + const isLongPressedRef = useRef(false) + + const handleTouchStart = (): void => { + if (!isMobile) return + isLongPressedRef.current = false + longPressTimerRef.current = setTimeout(() => { + isLongPressedRef.current = true + setAnchorEl(document.body) + }, 300) + } + + const handleTouchEnd = (): void => { + if (longPressTimerRef.current) { + clearTimeout(longPressTimerRef.current) + longPressTimerRef.current = null + } + } + + const handleTouchMove = (): void => { + if (longPressTimerRef.current) { + clearTimeout(longPressTimerRef.current) + longPressTimerRef.current = null + } + } + const [userId, calId] = id.split('/') const isDefault = isPersonal && userId === calId const [deletePopupOpen, setDeletePopupOpen] = useState(false) - const handleDeleteConfirm = () => { - dispatch(removeCalendarAsync({ calId: id, calLink })) + const handleDeleteConfirm = async (): Promise => { + await dispatch(removeCalendarAsync({ calId: id, calLink })) setDeletePopupOpen(false) handleClose() } @@ -339,6 +364,15 @@ function CalendarSelector({ '& .MoreBtn': { opacity: 1 } } }} + onTouchStart={handleTouchStart} + onTouchEnd={handleTouchEnd} + onTouchMove={handleTouchMove} + onClickCapture={e => { + if (isLongPressedRef.current) { + e.stopPropagation() + e.preventDefault() + } + }} > - - - - - - { - setOpen() - handleClose() - }} - > - {t('actions.modify')} - - {!isDefault && } - {!isDefault && ( - setDeletePopupOpen(!deletePopupOpen)}> - {isPersonal ? t('actions.delete') : t('actions.remove')} - + {!isMobile && ( + + + )} - + + + setDeletePopupOpen(true)} + isDefault={isDefault} + isPersonal={isPersonal} + /> void handleDeleteConfirm()} /> ) } + +export default CalendarSelection diff --git a/src/components/Calendar/CalendarSelectorMenu/CalendarSelectorDesktopMenu.tsx b/src/components/Calendar/CalendarSelectorMenu/CalendarSelectorDesktopMenu.tsx new file mode 100644 index 0000000..ea5d915 --- /dev/null +++ b/src/components/Calendar/CalendarSelectorMenu/CalendarSelectorDesktopMenu.tsx @@ -0,0 +1,53 @@ +import React from 'react' +import { Divider, Menu, MenuItem } from '@linagora/twake-mui' +import { useI18n } from 'twake-i18n' + +interface CalendarSelectorDesktopMenuProps { + id: string + anchorEl: HTMLElement | null + open: boolean + onClose: () => void + onModify: () => void + onDelete: () => void + isDefault: boolean + isPersonal: boolean +} + +export const CalendarSelectorDesktopMenu: React.FC< + CalendarSelectorDesktopMenuProps +> = ({ + id, + anchorEl, + open, + onClose, + onModify, + onDelete, + isDefault, + isPersonal +}) => { + const { t } = useI18n() + + return ( + + { + onModify() + onClose() + }} + > + {t('actions.modify')} + + {!isDefault && } + {!isDefault && ( + { + onDelete() + onClose() + }} + > + {isPersonal ? t('actions.delete') : t('actions.remove')} + + )} + + ) +} diff --git a/src/components/Calendar/CalendarSelectorMenu/CalendarSelectorMobileMenu.tsx b/src/components/Calendar/CalendarSelectorMenu/CalendarSelectorMobileMenu.tsx new file mode 100644 index 0000000..aa01414 --- /dev/null +++ b/src/components/Calendar/CalendarSelectorMenu/CalendarSelectorMobileMenu.tsx @@ -0,0 +1,66 @@ +import React from 'react' +import { + styled, + SwipeableDrawer, + List, + ListItemButton, + ListItemText +} from '@linagora/twake-mui' +import { useI18n } from 'twake-i18n' + +const StyledSwipeableDrawer = styled(SwipeableDrawer)(({ theme }) => ({ + zIndex: theme.zIndex.modal + 100 +})) + +interface CalendarSelectorMobileMenuProps { + open: boolean + onClose: () => void + onModify: () => void + onDelete: () => void + isDefault: boolean + isPersonal: boolean +} + +export const CalendarSelectorMobileMenu: React.FC< + CalendarSelectorMobileMenuProps +> = ({ open, onClose, onModify, onDelete, isDefault, isPersonal }) => { + const { t } = useI18n() + + return ( + {}} + disableAutoFocus + > + + { + onModify() + onClose() + }} + > + + + {!isDefault && ( + { + onDelete() + onClose() + }} + > + + + )} + + + ) +} diff --git a/src/components/Calendar/CalendarSelectorMenu/index.tsx b/src/components/Calendar/CalendarSelectorMenu/index.tsx new file mode 100644 index 0000000..ef57ca7 --- /dev/null +++ b/src/components/Calendar/CalendarSelectorMenu/index.tsx @@ -0,0 +1,54 @@ +import React from 'react' +import { useScreenSizeDetection } from '@/useScreenSizeDetection' +import { CalendarSelectorDesktopMenu } from './CalendarSelectorDesktopMenu' +import { CalendarSelectorMobileMenu } from './CalendarSelectorMobileMenu' + +interface CalendarSelectorMenuProps { + id: string + anchorEl: HTMLElement | null + open: boolean + onClose: () => void + onModify: () => void + onDelete: () => void + isDefault: boolean + isPersonal: boolean +} + +export const CalendarSelectorMenu: React.FC = ({ + id, + anchorEl, + open, + onClose, + onModify, + onDelete, + isDefault, + isPersonal +}) => { + const { isTooSmall: isMobile } = useScreenSizeDetection() + + if (!isMobile) { + return ( + + ) + } + + return ( + + ) +}