diff --git a/apps/client/src/components/navbar/navbar.module.css b/apps/client/src/components/navbar/navbar.module.css index 6878e6c1..555c8869 100644 --- a/apps/client/src/components/navbar/navbar.module.css +++ b/apps/client/src/components/navbar/navbar.module.css @@ -85,3 +85,8 @@ color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0)); } } + +.activeButton { + background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0)); +} diff --git a/apps/client/src/components/navbar/navbar.tsx b/apps/client/src/components/navbar/navbar.tsx index 15e93f4f..45d331a9 100644 --- a/apps/client/src/components/navbar/navbar.tsx +++ b/apps/client/src/components/navbar/navbar.tsx @@ -20,25 +20,28 @@ import React from "react"; import { useAtom } from "jotai"; import { SearchSpotlight } from "@/features/search/search-spotlight"; import { treeApiAtom } from "@/features/page/tree/atoms/tree-api-atom"; -import { useNavigate } from "react-router-dom"; +import { useLocation, useNavigate } from "react-router-dom"; import SpaceContent from "@/features/page/tree/components/space-content.tsx"; +import clsx from "clsx"; +import APP_ROUTE from "@/lib/app-route.ts"; interface PrimaryMenuItem { icon: React.ElementType; label: string; + path?: string; onClick?: () => void; } const primaryMenu: PrimaryMenuItem[] = [ - { icon: IconHome, label: "Home" }, + { icon: IconHome, label: "Home", path: "/home" }, { icon: IconSearch, label: "Search" }, { icon: IconSettings, label: "Settings" }, - // { icon: IconFilePlus, label: 'New Page' }, ]; export function Navbar() { const [tree] = useAtom(treeApiAtom); const navigate = useNavigate(); + const location = useLocation(); const handleMenuItemClick = (label: string) => { if (label === "Home") { @@ -61,7 +64,12 @@ export function Navbar() { const primaryMenuItems = primaryMenu.map((menuItem) => ( handleMenuItemClick(menuItem.label)} >
diff --git a/apps/client/src/features/page/tree/components/space-tree.tsx b/apps/client/src/features/page/tree/components/space-tree.tsx index 616cfa96..f1eb3c38 100644 --- a/apps/client/src/features/page/tree/components/space-tree.tsx +++ b/apps/client/src/features/page/tree/components/space-tree.tsx @@ -8,7 +8,7 @@ import { useUpdatePageMutation, } from "@/features/page/queries/page-query.ts"; import React, { useEffect, useRef } from "react"; -import { useNavigate, useParams } from "react-router-dom"; +import { useLocation, useNavigate, useParams } from "react-router-dom"; import classes from "@/features/page/tree/styles/tree.module.css"; import { ActionIcon, Menu, rem, Text } from "@mantine/core"; import { @@ -45,6 +45,7 @@ import { useQueryEmit } from "@/features/websocket/use-query-emit.ts"; import { buildPageSlug } from "@/features/page/page.utils.ts"; import { notifications } from "@mantine/notifications"; import { modals } from "@mantine/modals"; +import APP_ROUTE from "@/lib/app-route.ts"; interface SpaceTreeProps { spaceId: string; @@ -72,6 +73,7 @@ export default function SpaceTree({ spaceId }: SpaceTreeProps) { const mergedRef = useMergedRef(rootElement, sizeRef); const isDataLoaded = useRef(false); const { data: currentPage } = usePageQuery(slugId); + const location = useLocation(); useEffect(() => { if (hasNextPage && !isFetching) { @@ -166,7 +168,7 @@ export default function SpaceTree({ spaceId }: SpaceTreeProps) { if (currentPage) { setTimeout(() => { treeApiRef.current?.select(currentPage.id, { align: "auto" }); - }, 200); + }, 100); } }, [currentPage?.id]); @@ -175,35 +177,38 @@ export default function SpaceTree({ spaceId }: SpaceTreeProps) { // @ts-ignore setTreeApi(treeApiRef.current); } - }, []); + }, [treeApiRef.current]); useEffect(() => { - if (openTreeNodes) { - treeApiRef.current.state.nodes.open.unfiltered = openTreeNodes; + if (location.pathname === APP_ROUTE.HOME && treeApiRef.current) { + treeApiRef.current.deselectAll(); } - }, []); + }, [location.pathname]); return (
- { - setOpenTreeNodes(treeApiRef.current.openState); - }} - > - {Node} - + {rootElement.current && ( + { + setOpenTreeNodes(treeApiRef.current.openState); + }} + initialOpenState={openTreeNodes} + > + {Node} + + )}
); } @@ -288,10 +293,16 @@ function Node({ node, style, dragHandle, tree }: NodeRendererProps) { }, 50); }; - if (node.willReceiveDrop && node.isClosed) { + if ( + node.willReceiveDrop && + node.isClosed && + (node.children.length > 0 || node.data.hasChildren) + ) { handleLoadChildren(node); setTimeout(() => { - if (node.state.willReceiveDrop) node.open(); + if (node.state.willReceiveDrop) { + node.open(); + } }, 650); } diff --git a/apps/client/src/features/page/tree/hooks/use-tree-mutation.ts b/apps/client/src/features/page/tree/hooks/use-tree-mutation.ts index 25a734a2..a0f3185b 100644 --- a/apps/client/src/features/page/tree/hooks/use-tree-mutation.ts +++ b/apps/client/src/features/page/tree/hooks/use-tree-mutation.ts @@ -124,6 +124,25 @@ export function useTreeMutation(spaceId: string) { changes: { position: newPosition } as any, }); + const previousParent = args.dragNodes[0].parent; + if ( + previousParent.id !== args.parentId && + previousParent.id !== "__REACT_ARBORIST_INTERNAL_ROOT__" + ) { + // if the page was moved to another parent, + // check if the previous still has children + // if no children left, change 'hasChildren' to false, to make the page toggle arrows work properly + const childrenCount = previousParent.children.filter( + (child) => child.id !== draggedNodeId, + ).length; + if (childrenCount === 0) { + tree.update({ + id: previousParent.id, + changes: { ...previousParent.data, hasChildren: false } as any, + }); + } + } + setData(tree.data); const payload: IMovePage = { diff --git a/apps/client/src/lib/api-client.ts b/apps/client/src/lib/api-client.ts index eb1bb485..fca1a57f 100644 --- a/apps/client/src/lib/api-client.ts +++ b/apps/client/src/lib/api-client.ts @@ -1,6 +1,6 @@ import axios, { AxiosInstance } from "axios"; import Cookies from "js-cookie"; -import Routes from "@/lib/routes"; +import Routes from "@/lib/app-route.ts"; const baseUrl = import.meta.env.DEV ? "http://localhost:3000" : ""; const api: AxiosInstance = axios.create({ diff --git a/apps/client/src/lib/app-route.ts b/apps/client/src/lib/app-route.ts new file mode 100644 index 00000000..e7c97665 --- /dev/null +++ b/apps/client/src/lib/app-route.ts @@ -0,0 +1,9 @@ +const APP_ROUTE = { + HOME: "/home", + AUTH: { + LOGIN: "/login", + SIGNUP: "/signup", + }, +}; + +export default APP_ROUTE; diff --git a/apps/client/src/lib/routes.ts b/apps/client/src/lib/routes.ts deleted file mode 100644 index c16f53b9..00000000 --- a/apps/client/src/lib/routes.ts +++ /dev/null @@ -1,9 +0,0 @@ -const ROUTES = { - HOME: '/home', - AUTH: { - LOGIN: '/login', - SIGNUP: '/signup', - }, -}; - -export default ROUTES; diff --git a/apps/server/src/core/page/services/page.service.ts b/apps/server/src/core/page/services/page.service.ts index 0cf7476e..d6d6a36f 100644 --- a/apps/server/src/core/page/services/page.service.ts +++ b/apps/server/src/core/page/services/page.service.ts @@ -184,7 +184,7 @@ export class PageService { throw new BadRequestException('Invalid move position'); } - let parentPageId: string; + let parentPageId = null; if (movedPage.parentPageId === dto.parentPageId) { parentPageId = undefined; } else {