diff --git a/superset-frontend/packages/superset-ui-core/src/components/Tabs/Tabs.tsx b/superset-frontend/packages/superset-ui-core/src/components/Tabs/Tabs.tsx index 291b16efea1..16c1c1ffb21 100644 --- a/superset-frontend/packages/superset-ui-core/src/components/Tabs/Tabs.tsx +++ b/superset-frontend/packages/superset-ui-core/src/components/Tabs/Tabs.tsx @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import type { FC } from 'react'; import { css, styled, useTheme } from '@apache-superset/core/ui'; // eslint-disable-next-line no-restricted-imports @@ -161,7 +162,9 @@ export const StyledLineEditableTabs = styled(EditableTabs)` } `; -export const LineEditableTabs = Object.assign(StyledLineEditableTabs, { +export const LineEditableTabs: FC & { + TabPane: typeof StyledTabPane; +} = Object.assign(StyledLineEditableTabs, { TabPane: StyledTabPane, }); diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tabs/Tabs.jsx b/superset-frontend/src/dashboard/components/gridComponents/Tabs/Tabs.jsx index b23f1da5674..6beb76db721 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Tabs/Tabs.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Tabs/Tabs.jsx @@ -362,7 +362,7 @@ const Tabs = props => { setActiveKey(currentActiveTabId); } }, - [props.component, props.updateComponents, selectedTabIndex], + [props.component, props.updateComponents, selectedTabIndex, activeKey], ); const { diff --git a/superset-frontend/src/dashboard/components/gridComponents/TabsRenderer/TabsRenderer.tsx b/superset-frontend/src/dashboard/components/gridComponents/TabsRenderer/TabsRenderer.tsx index 445ddf1db31..906150a5f32 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/TabsRenderer/TabsRenderer.tsx +++ b/superset-frontend/src/dashboard/components/gridComponents/TabsRenderer/TabsRenderer.tsx @@ -22,6 +22,7 @@ import { ReactElement, RefObject, useCallback, + useRef, useState, } from 'react'; import { styled } from '@apache-superset/core/ui'; @@ -61,6 +62,16 @@ const StyledTabsContainer = styled.div<{ isDragging?: boolean }>` height: calc(100% - 47px); } + /* Ensure tab labels maintain full opacity during drag */ + .ant-tabs-tab { + .dragdroppable-tab, + .editable-title, + textarea { + opacity: 1; + color: inherit; + } + } + /* Hide ink-bar during drag */ ${({ isDragging }) => isDragging && @@ -126,7 +137,7 @@ const DraggableTabNode: React.FC> = ({ ...props.style, position: 'relative', transform: transform ? `translate3d(${transform.x}px, 0, 0)` : undefined, - transition, + transition: isDragging ? 'none' : transition, cursor: disabled ? 'default' : 'move', zIndex: isDragging ? 1000 : 'auto', opacity: 1, @@ -163,6 +174,10 @@ const TabsRenderer = memo( }) => { const [activeId, setActiveId] = useState(null); + // Use ref to always have access to the current tabIds in callbacks + const tabIdsRef = useRef(tabIds); + tabIdsRef.current = tabIds; + const sensor = useSensor(PointerSensor, { activationConstraint: { distance: 10 }, }); @@ -173,14 +188,18 @@ const TabsRenderer = memo( const onDragEnd = useCallback( ({ active, over }: DragEndEvent) => { + const currentTabIds = tabIdsRef.current; + // Only reorder when we have a valid drop target and both IDs are found if (active.id !== over?.id && onTabsReorder) { - const activeIndex = tabIds.findIndex(id => id === active.id); - const overIndex = tabIds.findIndex(id => id === over?.id); - onTabsReorder(activeIndex, overIndex); + const activeIndex = currentTabIds.findIndex(id => id === active.id); + const overIndex = currentTabIds.findIndex(id => id === over?.id); + if (activeIndex !== -1 && overIndex !== -1) { + onTabsReorder(activeIndex, overIndex); + } } setActiveId(null); }, - [onTabsReorder, tabIds], + [onTabsReorder], ); const onDragCancel = useCallback(() => { @@ -220,6 +239,7 @@ const TabsRenderer = memo( {...(editMode && { renderTabBar: (tabBarProps, DefaultTabBar) => (