1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-09 10:50:24 +00:00

feat(tag): display tags sidebar with tree (#7428)

This commit is contained in:
Jerry Ren
2025-01-26 17:56:49 +08:00
committed by GitHub
parent 01791be348
commit 94914009c1
10 changed files with 246 additions and 77 deletions

View File

@@ -162,3 +162,26 @@ export const getAllSubTreeNodes = (nodeIndex, tree) => {
} }
return subNodes; return subNodes;
}; };
export const getTreeChildNodes = (parentNode, tree) => {
const parentNodeKey = getTreeNodeKey(parentNode);
const parentNodeIndex = tree.findIndex((node) => getTreeNodeKey(node) === parentNodeKey);
if (parentNodeIndex < 0) {
return [];
}
const parentNodeDepth = getTreeNodeDepth(parentNode);
const childNodeDepth = parentNodeDepth + 1;
let childNodes = [];
for (let i = parentNodeIndex + 1, len = tree.length; i < len; i++) {
const currentNode = tree[i];
if (!getTreeNodeKey(currentNode).includes(parentNodeKey)) {
break;
}
if (getTreeNodeDepth(currentNode) === childNodeDepth) {
childNodes.push({ ...currentNode });
}
}
return childNodes;
};

View File

@@ -0,0 +1 @@
export const SIDEBAR_INIT_LEFT_INDENT = 40;

View File

