This commit is contained in:
Nathan Panchout
2025-02-21 08:12:56 +01:00
parent 68b185e49a
commit 8bc56f0000
8 changed files with 117 additions and 32 deletions

View File

@@ -8,6 +8,7 @@ import {
NodeRendererProps,
Tree,
} from 'react-arborist';
import { OpenMap } from 'react-arborist/dist/module/state/open-slice';
import {
BaseType,
@@ -27,6 +28,7 @@ export type TreeViewProps<T> = {
width?: number | string;
selectedNodeId?: string;
rootNodeId: string;
initialOpenState?: OpenMap;
renderNode: (
props: NodeRendererProps<TreeViewDataType<T>>,
) => React.ReactNode;
@@ -43,6 +45,7 @@ export const TreeView = <T,>({
renderNode,
afterMove,
selectedNodeId,
initialOpenState,
}: TreeViewProps<T>) => {
const onMove3 = (args: {
dragIds: string[];
@@ -223,6 +226,7 @@ export const TreeView = <T,>({
height={1000}
indent={20}
width={width}
initialOpenState={initialOpenState}
selection={selectedNodeId}
disableEdit={true}
rowHeight={32}
@@ -326,7 +330,11 @@ export const TreeViewNode = <T,>({
</Box>
) : (
<Icon
onClick={() => void handleClick()}
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
void handleClick();
}}
$variation="500"
$size="16px"
iconName={

View File

@@ -84,9 +84,11 @@ export const createTreeStore = <T>(
return filteredNodes.map((node) => {
if (node.children) {
const children = removeNodeFromTree(node.children);
return {
...node,
children: removeNodeFromTree(node.children),
children: children,
childrenCount: children.length,
};
}
return node;

View File

@@ -28,6 +28,7 @@ export function useDoc(
return useQuery<Doc, APIError, Doc>({
queryKey: [KEY_DOC, param],
queryFn: () => getDoc(param),
...queryConfig,
});
}

View File

@@ -17,16 +17,20 @@ import { Doc } from '../types';
interface ModalRemoveDocProps {
onClose: () => void;
doc: Doc;
afterDelete?: (doc: Doc) => void;
}
export const ModalRemoveDoc = ({ onClose, doc }: ModalRemoveDocProps) => {
export const ModalRemoveDoc = ({
onClose,
doc,
afterDelete,
}: ModalRemoveDocProps) => {
const { toast } = useToastProvider();
const { push } = useRouter();
const pathname = usePathname();
const {
mutate: removeDoc,
isError,
error,
} = useRemoveDoc({
@@ -34,6 +38,11 @@ export const ModalRemoveDoc = ({ onClose, doc }: ModalRemoveDocProps) => {
toast(t('The document has been deleted.'), VariantType.SUCCESS, {
duration: 4000,
});
if (afterDelete) {
afterDelete(doc);
return;
}
if (pathname === '/') {
onClose();
} else {

View File

@@ -46,6 +46,7 @@ export interface Doc {
created_at: string;
updated_at: string;
numchild: number;
children?: Doc[];
abilities: {
accesses_manage: boolean;
accesses_view: boolean;

View File

@@ -47,6 +47,8 @@ export function useDocTree(
return useQuery<Doc[], APIError, Doc[]>({
queryKey: [KEY_LIST_DOC_CHILDREN, params],
queryFn: () => getDocTree(params),
staleTime: 0,
gcTime: 0,
...queryConfig,
});
}

View File

@@ -1,4 +1,5 @@
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import { OpenMap } from 'react-arborist/dist/module/state/open-slice';
import { css } from 'styled-components';
import { fetchAPI } from '@/api';
@@ -7,7 +8,7 @@ import { TreeView } from '@/components/common/tree/TreeView';
import { useTreeStore } from '@/components/common/tree/treeStore';
import { useCunninghamTheme } from '@/cunningham';
import { Doc, useDoc } from '../../doc-management';
import { Doc } from '../../doc-management';
import { SimpleDocItem } from '../../docs-grid';
import { useDocTree } from '../api/useDocTree';
import { TreeViewDataType, TreeViewMoveResult } from '../types/tree';
@@ -20,9 +21,14 @@ type Props = {
export type DocTreeDataType = TreeViewDataType<Doc>;
export const DocTree = ({ docId }: Props) => {
const [rootNode, setRootNode] = useState<Doc | null>(null);
const { spacingsTokens } = useCunninghamTheme();
const spacing = spacingsTokens();
const { data: rootNode } = useDoc({ id: docId });
const [initialOpenState, setInitialOpenState] = useState<OpenMap | undefined>(
undefined,
);
const {
selectedNode,
setSelectedNode,
@@ -62,18 +68,46 @@ export const DocTree = ({ docId }: Props) => {
return;
}
console.log(data);
const newChildren = data ?? [];
const root = newChildren.shift();
console.log('root', root);
const newData: DocTreeDataType[] = newChildren.map((child) => ({
...child,
childrenCount: child.numchild,
children: [],
parentId: docId,
}));
setTreeDataStore(newData);
}, [data, setTreeDataStore, docId]);
const initialOpenState: OpenMap = {};
const root = data[0];
initialOpenState[root.id] = true;
const serialize = (
children: Doc[],
parentId: Doc['id'],
): DocTreeDataType[] => {
if (children.length === 0) {
return [];
}
return children.map((child) => {
if (child?.children?.length && child?.children?.length > 0) {
initialOpenState[child.id] = true;
}
if (docId === child.id) {
setSelectedNode(child);
}
const node = {
...child,
childrenCount: child.numchild,
children: serialize(child.children ?? [], child.id),
parentId: parentId,
};
if (child?.children?.length && child?.children?.length > 0) {
initialOpenState[child.id] = true;
}
return node;
});
};
root.children = serialize(root.children ?? [], docId);
console.log('initialOpenState', initialOpenState);
setInitialOpenState(initialOpenState);
setRootNode(root);
setTreeDataStore(root.children ?? []);
}, [data, setTreeDataStore, docId, setSelectedNode, rootNode]);
const isRootNodeSelected = !selectedNode
? true
@@ -114,16 +148,19 @@ export const DocTree = ({ docId }: Props) => {
</Box>
</SeparatedSection>
<Box $padding={{ all: 'sm' }} $margin={{ top: '-35px' }} $width="100%">
<TreeView
treeData={treeDataStore}
width="100%"
selectedNodeId={selectedNode?.id}
rootNodeId={docId}
renderNode={(props) => <DocTreeItem {...props} />}
afterMove={(result, newTreeData) => {
void afterMove(result, newTreeData);
}}
/>
{initialOpenState && treeDataStore.length > 0 && (
<TreeView
initialOpenState={initialOpenState}
treeData={treeDataStore}
width="100%"
selectedNodeId={selectedNode?.id}
rootNodeId={docId}
renderNode={(props) => <DocTreeItem {...props} />}
afterMove={(result, newTreeData) => {
void afterMove(result, newTreeData);
}}
/>
)}
</Box>
</>
);

View File

@@ -25,11 +25,15 @@ type DocTreeItemProps = NodeRendererProps<TreeViewDataType<DocTreeDataType>>;
export const DocTreeItem = ({ node, ...props }: DocTreeItemProps) => {
const data = node.data;
const { push } = useRouter();
const deleteModal = useModal();
const shareModal = useModal();
const renameModal = useModal();
const [isOpen, setIsOpen] = useState(false);
const { updateNode, setSelectedNode } = useTreeStore();
const { updateNode, setSelectedNode, removeNode, refreshNode } =
useTreeStore();
const { spacingsTokens } = useCunninghamTheme();
const { refetch } = useDocChildren(
{
@@ -54,9 +58,16 @@ export const DocTreeItem = ({ node, ...props }: DocTreeItemProps) => {
})
.catch(console.error);
} else {
const newDoc = {
...doc,
children: [],
childrenCount: 0,
parentId: node.id,
};
updateNode(node.id, {
...node.data,
children: [...actualChildren, doc],
children: [...actualChildren, newDoc],
childrenCount: actualChildren.length + 1,
});
node.open();
router.push(`/docs/${doc.id}`);
@@ -84,6 +95,16 @@ export const DocTreeItem = ({ node, ...props }: DocTreeItemProps) => {
return newChilds;
};
const afterDelete = () => {
console.log('afterDelete', node.data);
if (node.data.parentId) {
router.push(`/docs/${node.data.parentId}`);
refreshNode(node.data.parentId);
setSelectedNode(node.data);
}
removeNode(node.data.id);
};
const options = [
{
label: t('Rename'),
@@ -140,7 +161,11 @@ export const DocTreeItem = ({ node, ...props }: DocTreeItemProps) => {
/>
</TreeViewNode>
{deleteModal.isOpen && (
<ModalRemoveDoc onClose={deleteModal.onClose} doc={node.data} />
<ModalRemoveDoc
onClose={deleteModal.onClose}
doc={node.data}
afterDelete={afterDelete}
/>
)}
{shareModal.isOpen && (
<DocShareModal doc={node.data} onClose={shareModal.close} />