Merge pull request #12395 from ToolJet/feat/tab-navigation

Feat: Added support for tab navigation in Editor and Viewer
This commit is contained in:
Johnson Cherian 2025-04-07 09:18:18 +05:30 committed by GitHub
commit d0966c947c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 79 additions and 1 deletions

View file

@ -20,6 +20,7 @@ import NoComponentCanvasContainer from './NoComponentCanvasContainer';
import { RIGHT_SIDE_BAR_TAB } from '../RightSideBar/rightSidebarConstants';
import { isPDFSupported } from '@/_helpers/appUtils';
import toast from 'react-hot-toast';
import useSortedComponents from '../_hooks/useSortedComponents';
//TODO: Revisit the logic of height (dropRef)
@ -146,6 +147,8 @@ export const Container = React.memo(
[setLastCanvasClickPosition]
);
const sortedComponents = useSortedComponents(components, currentLayout, id);
return (
<div
// {...(config.COMMENT_FEATURE_ENABLE && showComments && { onClick: handleAddThread })}
@ -197,7 +200,7 @@ export const Container = React.memo(
data-parent-type={id === 'canvas' ? 'canvas' : componentType}
style={{ height: !showEmptyContainer ? '100%' : 'auto' }} //TODO: remove hardcoded height & canvas condition
>
{components.map((id) => (
{sortedComponents.map((id) => (
<WidgetWrapper
id={id}
key={id}

View file

@ -67,6 +67,7 @@ export default function Grid({ gridWidth, currentLayout }) {
const prevDragParentId = useRef(null);
const newDragParentId = useRef(null);
const [isGroupDragging, setIsGroupDragging] = useState(false);
const setReorderContainerChildren = useStore((state) => state.setReorderContainerChildren, shallow);
useEffect(() => {
const selectedSet = new Set(selectedComponents);
@ -536,6 +537,7 @@ export default function Grid({ gridWidth, currentLayout }) {
})
);
}
setReorderContainerChildren(draggedOverElemId ?? 'canvas');
} catch (error) {
console.error('Error dragging group', error);
}
@ -696,6 +698,7 @@ export default function Grid({ gridWidth, currentLayout }) {
resizeData.gw = _gridWidth;
}
handleResizeStop([resizeData]);
setReorderContainerChildren(currentWidget?.parent ?? 'canvas');
} catch (error) {
console.error('ResizeEnd error ->', error);
}
@ -775,6 +778,11 @@ export default function Grid({ gridWidth, currentLayout }) {
ev.target.style.transform = `translate(${posX}px, ${posY}px)`;
});
}
const groupParentId =
boxList.find(({ id }) => id === groupResizeDataRef.current[0].target.id)?.parent ?? 'canvas';
setReorderContainerChildren(groupParentId);
groupResizeDataRef.current = [];
reloadGrid();
} catch (error) {
@ -841,6 +849,8 @@ export default function Grid({ gridWidth, currentLayout }) {
useStore.getState().setDraggingComponentId(null);
isDraggingRef.current = false;
}
const oldParentId = boxList.find((b) => b.id === e.target.id)?.parent ?? 'canvas';
prevDragParentId.current = null;
newDragParentId.current = null;
setDragParentId(null);
@ -880,6 +890,12 @@ export default function Grid({ gridWidth, currentLayout }) {
// Apply transform for smooth transition
e.target.style.transform = `translate(${left}px, ${top}px)`;
// Force reordering of conatiner if the parent has not changed
const newParentId = target.slotId === 'real-canvas' ? 'canvas' : target.slotId;
if (oldParentId === newParentId) {
setReorderContainerChildren(newParentId);
}
// Select the dragged component after drop
setTimeout(() => setSelectedComponents([dragged.id]));
} catch (error) {

View file

@ -0,0 +1,49 @@
import { useMemo, useRef } from 'react';
import useStore from '@/AppBuilder/_stores/store';
import { shallow } from 'zustand/shallow';
const useSortedComponents = (components, currentLayout, id) => {
const getCurrentPageComponents = useStore((state) => state.getCurrentPageComponents, shallow);
const reorderContainerChildren = useStore((state) => state.reorderContainerChildren, shallow);
const prevForceUpdateRef = useRef(0);
const prevComponentsOrder = useRef(components);
// Function to sort the components based on position in container for tab navigation
const sortedComponents = useMemo(() => {
const { triggerUpdate, containerId } = reorderContainerChildren;
// If a forced update occurred for a different container, return the previous order
const isForcedUpdate = prevForceUpdateRef.current !== triggerUpdate;
if (isForcedUpdate) {
prevForceUpdateRef.current = triggerUpdate;
if (containerId !== id) {
return prevComponentsOrder.current;
}
}
const currentPageComponents = getCurrentPageComponents();
const newComponentsOrder = [...components].sort((a, b) => {
const aTop = currentPageComponents?.[a]?.layouts?.[currentLayout]?.top;
const bTop = currentPageComponents?.[b]?.layouts?.[currentLayout]?.top;
if (aTop !== bTop) {
return aTop - bTop;
} else {
const aLeft = currentPageComponents?.[a]?.layouts?.[currentLayout]?.left;
const bLeft = currentPageComponents?.[b]?.layouts?.[currentLayout]?.left;
if (aLeft !== bLeft) {
return aLeft - bLeft;
}
return 0;
}
});
prevComponentsOrder.current = newComponentsOrder;
return newComponentsOrder;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [components, currentLayout, reorderContainerChildren.triggerUpdate, id]);
return sortedComponents;
};
export default useSortedComponents;

View file

@ -8,6 +8,10 @@ const initialState = {
lastCanvasIdClick: '',
lastCanvasClickPosition: null,
draggingComponentId: null,
reorderContainerChildren: {
containerId: null,
triggerUpdate: 0,
},
};
export const createGridSlice = (set, get) => ({
@ -73,4 +77,10 @@ export const createGridSlice = (set, get) => ({
setLastCanvasClickPosition: (position) => {
set({ lastCanvasClickPosition: position });
},
setReorderContainerChildren: (containerId) => {
// Function to trigger reordering of specific container for tab navigation
set((state) => ({
reorderContainerChildren: { containerId, triggerUpdate: state.reorderContainerChildren.triggerUpdate + 1 },
}));
},
});