@@ -2,7 +2,7 @@ import React, { useCallback, useContext, useEffect, useState } from 'react';
import { Utils } from '../../utils/utils'; import { Utils } from '../../utils/utils';
import tagsAPI from '../api'; import tagsAPI from '../api';
import { useTags } from './tags'; import { useTags } from './tags';
import { getTreeNodeByKey } from '../../components/sf-table/utils/tree'; import { getTreeNodeById, getTreeNodeByKey } from '../../components/sf-table/utils/tree';
import { getAllChildTagsIdsFromNode } from '../utils/tree'; import { getAllChildTagsIdsFromNode } from '../utils/tree';
// This hook provides content related to seahub interaction, such as whether to enable extended attributes, views data, etc. // This hook provides content related to seahub interaction, such as whether to enable extended attributes, views data, etc.
@@ -15,15 +15,20 @@ export const TagViewProvider = ({ repoID, tagID, nodeKey, children, ...params })
const { tagsData } = useTags(); const { tagsData } = useTags();
const getChildTagsIds = useCallback((nodeKey) => { const getChildTagsIds = useCallback((tagID, nodeKey) => {
if (!nodeKey) return []; let displayNode = null;
const displayNode = getTreeNodeByKey(nodeKey, tagsData.key_tree_node_map); if (nodeKey) {
displayNode = getTreeNodeByKey(nodeKey, tagsData.key_tree_node_map);
}
if (!displayNode) {
displayNode = getTreeNodeById(tagID, tagsData.rows_tree);
}
return getAllChildTagsIdsFromNode(displayNode); return getAllChildTagsIdsFromNode(displayNode);
}, [tagsData]); }, [tagsData]);
useEffect(() => { useEffect(() => {
setLoading(true); setLoading(true);
const childTagsIds = getChildTagsIds(nodeKey); const childTagsIds = getChildTagsIds(tagID, nodeKey);
let tagsIds = [tagID]; let tagsIds = [tagID];
if (Array.isArray(childTagsIds) && childTagsIds.length > 0) { if (Array.isArray(childTagsIds) && childTagsIds.length > 0) {
tagsIds.push(...childTagsIds); tagsIds.push(...childTagsIds);

View File

@@ -21,6 +21,7 @@ export const TagsProvider = ({ repoID, currentPath, selectTagsView, children, ..
const [isLoading, setLoading] = useState(true); const [isLoading, setLoading] = useState(true);
const [isReloading, setReloading] = useState(false); const [isReloading, setReloading] = useState(false);
const [tagsData, setTagsData] = useState(null); const [tagsData, setTagsData] = useState(null);
const [displayNodeKey, setDisplayNodeKey] = useState('');
const storeRef = useRef(null); const storeRef = useRef(null);
const contextRef = useRef(null); const contextRef = useRef(null);
@@ -92,7 +93,7 @@ export const TagsProvider = ({ repoID, currentPath, selectTagsView, children, ..
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [enableMetadata, enableTags]); }, [enableMetadata, enableTags]);
const handelSelectTag = useCallback((tag, isSelected) => { const handleSelectTag = useCallback((tag, nodeKey, isSelected) => {
if (isSelected) return; if (isSelected) return;
const id = getTagId(tag); const id = getTagId(tag);
const node = { const node = {
@@ -111,21 +112,22 @@ export const TagsProvider = ({ repoID, currentPath, selectTagsView, children, ..
key: repoID, key: repoID,
tag_id: id, tag_id: id,
}; };
setDisplayNodeKey(nodeKey || '');
selectTagsView(node); selectTagsView(node);
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [repoID, selectTagsView]); }, [repoID, selectTagsView]);
const addTag = useCallback((row, callback) => { const addTag = useCallback((row, callback) => {
return storeRef.current.addTags([row], callback); return storeRef.current.addTags([row], callback);
}, []); }, [storeRef]);
const addTags = useCallback((rows, callback) => { const addTags = useCallback((rows, callback) => {
return storeRef.current.addTags(rows, callback); return storeRef.current.addTags(rows, callback);
}, []); }, [storeRef]);
const addChildTag = useCallback((tagData, parentTagId, callback = {}) => { const addChildTag = useCallback((tagData, parentTagId, callback = {}) => {
return storeRef.current.addChildTag(tagData, parentTagId, callback); return storeRef.current.addChildTag(tagData, parentTagId, callback);
}, []); }, [storeRef]);
const modifyTags = useCallback((tagIds, idTagUpdates, idOriginalRowUpdates, idOldRowData, idOriginalOldRowData, { success_callback, fail_callback }) => { const modifyTags = useCallback((tagIds, idTagUpdates, idOriginalRowUpdates, idOldRowData, idOriginalOldRowData, { success_callback, fail_callback }) => {
storeRef.current.modifyTags(tagIds, idTagUpdates, idOriginalRowUpdates, idOldRowData, idOriginalOldRowData, { success_callback, fail_callback }); storeRef.current.modifyTags(tagIds, idTagUpdates, idOriginalRowUpdates, idOldRowData, idOriginalOldRowData, { success_callback, fail_callback });
@@ -149,10 +151,10 @@ export const TagsProvider = ({ repoID, currentPath, selectTagsView, children, ..
addTag(newTag, { addTag(newTag, {
success_callback: (operation) => { success_callback: (operation) => {
const copiedTag = operation.tags[0]; const copiedTag = operation.tags[0];
handelSelectTag(copiedTag); handleSelectTag(copiedTag);
} }
}); });
}, [tagsData, addTag, handelSelectTag]); }, [tagsData, addTag, handleSelectTag]);
const updateTag = useCallback((tagId, update, { success_callback, fail_callback } = { }) => { const updateTag = useCallback((tagId, update, { success_callback, fail_callback } = { }) => {
const tag = getRowById(tagsData, tagId); const tag = getRowById(tagsData, tagId);
@@ -192,22 +194,22 @@ export const TagsProvider = ({ repoID, currentPath, selectTagsView, children, ..
const addTagLinks = useCallback((columnKey, tagId, otherTagsIds, { success_callback, fail_callback } = {}) => { const addTagLinks = useCallback((columnKey, tagId, otherTagsIds, { success_callback, fail_callback } = {}) => {
storeRef.current.addTagLinks(columnKey, tagId, otherTagsIds, success_callback, fail_callback); storeRef.current.addTagLinks(columnKey, tagId, otherTagsIds, success_callback, fail_callback);
}, []); }, [storeRef]);
const deleteTagLinks = useCallback((columnKey, tagId, otherTagsIds, { success_callback, fail_callback } = {}) => { const deleteTagLinks = useCallback((columnKey, tagId, otherTagsIds, { success_callback, fail_callback } = {}) => {
storeRef.current.deleteTagLinks(columnKey, tagId, otherTagsIds, success_callback, fail_callback); storeRef.current.deleteTagLinks(columnKey, tagId, otherTagsIds, success_callback, fail_callback);
}, []); }, [storeRef]);
const mergeTags = useCallback((target_tag_id, merged_tags_ids, { success_callback, fail_callback } = {}) => { const mergeTags = useCallback((target_tag_id, merged_tags_ids, { success_callback, fail_callback } = {}) => {
storeRef.current.mergeTags(target_tag_id, merged_tags_ids, success_callback, fail_callback); storeRef.current.mergeTags(target_tag_id, merged_tags_ids, success_callback, fail_callback);
}, []); }, [storeRef]);
const modifyColumnWidth = useCallback((columnKey, newWidth) => { const modifyColumnWidth = useCallback((columnKey, newWidth) => {
storeRef.current.modifyColumnWidth(columnKey, newWidth); storeRef.current.modifyColumnWidth(columnKey, newWidth);
}, [storeRef]); }, [storeRef]);
useEffect(() => { useEffect(() => {
if (!handelSelectTag) return; if (!handleSelectTag) return;
if (isLoading) return; if (isLoading) return;
const { search } = window.location; const { search } = window.location;
const urlParams = new URLSearchParams(search); const urlParams = new URLSearchParams(search);
@@ -215,17 +217,17 @@ export const TagsProvider = ({ repoID, currentPath, selectTagsView, children, ..
const tagId = urlParams.get('tag'); const tagId = urlParams.get('tag');
if (tagId) { if (tagId) {
if (tagId === ALL_TAGS_ID) { if (tagId === ALL_TAGS_ID) {
handelSelectTag({ [PRIVATE_COLUMN_KEY.ID]: ALL_TAGS_ID }); handleSelectTag({ [PRIVATE_COLUMN_KEY.ID]: ALL_TAGS_ID });
return; return;
} }
const lastOpenedTag = getRowById(tagsData, tagId); const lastOpenedTag = getRowById(tagsData, tagId);
if (lastOpenedTag) { if (lastOpenedTag) {
handelSelectTag(lastOpenedTag); handleSelectTag(lastOpenedTag);
return; return;
} }
handelSelectTag({ [PRIVATE_COLUMN_KEY.ID]: ALL_TAGS_ID }); handleSelectTag({ [PRIVATE_COLUMN_KEY.ID]: ALL_TAGS_ID });
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [isLoading]); }, [isLoading]);
@@ -262,6 +264,7 @@ export const TagsProvider = ({ repoID, currentPath, selectTagsView, children, ..
isLoading, isLoading,
isReloading, isReloading,
tagsData, tagsData,
displayNodeKey,
currentPath, currentPath,
store: storeRef.current, store: storeRef.current,
context: contextRef.current, context: contextRef.current,
@@ -279,7 +282,7 @@ export const TagsProvider = ({ repoID, currentPath, selectTagsView, children, ..
deleteTagLinks, deleteTagLinks,
mergeTags, mergeTags,
updateLocalTag, updateLocalTag,
selectTag: handelSelectTag, selectTag: handleSelectTag,
modifyColumnWidth, modifyColumnWidth,
}}> }}>
{children} {children}

View File

@@ -2,23 +2,19 @@ import React, { useCallback, useMemo } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classnames from 'classnames'; import classnames from 'classnames';
import { PRIVATE_FILE_TYPE } from '../../../constants'; import { PRIVATE_FILE_TYPE } from '../../../constants';
import { PRIVATE_COLUMN_KEY, ALL_TAGS_ID } from '../../constants'; import { ALL_TAGS_ID } from '../../constants';
import { useTags } from '../../hooks';
import { gettext } from '../../../utils/constants'; import { gettext } from '../../../utils/constants';
import './index.css'; import './index.css';
const AllTags = ({ currentPath }) => { const AllTags = ({ currentPath, selectAllTags }) => {
const { selectTag } = useTags();
const path = useMemo(() => '/' + PRIVATE_FILE_TYPE.TAGS_PROPERTIES + '/' + ALL_TAGS_ID, []); const path = useMemo(() => '/' + PRIVATE_FILE_TYPE.TAGS_PROPERTIES + '/' + ALL_TAGS_ID, []);
const isSelected = useMemo(() => currentPath === path, [currentPath, path]); const isSelected = useMemo(() => currentPath === path, [currentPath, path]);
const handelClick = useCallback(() => { const handelClick = useCallback(() => {
selectTag({ selectAllTags(isSelected);
[PRIVATE_COLUMN_KEY.ID]: ALL_TAGS_ID,
}, isSelected); }, [isSelected, selectAllTags]);
}, [isSelected, selectTag]);
return ( return (
<div <div

View File

@@ -1,17 +1,6 @@
.metadata-tree-view-tag .tree-node-inner .left-icon {
top: 5px;
padding-left: 8px;
}
.metadata-tree-view-tag .tree-node-inner .tree-node-text {
padding-left: 28px;
}
.metadata-tree-view-tag .tree-node-icon { .metadata-tree-view-tag .tree-node-icon {
height: 100%; height: 26px;
line-height: 1.5;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
transform: translateY(1px);
} }

View File

@@ -1,39 +1,124 @@
import React, { useMemo } from 'react'; import React, { useCallback, useEffect, useMemo, useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import AllTags from './all-tags'; import AllTags from './all-tags';
import Tag from './tag'; import Tag from './tag';
import { useTags } from '../hooks'; import { useTags } from '../hooks';
import { getTagId } from '../utils/cell';
import { PRIVATE_FILE_TYPE } from '../../constants'; import { PRIVATE_FILE_TYPE } from '../../constants';
import { PRIVATE_COLUMN_KEY, ALL_TAGS_ID } from '../constants';
import { checkTreeNodeHasChildNodes, getTreeChildNodes, getTreeNodeDepth, getTreeNodeId, getTreeNodeKey } from '../../components/sf-table/utils/tree';
import { getRowById } from '../../metadata/utils/table';
import { SIDEBAR_INIT_LEFT_INDENT } from '../constants/sidebar-tree';
import './index.css'; import './index.css';
const LOCAL_KEY_TREE_NODE_EXPANDED = 'sidebar_key_tree_node_expanded_map';
const TagsTreeView = ({ currentPath }) => { const TagsTreeView = ({ currentPath }) => {
const { tagsData, selectTag } = useTags(); const { tagsData, selectTag } = useTags();
const [currSelectedNodeKey, setCurrSelectedNodeKey] = useState('');
const [keyTreeNodeExpandedMap, setKeyTreeNodeExpandedMap] = useState({});
const tags = useMemo(() => { const recordsTree = useMemo(() => {
if (!tagsData) return []; return tagsData.rows_tree || [];
return tagsData.rows;
}, [tagsData]); }, [tagsData]);
const buildTree = useCallback((roots, tree) => {
roots.forEach((node) => {
const childNodes = checkTreeNodeHasChildNodes(node) ? getTreeChildNodes(node, tree) : [];
if (childNodes.length > 0) {
node.children = childNodes;
buildTree(node.children, tree);
}
});
}, []);
const visibleRoots = useMemo(() => {
let roots = recordsTree.filter((node) => getTreeNodeDepth(node) === 0);
roots = roots.slice(0, 20);
buildTree(roots, recordsTree);
return roots;
}, [recordsTree, buildTree]);
const getKeyTreeNodeExpandedMap = useCallback(() => {
const strKeyTreeNodeExpandedMap = window.sfTagsDataContext.localStorage.getItem(LOCAL_KEY_TREE_NODE_EXPANDED);
if (strKeyTreeNodeExpandedMap) {
try {
return JSON.parse(strKeyTreeNodeExpandedMap);
} catch {
return {};
}
}
return {};
}, []);
const storeKeyTreeNodeExpandedMap = useCallback((keyTreeNodeExpandedMap) => {
window.sfTagsDataContext.localStorage.setItem(LOCAL_KEY_TREE_NODE_EXPANDED, JSON.stringify(keyTreeNodeExpandedMap));
}, []);
const checkNodeExpanded = useCallback((nodeKey) => {
return !!keyTreeNodeExpandedMap[nodeKey];
}, [keyTreeNodeExpandedMap]);
const toggleExpanded = useCallback((nodeKey, expanded) => {
let updatedKeyTreeNodeExpandedMap = { ...keyTreeNodeExpandedMap };
if (expanded) {
delete updatedKeyTreeNodeExpandedMap[nodeKey];
} else {
updatedKeyTreeNodeExpandedMap[nodeKey] = true;
}
storeKeyTreeNodeExpandedMap(updatedKeyTreeNodeExpandedMap);
setKeyTreeNodeExpandedMap(updatedKeyTreeNodeExpandedMap);
}, [keyTreeNodeExpandedMap, storeKeyTreeNodeExpandedMap]);
const selectNode = useCallback((node) => {
const tagId = getTreeNodeId(node);
const tag = getRowById(tagsData, tagId);
const nodeKey = getTreeNodeKey(node);
selectTag(tag, nodeKey);
setCurrSelectedNodeKey(nodeKey);
}, [tagsData, selectTag]);
const selectAllTags = useCallback((isSelected) => {
selectTag({ [PRIVATE_COLUMN_KEY.ID]: ALL_TAGS_ID }, isSelected);
setCurrSelectedNodeKey('');
}, [selectTag]);
useEffect(() => {
if (!currSelectedNodeKey) {
const selectedNode = recordsTree.find((node) => {
const nodePath = '/' + PRIVATE_FILE_TYPE.TAGS_PROPERTIES + '/' + getTreeNodeId(node);
return nodePath === currentPath;
});
const nextSelectedNodeKey = getTreeNodeKey(selectedNode);
setCurrSelectedNodeKey(nextSelectedNodeKey);
}
}, [currentPath, currSelectedNodeKey, recordsTree]);
useEffect(() => {
setKeyTreeNodeExpandedMap(getKeyTreeNodeExpandedMap());
}, [getKeyTreeNodeExpandedMap]);
return ( return (
<div className="tree-view tree metadata-tree-view metadata-tree-view-tag"> <div className="tree-view tree metadata-tree-view metadata-tree-view-tag">
<div className="tree-node"> <div className="tree-node">
<div className="children"> <div className="children">
{tags.slice(0, 20).map(tag => { {visibleRoots.map((node) => {
const id = getTagId(tag); const nodeKey = getTreeNodeKey(node);
const tagPath = '/' + PRIVATE_FILE_TYPE.TAGS_PROPERTIES + '/' + id;
const isSelected = currentPath === tagPath;
return ( return (
<Tag <Tag
key={id} key={`sidebar-tree-node-${nodeKey}`}
tag={tag} node={node}
isSelected={isSelected} expanded={checkNodeExpanded(nodeKey)}
onClick={(tag) => selectTag(tag, isSelected)} currentPath={currentPath}
leftIndent={SIDEBAR_INIT_LEFT_INDENT}
selectedNodeKey={currSelectedNodeKey}
checkNodeExpanded={checkNodeExpanded}
toggleExpanded={toggleExpanded}
selectNode={selectNode}
/> />
); );
})} })}
<AllTags currentPath={currentPath} /> <AllTags currentPath={currentPath} selectAllTags={selectAllTags} />
</div> </div>
</div> </div>
</div> </div>

View File

@@ -2,7 +2,6 @@
height: 12px; height: 12px;
width: 12px; width: 12px;
border-radius: 50%; border-radius: 50%;
transform: translateY(2px);
} }
.tag-tree-node .tag-tree-node-text { .tag-tree-node .tag-tree-node-text {

View File

@@ -1,15 +1,51 @@
import React, { useCallback, useMemo, useState } from 'react'; import React, { useCallback, useMemo, useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classnames from 'classnames'; import classnames from 'classnames';
import { getTagColor, getTagName, getTagFilesCount } from '../../utils/cell'; import { getTagColor, getTagName, getTagFilesLinks } from '../../utils/cell';
import { checkTreeNodeHasChildNodes, getTreeNodeId, getTreeNodeKey } from '../../../components/sf-table/utils/tree';
import { getRowById } from '../../../metadata/utils/table';
import { useTags } from '../../hooks';
import { SIDEBAR_INIT_LEFT_INDENT } from '../../constants/sidebar-tree';
import { getAllChildTagsIdsFromNode } from '../../utils/tree';
import './index.css'; import './index.css';
const Tag = ({ isSelected, tag, onClick }) => { const LEFT_INDENT_UNIT = 20;
const NODE_TEXT_LEFT_INDENT_UNIT = 5;
const Tag = ({ node, currentPath, leftIndent, selectedNodeKey, expanded, checkNodeExpanded, toggleExpanded, selectNode }) => {
const { tagsData } = useTags();
const [highlight, setHighlight] = useState(false);
const tagId = useMemo(() => {
return getTreeNodeId(node);
}, [node]);
const tag = useMemo(() => {
return getRowById(tagsData, tagId);
}, [tagsData, tagId]);
const hasChildren = useMemo(() => checkTreeNodeHasChildNodes(node), [node]);
const nodeKey = useMemo(() => getTreeNodeKey(node), [node]);
const tagName = useMemo(() => getTagName(tag), [tag]); const tagName = useMemo(() => getTagName(tag), [tag]);
const tagColor = useMemo(() => getTagColor(tag), [tag]); const tagColor = useMemo(() => getTagColor(tag), [tag]);
const tagCount = useMemo(() => getTagFilesCount(tag), [tag]); const tagCount = useMemo(() => {
const [highlight, setHighlight] = useState(false); const filesLinks = getTagFilesLinks(tag);
let allFilesLinks = [...filesLinks];
const childTagsIds = getAllChildTagsIdsFromNode(node);
childTagsIds.forEach((childTagId) => {
const childTag = getRowById(tagsData, childTagId);
const childFilesLinks = getTagFilesLinks(childTag);
if (childFilesLinks && childFilesLinks.length > 0) {
allFilesLinks.push(...childFilesLinks);
}
});
return allFilesLinks.length;
}, [node, tag, tagsData]);
const isSelected = useMemo(() => {
return nodeKey === selectedNodeKey;
}, [nodeKey, selectedNodeKey]);
const onMouseEnter = useCallback(() => { const onMouseEnter = useCallback(() => {
setHighlight(true); setHighlight(true);
@@ -23,30 +59,62 @@ const Tag = ({ isSelected, tag, onClick }) => {
setHighlight(false); setHighlight(false);
}, []); }, []);
const onToggleExpanded = useCallback((event) => {
event.stopPropagation();
toggleExpanded(nodeKey, expanded);
}, [nodeKey, expanded, toggleExpanded]);
const renderChildren = useCallback(() => {
const { children } = node;
if (!expanded || !hasChildren || !Array.isArray(children) || children.length === 0) {
return null;
}
return children.map((childNode) => {
const childNodeKey = getTreeNodeKey(childNode);
return ( return (
<Tag
key={`sidebar-tree-node-${childNodeKey}`}
node={childNode}
expanded={checkNodeExpanded(childNodeKey)}
selectedNodeKey={selectedNodeKey}
leftIndent={leftIndent + LEFT_INDENT_UNIT}
currentPath={currentPath}
checkNodeExpanded={checkNodeExpanded}
toggleExpanded={toggleExpanded}
selectNode={selectNode}
/>
);
});
}, [currentPath, node, selectedNodeKey, hasChildren, leftIndent, expanded, checkNodeExpanded, toggleExpanded, selectNode]);
return (
<div className="tree-node">
<div <div
className={classnames('tree-node-inner text-nowrap tag-tree-node', { 'tree-node-inner-hover': highlight, 'tree-node-hight-light': isSelected })} className={classnames('tree-node-inner text-nowrap tag-tree-node', { 'tree-node-inner-hover': highlight, 'tree-node-hight-light': isSelected })}
title={`${tagName} (${tagCount})`} title={`${tagName} (${tagCount})`}
onMouseEnter={onMouseEnter} onMouseEnter={onMouseEnter}
onMouseOver={onMouseOver} onMouseOver={onMouseOver}
onMouseLeave={onMouseLeave} onMouseLeave={onMouseLeave}
onClick={() => onClick(tag)} onClick={() => selectNode(node)}
> >
<div className="tree-node-text tag-tree-node-text"> <div className="tree-node-text tag-tree-node-text" style={{ paddingLeft: leftIndent + NODE_TEXT_LEFT_INDENT_UNIT }}>
<div className="tag-tree-node-name">{tagName}</div> <div className="tag-tree-node-name">{tagName}</div>
<div className="tag-tree-node-count">{tagCount}</div> <div className="tag-tree-node-count">{tagCount}</div>
</div> </div>
<div className="left-icon"> <div className="left-icon" style={{ left: leftIndent - SIDEBAR_INIT_LEFT_INDENT }}>
{hasChildren && <i className={classnames('folder-toggle-icon sf3-font sf3-font-down', { 'rotate-270': !expanded })} onClick={onToggleExpanded}></i>}
<div className="tree-node-icon"> <div className="tree-node-icon">
<div className="tag-tree-node-color" style={{ backgroundColor: tagColor }}></div> <div className="tag-tree-node-color" style={{ backgroundColor: tagColor }}></div>
</div> </div>
</div> </div>
</div> </div>
{hasChildren && renderChildren()}
</div>
); );
}; };
Tag.propTypes = { Tag.propTypes = {
isSelected: PropTypes.bool,
tag: PropTypes.object, tag: PropTypes.object,
onClick: PropTypes.func, onClick: PropTypes.func,
}; };

View File

@@ -6,7 +6,7 @@ import AllTags from './all-tags';
import { ALL_TAGS_ID } from '../constants'; import { ALL_TAGS_ID } from '../constants';
const Views = ({ ...params }) => { const Views = ({ ...params }) => {
const { isLoading } = useTags(); const { isLoading, displayNodeKey } = useTags();
if (isLoading) return (<CenteredLoading />); if (isLoading) return (<CenteredLoading />);
if (params.tagID === ALL_TAGS_ID) { if (params.tagID === ALL_TAGS_ID) {
@@ -14,7 +14,7 @@ const Views = ({ ...params }) => {
} }
return ( return (
<TagViewProvider { ...params }> <TagViewProvider { ...params } nodeKey={displayNodeKey}>
<View /> <View />
</TagViewProvider> </TagViewProvider>
); );