* fix tree root element ref
* fix tree node toggle bug
* fix tree node reparenting on the backend
* highlight active home menu on the sidebar
This commit is contained in:
Philipinho
2024-05-20 01:08:31 +01:00
parent 6287f41ef6
commit b06a78b6ec
8 changed files with 85 additions and 42 deletions

View File

@ -85,3 +85,8 @@
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0)); 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));
}

View File

@ -20,25 +20,28 @@ import React from "react";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { SearchSpotlight } from "@/features/search/search-spotlight"; import { SearchSpotlight } from "@/features/search/search-spotlight";
import { treeApiAtom } from "@/features/page/tree/atoms/tree-api-atom"; 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 SpaceContent from "@/features/page/tree/components/space-content.tsx";
import clsx from "clsx";
import APP_ROUTE from "@/lib/app-route.ts";
interface PrimaryMenuItem { interface PrimaryMenuItem {
icon: React.ElementType; icon: React.ElementType;
label: string; label: string;
path?: string;
onClick?: () => void; onClick?: () => void;
} }
const primaryMenu: PrimaryMenuItem[] = [ const primaryMenu: PrimaryMenuItem[] = [
{ icon: IconHome, label: "Home" }, { icon: IconHome, label: "Home", path: "/home" },
{ icon: IconSearch, label: "Search" }, { icon: IconSearch, label: "Search" },
{ icon: IconSettings, label: "Settings" }, { icon: IconSettings, label: "Settings" },
// { icon: IconFilePlus, label: 'New Page' },
]; ];
export function Navbar() { export function Navbar() {
const [tree] = useAtom(treeApiAtom); const [tree] = useAtom(treeApiAtom);
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation();
const handleMenuItemClick = (label: string) => { const handleMenuItemClick = (label: string) => {
if (label === "Home") { if (label === "Home") {
@ -61,7 +64,12 @@ export function Navbar() {
const primaryMenuItems = primaryMenu.map((menuItem) => ( const primaryMenuItems = primaryMenu.map((menuItem) => (
<UnstyledButton <UnstyledButton
key={menuItem.label} key={menuItem.label}
className={classes.menu} className={clsx(
classes.menu,
location.pathname.toLowerCase() === menuItem?.path
? classes.activeButton
: "",
)}
onClick={() => handleMenuItemClick(menuItem.label)} onClick={() => handleMenuItemClick(menuItem.label)}
> >
<div className={classes.menuItemInner}> <div className={classes.menuItemInner}>

View File

@ -8,7 +8,7 @@ import {
useUpdatePageMutation, useUpdatePageMutation,
} from "@/features/page/queries/page-query.ts"; } from "@/features/page/queries/page-query.ts";
import React, { useEffect, useRef } from "react"; 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 classes from "@/features/page/tree/styles/tree.module.css";
import { ActionIcon, Menu, rem, Text } from "@mantine/core"; import { ActionIcon, Menu, rem, Text } from "@mantine/core";
import { import {
@ -45,6 +45,7 @@ import { useQueryEmit } from "@/features/websocket/use-query-emit.ts";
import { buildPageSlug } from "@/features/page/page.utils.ts"; import { buildPageSlug } from "@/features/page/page.utils.ts";
import { notifications } from "@mantine/notifications"; import { notifications } from "@mantine/notifications";
import { modals } from "@mantine/modals"; import { modals } from "@mantine/modals";
import APP_ROUTE from "@/lib/app-route.ts";
interface SpaceTreeProps { interface SpaceTreeProps {
spaceId: string; spaceId: string;
@ -72,6 +73,7 @@ export default function SpaceTree({ spaceId }: SpaceTreeProps) {
const mergedRef = useMergedRef(rootElement, sizeRef); const mergedRef = useMergedRef(rootElement, sizeRef);
const isDataLoaded = useRef(false); const isDataLoaded = useRef(false);
const { data: currentPage } = usePageQuery(slugId); const { data: currentPage } = usePageQuery(slugId);
const location = useLocation();
useEffect(() => { useEffect(() => {
if (hasNextPage && !isFetching) { if (hasNextPage && !isFetching) {
@ -166,7 +168,7 @@ export default function SpaceTree({ spaceId }: SpaceTreeProps) {
if (currentPage) { if (currentPage) {
setTimeout(() => { setTimeout(() => {
treeApiRef.current?.select(currentPage.id, { align: "auto" }); treeApiRef.current?.select(currentPage.id, { align: "auto" });
}, 200); }, 100);
} }
}, [currentPage?.id]); }, [currentPage?.id]);
@ -175,35 +177,38 @@ export default function SpaceTree({ spaceId }: SpaceTreeProps) {
// @ts-ignore // @ts-ignore
setTreeApi(treeApiRef.current); setTreeApi(treeApiRef.current);
} }
}, []); }, [treeApiRef.current]);
useEffect(() => { useEffect(() => {
if (openTreeNodes) { if (location.pathname === APP_ROUTE.HOME && treeApiRef.current) {
treeApiRef.current.state.nodes.open.unfiltered = openTreeNodes; treeApiRef.current.deselectAll();
} }
}, []); }, [location.pathname]);
return ( return (
<div ref={mergedRef} className={classes.treeContainer}> <div ref={mergedRef} className={classes.treeContainer}>
<Tree {rootElement.current && (
data={data} <Tree
{...controllers} data={data}
width={width} {...controllers}
height={height} width={width}
ref={treeApiRef} height={height}
openByDefault={false} ref={treeApiRef}
disableMultiSelection={true} openByDefault={false}
className={classes.tree} disableMultiSelection={true}
rowClassName={classes.row} className={classes.tree}
rowHeight={30} rowClassName={classes.row}
overscanCount={10} rowHeight={30}
dndRootElement={rootElement.current} overscanCount={10}
onToggle={() => { dndRootElement={rootElement.current}
setOpenTreeNodes(treeApiRef.current.openState); onToggle={() => {
}} setOpenTreeNodes(treeApiRef.current.openState);
> }}
{Node} initialOpenState={openTreeNodes}
</Tree> >
{Node}
</Tree>
)}
</div> </div>
); );
} }
@ -288,10 +293,16 @@ function Node({ node, style, dragHandle, tree }: NodeRendererProps<any>) {
}, 50); }, 50);
}; };
if (node.willReceiveDrop && node.isClosed) { if (
node.willReceiveDrop &&
node.isClosed &&
(node.children.length > 0 || node.data.hasChildren)
) {
handleLoadChildren(node); handleLoadChildren(node);
setTimeout(() => { setTimeout(() => {
if (node.state.willReceiveDrop) node.open(); if (node.state.willReceiveDrop) {
node.open();
}
}, 650); }, 650);
} }

View File

@ -124,6 +124,25 @@ export function useTreeMutation<T>(spaceId: string) {
changes: { position: newPosition } as any, 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); setData(tree.data);
const payload: IMovePage = { const payload: IMovePage = {

View File

@ -1,6 +1,6 @@
import axios, { AxiosInstance } from "axios"; import axios, { AxiosInstance } from "axios";
import Cookies from "js-cookie"; 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 baseUrl = import.meta.env.DEV ? "http://localhost:3000" : "";
const api: AxiosInstance = axios.create({ const api: AxiosInstance = axios.create({

View File

@ -0,0 +1,9 @@
const APP_ROUTE = {
HOME: "/home",
AUTH: {
LOGIN: "/login",
SIGNUP: "/signup",
},
};
export default APP_ROUTE;

View File

@ -1,9 +0,0 @@
const ROUTES = {
HOME: '/home',
AUTH: {
LOGIN: '/login',
SIGNUP: '/signup',
},
};
export default ROUTES;

View File

@ -184,7 +184,7 @@ export class PageService {
throw new BadRequestException('Invalid move position'); throw new BadRequestException('Invalid move position');
} }
let parentPageId: string; let parentPageId = null;
if (movedPage.parentPageId === dto.parentPageId) { if (movedPage.parentPageId === dto.parentPageId) {
parentPageId = undefined; parentPageId = undefined;
} else { } else {