|
@@ -1,7 +1,11 @@
|
|
|
-import React, { useCallback, useState, FC } from 'react';
|
|
|
|
|
|
|
+import React, {
|
|
|
|
|
+ useCallback, useState, FC, useEffect,
|
|
|
|
|
+} from 'react';
|
|
|
|
|
+import nodePath from 'path';
|
|
|
|
|
|
|
|
import { ItemNode } from './ItemNode';
|
|
import { ItemNode } from './ItemNode';
|
|
|
import { useSWRxPageChildren } from '../../../stores/page-listing';
|
|
import { useSWRxPageChildren } from '../../../stores/page-listing';
|
|
|
|
|
+import { usePageId } from '../../../stores/context';
|
|
|
|
|
|
|
|
|
|
|
|
|
interface ItemProps {
|
|
interface ItemProps {
|
|
@@ -9,15 +13,27 @@ interface ItemProps {
|
|
|
isOpen?: boolean
|
|
isOpen?: boolean
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// Utility to mark target
|
|
|
|
|
+const markTarget = (children: ItemNode[], targetId: string): void => {
|
|
|
|
|
+ children.forEach((node) => {
|
|
|
|
|
+ if (node.page._id === targetId) {
|
|
|
|
|
+ node.page.isTarget = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ return node;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ return;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
const Item: FC<ItemProps> = (props: ItemProps) => {
|
|
const Item: FC<ItemProps> = (props: ItemProps) => {
|
|
|
const { itemNode, isOpen: _isOpen = false } = props;
|
|
const { itemNode, isOpen: _isOpen = false } = props;
|
|
|
|
|
|
|
|
const { page, children } = itemNode;
|
|
const { page, children } = itemNode;
|
|
|
|
|
|
|
|
const [currentChildren, setCurrentChildren] = useState(children);
|
|
const [currentChildren, setCurrentChildren] = useState(children);
|
|
|
-
|
|
|
|
|
const [isOpen, setIsOpen] = useState(_isOpen);
|
|
const [isOpen, setIsOpen] = useState(_isOpen);
|
|
|
|
|
|
|
|
|
|
+ const { data: targetId } = usePageId();
|
|
|
const { data, error } = useSWRxPageChildren(isOpen ? page._id : null);
|
|
const { data, error } = useSWRxPageChildren(isOpen ? page._id : null);
|
|
|
|
|
|
|
|
const hasChildren = useCallback((): boolean => {
|
|
const hasChildren = useCallback((): boolean => {
|
|
@@ -28,28 +44,47 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
|
|
|
setIsOpen(!isOpen);
|
|
setIsOpen(!isOpen);
|
|
|
}, [isOpen]);
|
|
}, [isOpen]);
|
|
|
|
|
|
|
|
|
|
+ // didMount
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if (hasChildren()) setIsOpen(true);
|
|
|
|
|
+ }, []);
|
|
|
|
|
+
|
|
|
/*
|
|
/*
|
|
|
- * When swr fetch succeeded
|
|
|
|
|
|
|
+ * Make sure itemNode.children and currentChildren are synced
|
|
|
*/
|
|
*/
|
|
|
- if (isOpen && error == null && data != null) {
|
|
|
|
|
- const { children } = data;
|
|
|
|
|
- itemNode.children = ItemNode.generateNodesFromPages(children);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if (children.length > currentChildren.length) {
|
|
|
|
|
+ markTarget(children, targetId);
|
|
|
|
|
+ setCurrentChildren(children);
|
|
|
|
|
+ }
|
|
|
|
|
+ }, []);
|
|
|
|
|
|
|
|
- // make sure itemNode.children and currentChildren are synced
|
|
|
|
|
- if (children.length > currentChildren.length) {
|
|
|
|
|
- setCurrentChildren(children);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ /*
|
|
|
|
|
+ * When swr fetch succeeded
|
|
|
|
|
+ */
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if (isOpen && error == null && data != null) {
|
|
|
|
|
+ const newChildren = ItemNode.generateNodesFromPages(data.children);
|
|
|
|
|
+ markTarget(newChildren, targetId);
|
|
|
|
|
+ setCurrentChildren(newChildren);
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [data]);
|
|
|
|
|
|
|
|
// TODO: improve style
|
|
// TODO: improve style
|
|
|
- const style = { margin: '10px', opacity: 1.0 };
|
|
|
|
|
- if (page.isTarget) style.opacity = 0.7;
|
|
|
|
|
|
|
+ const opacityStyle = { opacity: 1.0 };
|
|
|
|
|
+ if (page.isTarget) opacityStyle.opacity = 0.7;
|
|
|
|
|
+ if (isOpen) opacityStyle.opacity = 0.5;
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
- <div style={style}>
|
|
|
|
|
- <p><button type="button" className="btn btn-light p-1" onClick={onClickLoadChildren}>Load</button> {page.path}</p>
|
|
|
|
|
|
|
+ <div style={{ margin: '10px' }}>
|
|
|
|
|
+ <div style={opacityStyle}>
|
|
|
|
|
+ <button type="button" className="d-inline-block btn btn-light p-1 mr-1" onClick={onClickLoadChildren}>Load</button>
|
|
|
|
|
+ <a href={page._id} className="d-inline-block">
|
|
|
|
|
+ <p>{nodePath.basename(page.path as string) || '/'}</p>
|
|
|
|
|
+ </a>
|
|
|
|
|
+ </div>
|
|
|
{
|
|
{
|
|
|
- hasChildren() && currentChildren.map(node => (
|
|
|
|
|
|
|
+ isOpen && hasChildren() && currentChildren.map(node => (
|
|
|
<Item
|
|
<Item
|
|
|
key={node.page._id}
|
|
key={node.page._id}
|
|
|
itemNode={node}
|
|
itemNode={node}
|