@lonli-lokli/react-mosaic-component
Version:
A React Tiling Window Manager
157 lines (156 loc) • 5.51 kB
JavaScript
// libs/react-mosaic-component/src/lib/DraggableTab.tsx
import { useDrag } from "react-dnd";
import { isEqual } from "lodash-es";
import { MosaicDragType } from "./types.mjs";
import { createDragToUpdates } from "./util/mosaicUpdates.mjs";
import { getNodeAtPath, isTabsNode } from "./util/mosaicUtilities.mjs";
var DraggableTab = ({
tabKey,
tabIndex,
tabContainerPath,
mosaicActions,
mosaicId,
children
}) => {
const tabPath = tabContainerPath.concat(tabIndex);
const [{ isDragging }, connectDragSource, connectDragPreview] = useDrag({
type: MosaicDragType.WINDOW,
item: () => {
const hideTimer = window.setTimeout(() => {
mosaicActions.hide(tabPath, true);
}, 50);
return {
mosaicId,
hideTimer,
// Add additional properties for tab reordering
isTab: true,
tabIndex,
tabKey,
tabContainerPath
};
},
end: (item, monitor) => {
window.clearTimeout(item.hideTimer);
const dropResult = monitor.getDropResult();
const didDrop = monitor.didDrop();
const ownPath = tabPath;
const tabReorderIndex = dropResult?.tabReorderIndex;
const isTabReorder = tabReorderIndex !== void 0 && dropResult?.path && isEqual(dropResult.path, tabContainerPath);
const isTabToTabContainer = tabReorderIndex !== void 0 && dropResult?.path && !isEqual(dropResult.path, tabContainerPath);
const isSelfDrop = dropResult?.path && isEqual(dropResult.path, tabContainerPath) && tabReorderIndex === void 0;
const isChildDrop = dropResult?.path && dropResult.path.length > ownPath.length && isEqual(dropResult.path.slice(0, ownPath.length), ownPath);
if (!didDrop || !dropResult || isChildDrop) {
mosaicActions.show(ownPath, true);
return;
}
if (isTabReorder) {
const root = mosaicActions.getRoot();
const tabsNode = getNodeAtPath(
root,
tabContainerPath
);
if (tabsNode && isTabsNode(tabsNode)) {
const currentTabs = [...tabsNode.tabs];
const currentActiveTabIndex = tabsNode.activeTabIndex;
const [movedTab] = currentTabs.splice(tabIndex, 1);
const adjustedInsertIndex = tabIndex < tabReorderIndex ? tabReorderIndex - 1 : tabReorderIndex;
currentTabs.splice(adjustedInsertIndex, 0, movedTab);
let newActiveTabIndex = currentActiveTabIndex;
if (tabIndex === currentActiveTabIndex) {
newActiveTabIndex = adjustedInsertIndex;
} else if (tabIndex < currentActiveTabIndex && adjustedInsertIndex >= currentActiveTabIndex) {
newActiveTabIndex = currentActiveTabIndex - 1;
} else if (tabIndex > currentActiveTabIndex && adjustedInsertIndex <= currentActiveTabIndex) {
newActiveTabIndex = currentActiveTabIndex + 1;
}
mosaicActions.updateTree([
{
path: tabContainerPath,
spec: {
tabs: { $set: currentTabs },
activeTabIndex: { $set: newActiveTabIndex }
}
}
]);
} else {
console.warn("Could not find tabs node at path:", tabContainerPath);
}
return;
}
if (isTabToTabContainer) {
const root = mosaicActions.getRoot();
const destinationTabsNode = getNodeAtPath(
root,
dropResult.path
);
if (destinationTabsNode && isTabsNode(destinationTabsNode)) {
const sourceTabsNode = getNodeAtPath(
root,
tabContainerPath
);
const currentSourceTabs = [...sourceTabsNode.tabs];
currentSourceTabs.splice(tabIndex, 1);
const removeUpdates = [
{
path: tabContainerPath,
spec: {
tabs: { $set: currentSourceTabs },
activeTabIndex: {
$set: Math.max(
0,
Math.min(tabIndex, sourceTabsNode.tabs.length - 2)
)
}
}
}
];
const currentDestinationTabs = [...destinationTabsNode.tabs];
currentDestinationTabs.splice(tabReorderIndex, 0, tabKey);
const insertUpdates = [
{
path: dropResult.path,
spec: {
tabs: { $set: currentDestinationTabs },
activeTabIndex: { $set: tabReorderIndex }
// Make the newly inserted tab active
}
}
];
mosaicActions.updateTree([...removeUpdates, ...insertUpdates]);
} else {
console.warn(
"Could not find destination tabs node at path:",
dropResult.path
);
}
return;
}
if (isSelfDrop || dropResult.path === void 0) {
mosaicActions.show(ownPath, true);
return;
}
const updates = createDragToUpdates(
mosaicActions.getRoot(),
ownPath,
dropResult.path,
dropResult.position === void 0 ? {
type: "tab-container"
} : { type: "split", position: dropResult.position }
);
mosaicActions.updateTree(updates, {
shouldNormalize: true
});
},
collect: (monitor) => ({
isDragging: monitor.isDragging()
})
});
return children({
isDragging,
connectDragSource,
connectDragPreview
});
};
export {
DraggableTab
};