diff --git a/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridLayout.tsx b/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridLayout.tsx index c7c2aea5c8..a85fb01ca7 100644 --- a/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridLayout.tsx +++ b/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridLayout.tsx @@ -7,6 +7,7 @@ import { PAGE_LAYOUT_CONFIG, type PageLayoutBreakpoint, } from '@/page-layout/constants/PageLayoutBreakpoints'; +import { PAGE_LAYOUT_GRID_ITEM_Z_INDEX } from '@/page-layout/constants/PageLayoutGridItemZIndex'; import { PAGE_LAYOUT_GRID_MARGIN } from '@/page-layout/constants/PageLayoutGridMargin'; import { PAGE_LAYOUT_GRID_ROW_HEIGHT } from '@/page-layout/constants/PageLayoutGridRowHeight'; import { usePageLayoutHandleLayoutChange } from '@/page-layout/hooks/usePageLayoutHandleLayoutChange'; @@ -54,6 +55,10 @@ const StyledGridContainer = styled.div` user-select: auto; } + .react-grid-item { + z-index: ${PAGE_LAYOUT_GRID_ITEM_Z_INDEX}; + } + .react-grid-item:hover .widget-card-resize-handle { display: block !important; } diff --git a/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridOverlay.tsx b/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridOverlay.tsx index 5361598977..02cd836b40 100644 --- a/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridOverlay.tsx +++ b/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridOverlay.tsx @@ -1,4 +1,6 @@ import { type PageLayoutBreakpoint } from '@/page-layout/constants/PageLayoutBreakpoints'; +import { PAGE_LAYOUT_GRID_OVERLAY_Z_INDEX } from '@/page-layout/constants/PageLayoutGridOverlayZIndex'; +import { useCreateWidgetFromClick } from '@/page-layout/hooks/useCreateWidgetFromClick'; import { pageLayoutCurrentBreakpointComponentState } from '@/page-layout/states/pageLayoutCurrentBreakpointComponentState'; import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState'; import { pageLayoutSelectedCellsComponentState } from '@/page-layout/states/pageLayoutSelectedCellsComponentState'; @@ -26,7 +28,7 @@ const StyledGridOverlay = styled.div<{ gap: ${({ theme }) => theme.spacing(2)}; pointer-events: ${({ isDragSelecting }) => isDragSelecting ? 'auto' : 'none'}; - z-index: 0; + z-index: ${PAGE_LAYOUT_GRID_OVERLAY_Z_INDEX}; `; const StyledGridCell = styled.div<{ isSelected?: boolean }>` @@ -36,6 +38,7 @@ const StyledGridCell = styled.div<{ isSelected?: boolean }>` ${({ theme, isSelected }) => isSelected ? theme.color.blue7 : theme.border.color.light}; border-radius: ${({ theme }) => theme.border.radius.md}; + cursor: pointer; transition: background-color 0.3s ease; &:hover { @@ -59,6 +62,8 @@ export const PageLayoutGridOverlay = () => { const activeTabId = useRecoilComponentValue(activeTabIdComponentState); + const { createWidgetFromClick } = useCreateWidgetFromClick(); + const numberOfRows = useMemo(() => { const currentTabLayouts = pageLayoutCurrentLayouts[activeTabId ?? ''] || { desktop: [], @@ -92,6 +97,7 @@ export const PageLayoutGridOverlay = () => { key={i} data-selectable-id={cellId} isSelected={pageLayoutSelectedCells.has(cellId)} + onClick={() => createWidgetFromClick(cellId)} /> ); }, diff --git a/packages/twenty-front/src/modules/page-layout/constants/PageLayoutGridItemZIndex.ts b/packages/twenty-front/src/modules/page-layout/constants/PageLayoutGridItemZIndex.ts new file mode 100644 index 0000000000..14514d68f1 --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/constants/PageLayoutGridItemZIndex.ts @@ -0,0 +1 @@ +export const PAGE_LAYOUT_GRID_ITEM_Z_INDEX = 2; diff --git a/packages/twenty-front/src/modules/page-layout/constants/PageLayoutGridOverlayZIndex.ts b/packages/twenty-front/src/modules/page-layout/constants/PageLayoutGridOverlayZIndex.ts new file mode 100644 index 0000000000..43285df6f6 --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/constants/PageLayoutGridOverlayZIndex.ts @@ -0,0 +1 @@ +export const PAGE_LAYOUT_GRID_OVERLAY_Z_INDEX = 1; diff --git a/packages/twenty-front/src/modules/page-layout/constants/WidgetSizes.ts b/packages/twenty-front/src/modules/page-layout/constants/WidgetSizes.ts index 1d2b3e0754..952442df89 100644 --- a/packages/twenty-front/src/modules/page-layout/constants/WidgetSizes.ts +++ b/packages/twenty-front/src/modules/page-layout/constants/WidgetSizes.ts @@ -8,6 +8,6 @@ export const WIDGET_SIZES: Partial> = { }, [WidgetType.STANDALONE_RICH_TEXT]: { default: { w: 4, h: 4 }, - minimum: { w: 1, h: 1 }, + minimum: { w: 2, h: 2 }, }, }; diff --git a/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useCreateWidgetFromClick.test.tsx b/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useCreateWidgetFromClick.test.tsx new file mode 100644 index 0000000000..fe430fce45 --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useCreateWidgetFromClick.test.tsx @@ -0,0 +1,59 @@ +import { useNavigatePageLayoutCommandMenu } from '@/command-menu/pages/page-layout/hooks/useNavigatePageLayoutCommandMenu'; +import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; +import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { act, renderHook } from '@testing-library/react'; +import { type ReactNode } from 'react'; +import { pageLayoutDraggedAreaComponentState } from '../../states/pageLayoutDraggedAreaComponentState'; +import { pageLayoutEditingWidgetIdComponentState } from '../../states/pageLayoutEditingWidgetIdComponentState'; +import { useCreateWidgetFromClick } from '../useCreateWidgetFromClick'; +import { + PAGE_LAYOUT_TEST_INSTANCE_ID, + PageLayoutTestWrapper, +} from './PageLayoutTestWrapper'; + +jest.mock( + '@/command-menu/pages/page-layout/hooks/useNavigatePageLayoutCommandMenu', +); + +describe('useCreateWidgetFromClick', () => { + const mockNavigatePageLayoutCommandMenu = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + (useNavigatePageLayoutCommandMenu as jest.Mock).mockReturnValue({ + navigatePageLayoutCommandMenu: mockNavigatePageLayoutCommandMenu, + }); + }); + + it('should set dragged area and navigate to widget selection when called with a cellId', () => { + const { result } = renderHook( + () => ({ + createWidget: useCreateWidgetFromClick(), + draggedArea: useRecoilComponentValue( + pageLayoutDraggedAreaComponentState, + PAGE_LAYOUT_TEST_INSTANCE_ID, + ), + editingWidgetId: useRecoilComponentValue( + pageLayoutEditingWidgetIdComponentState, + PAGE_LAYOUT_TEST_INSTANCE_ID, + ), + }), + { + wrapper: ({ children }: { children: ReactNode }) => ( + {children} + ), + }, + ); + + act(() => { + result.current.createWidget.createWidgetFromClick('cell-2-3'); + }); + + expect(result.current.draggedArea).toEqual({ x: 2, y: 3, w: 1, h: 1 }); + expect(result.current.editingWidgetId).toBeNull(); + expect(mockNavigatePageLayoutCommandMenu).toHaveBeenCalledWith({ + commandMenuPage: CommandMenuPages.PageLayoutWidgetTypeSelect, + resetNavigationStack: true, + }); + }); +}); diff --git a/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useEndPageLayoutDragSelection.test.tsx b/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useEndPageLayoutDragSelection.test.tsx index 15898345f9..faeb9986f7 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useEndPageLayoutDragSelection.test.tsx +++ b/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useEndPageLayoutDragSelection.test.tsx @@ -102,6 +102,7 @@ describe('useEndPageLayoutDragSelection', () => { expect(mockNavigatePageLayoutCommandMenu).toHaveBeenCalledWith({ commandMenuPage: CommandMenuPages.PageLayoutWidgetTypeSelect, + resetNavigationStack: true, }); }); diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useCreateWidgetFromClick.ts b/packages/twenty-front/src/modules/page-layout/hooks/useCreateWidgetFromClick.ts new file mode 100644 index 0000000000..3effad3324 --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/hooks/useCreateWidgetFromClick.ts @@ -0,0 +1,42 @@ +import { useNavigatePageLayoutCommandMenu } from '@/command-menu/pages/page-layout/hooks/useNavigatePageLayoutCommandMenu'; +import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; +import { pageLayoutDraggedAreaComponentState } from '@/page-layout/states/pageLayoutDraggedAreaComponentState'; +import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState'; +import { parseCellIdToCoordinates } from '@/page-layout/utils/parseCellIdToCoordinates'; +import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; +import { useRecoilCallback } from 'recoil'; + +export const useCreateWidgetFromClick = () => { + const pageLayoutDraggedAreaState = useRecoilComponentCallbackState( + pageLayoutDraggedAreaComponentState, + ); + + const pageLayoutEditingWidgetIdState = useRecoilComponentCallbackState( + pageLayoutEditingWidgetIdComponentState, + ); + + const { navigatePageLayoutCommandMenu } = useNavigatePageLayoutCommandMenu(); + + const createWidgetFromClick = useRecoilCallback( + ({ set }) => + (cellId: string) => { + const { col, row } = parseCellIdToCoordinates(cellId); + const bounds = { x: col, y: row, w: 1, h: 1 }; + + set(pageLayoutDraggedAreaState, bounds); + set(pageLayoutEditingWidgetIdState, null); + + navigatePageLayoutCommandMenu({ + commandMenuPage: CommandMenuPages.PageLayoutWidgetTypeSelect, + resetNavigationStack: true, + }); + }, + [ + navigatePageLayoutCommandMenu, + pageLayoutDraggedAreaState, + pageLayoutEditingWidgetIdState, + ], + ); + + return { createWidgetFromClick }; +}; diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useEndPageLayoutDragSelection.ts b/packages/twenty-front/src/modules/page-layout/hooks/useEndPageLayoutDragSelection.ts index dc80c58d13..9175a5a99f 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/useEndPageLayoutDragSelection.ts +++ b/packages/twenty-front/src/modules/page-layout/hooks/useEndPageLayoutDragSelection.ts @@ -52,6 +52,7 @@ export const useEndPageLayoutDragSelection = ( navigatePageLayoutCommandMenu({ commandMenuPage: CommandMenuPages.PageLayoutWidgetTypeSelect, + resetNavigationStack: true, }); } } diff --git a/packages/twenty-front/src/modules/page-layout/widgets/components/DashboardWidgetPlaceholder.tsx b/packages/twenty-front/src/modules/page-layout/widgets/components/DashboardWidgetPlaceholder.tsx index ca47a81a45..95c4e3b59b 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/components/DashboardWidgetPlaceholder.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/components/DashboardWidgetPlaceholder.tsx @@ -38,6 +38,7 @@ export const DashboardWidgetPlaceholder = () => { } navigatePageLayoutCommandMenu({ commandMenuPage: CommandMenuPages.PageLayoutWidgetTypeSelect, + resetNavigationStack: true, }); };