import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { Droppable } from '@hello-pangea/dnd'; import { useLingui } from '@lingui/react/macro'; import { isNonEmptyString } from '@sniptt/guards'; import { useContext } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import { isDefined } from 'twenty-shared/utils'; import { IconChevronDown, IconChevronRight, IconPlus, useIcons, } from 'twenty-ui/display'; import { useIsDropDisabledForSection } from '@/navigation-menu-item/hooks/useIsDropDisabledForSection'; import { useOpenAddItemToFolderPage } from '@/navigation-menu-item/hooks/useOpenAddItemToFolderPage'; import { AnimatedExpandableContainer } from 'twenty-ui/layout'; import { useIsMobile } from 'twenty-ui/utilities'; import { NavigationMenuItemDroppable } from '@/navigation-menu-item/components/NavigationMenuItemDroppable'; import { NavigationMenuItemIcon } from '@/navigation-menu-item/components/NavigationMenuItemIcon'; import { WorkspaceNavigationMenuItemFolderDragClone } from '@/navigation-menu-item/components/WorkspaceNavigationMenuItemFolderDragClone'; import { FOLDER_ICON_DEFAULT } from '@/navigation-menu-item/constants/FolderIconDefault'; import { DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER } from '@/navigation-menu-item/constants/NavigationMenuItemDefaultColorFolder'; import { NavigationMenuItemDroppableIds } from '@/navigation-menu-item/constants/NavigationMenuItemDroppableIds'; import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType'; import { NavigationMenuItemDragContext } from '@/navigation-menu-item/contexts/NavigationMenuItemDragContext'; import { type NavigationMenuItemClickParams } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems'; import { openNavigationMenuItemFolderIdsState } from '@/navigation-menu-item/states/openNavigationMenuItemFolderIdsState'; import { getNavigationMenuItemSecondaryLabel } from '@/navigation-menu-item/utils/getNavigationMenuItemSecondaryLabel'; import { getObjectMetadataForNavigationMenuItem } from '@/navigation-menu-item/utils/getObjectMetadataForNavigationMenuItem'; import { isLocationMatchingNavigationMenuItem } from '@/navigation-menu-item/utils/isLocationMatchingNavigationMenuItem'; import { type ProcessedNavigationMenuItem } from '@/navigation-menu-item/utils/sortNavigationMenuItems'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableItem'; import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem'; import { NavigationDrawerItemsCollapsableContainer } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItemsCollapsableContainer'; import { NavigationDrawerSubItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem'; import { currentNavigationMenuItemFolderIdState } from '@/ui/navigation/navigation-drawer/states/currentNavigationMenuItemFolderIdState'; import { getNavigationSubItemLeftAdornment } from '@/ui/navigation/navigation-drawer/utils/getNavigationSubItemLeftAdornment'; import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; import { coreViewsState } from '@/views/states/coreViewState'; import { ViewKey } from '@/views/types/ViewKey'; import { convertCoreViewToView } from '@/views/utils/convertCoreViewToView'; const StyledFolderContainer = styled.div<{ $isSelectedInEditMode: boolean }>` border: ${({ theme, $isSelectedInEditMode }) => $isSelectedInEditMode ? `1px solid ${theme.color.blue}` : '1px solid transparent'}; border-radius: ${({ theme }) => theme.border.radius.sm}; `; const StyledFolderDroppableContent = styled.div<{ $compact: boolean; }>` display: flex; flex-direction: column; padding-bottom: ${({ theme, $compact }) => ($compact ? 0 : theme.spacing(2))}; `; const StyledFolderExpandableWrapper = styled.div` & > div { overflow: visible !important; } `; type WorkspaceNavigationMenuItemsFolderProps = { folderId: string; folderName: string; folderIconKey?: string | null; folderColor?: string | null; navigationMenuItems: ProcessedNavigationMenuItem[]; isGroup: boolean; isEditMode?: boolean; isSelectedInEditMode?: boolean; onEditModeClick?: () => void; onNavigationMenuItemClick?: (params: NavigationMenuItemClickParams) => void; selectedNavigationMenuItemId?: string | null; isDragging?: boolean; }; export const WorkspaceNavigationMenuItemsFolder = ({ folderId, folderName, folderIconKey, folderColor, navigationMenuItems, isGroup, isEditMode = false, isSelectedInEditMode = false, onEditModeClick, onNavigationMenuItemClick, selectedNavigationMenuItemId = null, isDragging = false, }: WorkspaceNavigationMenuItemsFolderProps) => { const theme = useTheme(); const { getIcon } = useIcons(); const FolderIcon = getIcon(folderIconKey ?? FOLDER_ICON_DEFAULT); const objectMetadataItems = useAtomStateValue(objectMetadataItemsState); const coreViews = useAtomStateValue(coreViewsState); const views = coreViews.map(convertCoreViewToView); const location = useLocation(); const navigate = useNavigate(); const currentPath = location.pathname; const currentViewPath = location.pathname + location.search; const isMobile = useIsMobile(); const { t } = useLingui(); const [openNavigationMenuItemFolderIds, setOpenNavigationMenuItemFolderIds] = useAtomState(openNavigationMenuItemFolderIdsState); const setCurrentNavigationMenuItemFolderId = useSetAtomState( currentNavigationMenuItemFolderIdState, ); const { openAddItemToFolderPage } = useOpenAddItemToFolderPage(); const isOpen = openNavigationMenuItemFolderIds.includes(folderId); const folderContentLengthForTree = isEditMode && isSelectedInEditMode ? navigationMenuItems.length + 1 : navigationMenuItems.length; const handleAddMenuItemToFolder = () => { openAddItemToFolderPage({ targetFolderId: folderId, targetIndex: navigationMenuItems.length, resetNavigationStack: true, }); }; const handleToggle = () => { if (isMobile) { setCurrentNavigationMenuItemFolderId((prev) => prev === folderId ? null : folderId, ); } else { setOpenNavigationMenuItemFolderIds((current) => isOpen ? current.filter((id) => id !== folderId) : [...current, folderId], ); } if (!isOpen) { const firstNonLinkItem = navigationMenuItems.find( (item) => item.itemType !== NavigationMenuItemType.LINK && isNonEmptyString(item.link), ); if (isDefined(firstNonLinkItem?.link)) { navigate(firstNonLinkItem.link); } } }; const shouldUseEditModeClick = isEditMode && isDefined(onEditModeClick); const handleClick = shouldUseEditModeClick ? onEditModeClick : handleToggle; const selectedNavigationMenuItemIndex = navigationMenuItems.findIndex( (item) => isLocationMatchingNavigationMenuItem(currentPath, currentViewPath, item), ); const navigationMenuItemFolderContentLength = navigationMenuItems.length; const { isDragging: isContextDragging } = useContext( NavigationMenuItemDragContext, ); const folderContentDropDisabled = useIsDropDisabledForSection(true); return ( ) : ( ) } /> ( )} getContainerForClone={() => document.body} > {(provided) => ( {navigationMenuItems.map((navigationMenuItem, index) => { const objectMetadataItem = navigationMenuItem.itemType === NavigationMenuItemType.VIEW || navigationMenuItem.itemType === NavigationMenuItemType.RECORD ? getObjectMetadataForNavigationMenuItem( navigationMenuItem, objectMetadataItems, views, ) : null; const handleEditModeClick = isEditMode && isDefined(onNavigationMenuItemClick) && (navigationMenuItem.itemType === NavigationMenuItemType.LINK || isDefined(objectMetadataItem)) ? () => onNavigationMenuItemClick({ item: navigationMenuItem, objectMetadataItem: objectMetadataItem ?? undefined, }) : undefined; return ( ( )} to={ isContextDragging || handleEditModeClick ? undefined : navigationMenuItem.link } onClick={handleEditModeClick} active={index === selectedNavigationMenuItemIndex} isSelectedInEditMode={ selectedNavigationMenuItemId === navigationMenuItem.id } subItemState={getNavigationSubItemLeftAdornment({ index, arrayLength: folderContentLengthForTree, selectedIndex: selectedNavigationMenuItemIndex, })} isDragging={isContextDragging} triggerEvent="CLICK" /> } /> ); })} {provided.placeholder} )} {isEditMode && isSelectedInEditMode && ( )} ); };