Files
twenty/packages/twenty-front/src/modules/page-layout/components/PageLayoutTabRenderClone.tsx
T
Arun 59029a0035 [Fix] : Dragged element is considered to be part of a dropdown in dashboard tab list (#18414)
Fixes #15327

The issue occurred because the drag clone's visual state was previously
tied strictly to hovering over the `VISIBLE_TABS` boundaries. When a tab
was dragged outside this area (such as the last tab naturally crossing
into the `MORE_BUTTON` hover zone), the drag clone incorrectly fell back
to the dropdown menu item style.

We fixed this by making the `isHoveringTabList` logic more robust.
Instead of enforcing the tab style only within the `VISIBLE_TABS`
boundary, the dropdown style is now strictly restricted to the
`OVERFLOW_TABS` boundary.

With this change:
- Visible tabs successfully maintain their appearance when dragged
anywhere outside the dropdown.
- Dropdown tabs correctly transition to the normal tab style when
dragged out of the dropdown area, improving UX.



https://github.com/user-attachments/assets/9474e4c1-26a8-46e3-b9ee-4c6dbd8a4ea6

---------

Co-authored-by: Arun kumar <arunkumar@Aruns-MacBook-Air.local>
Co-authored-by: ehconitin <nitinkoche03@gmail.com>
Co-authored-by: nitin <142569587+ehconitin@users.noreply.github.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
2026-03-06 14:35:32 +00:00

102 lines
3.3 KiB
TypeScript

import { isDefined } from 'twenty-shared/utils';
import { PAGE_LAYOUT_TAB_LIST_DROPPABLE_IDS } from '@/page-layout/components/PageLayoutTabListDroppableIds';
import { pageLayoutTabListCurrentDragDroppableIdComponentState } from '@/page-layout/states/pageLayoutTabListCurrentDragDroppableIdComponentState';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import { TabAvatar } from '@/ui/layout/tab-list/components/TabAvatar';
import { type SingleTabProps } from '@/ui/layout/tab-list/types/SingleTabProps';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { type DraggableProvided } from '@hello-pangea/dnd';
import { styled } from '@linaria/react';
import { useContext } from 'react';
import { StyledTabContainer, TabContent } from 'twenty-ui/input';
import { MenuItemSelectAvatar } from 'twenty-ui/navigation';
import { ThemeContext } from 'twenty-ui/theme-constants';
const StyledDraggableWrapper = styled.div`
cursor: grab;
display: flex;
&:active {
cursor: grabbing;
}
`;
export const PageLayoutTabRenderClone = ({
tab,
provided,
activeTabId,
}: {
tab: SingleTabProps;
provided: DraggableProvided;
activeTabId: string | null;
}) => {
const { theme } = useContext(ThemeContext);
const pageLayoutTabListCurrentDragDroppableId = useAtomComponentStateValue(
pageLayoutTabListCurrentDragDroppableIdComponentState,
);
const isHoveringTabList =
pageLayoutTabListCurrentDragDroppableId !==
PAGE_LAYOUT_TAB_LIST_DROPPABLE_IDS.OVERFLOW_TABS;
if (!isDefined(tab)) return null;
if (isHoveringTabList) {
return (
<StyledDraggableWrapper
ref={provided.innerRef}
// oxlint-disable-next-line react/jsx-props-no-spreading
{...provided.draggableProps}
// oxlint-disable-next-line react/jsx-props-no-spreading
{...provided.dragHandleProps}
style={{
...provided.draggableProps.style,
cursor: 'grabbing',
}}
>
<StyledTabContainer active={tab.id === activeTabId} disabled={false}>
<TabContent
id={tab.id}
active={false}
disabled={false}
LeftIcon={tab.Icon}
title={tab.title}
logo={tab.logo}
pill={tab.pill}
/>
</StyledTabContainer>
</StyledDraggableWrapper>
);
} else {
return (
<StyledDraggableWrapper
id={'clone-drag-wrapper'}
ref={provided.innerRef}
// oxlint-disable-next-line react/jsx-props-no-spreading
{...provided.draggableProps}
// oxlint-disable-next-line react/jsx-props-no-spreading
{...provided.dragHandleProps}
style={{
...provided.draggableProps.style,
cursor: 'grabbing',
}}
>
<div
style={{
minWidth:
GenericDropdownContentWidth.Medium -
theme.spacingMultiplicator * 2,
}}
>
<MenuItemSelectAvatar
text={tab.title}
avatar={<TabAvatar tab={tab} />}
selected={tab.id === activeTabId}
onClick={undefined}
disabled
/>
</div>
</StyledDraggableWrapper>
);
}
};