mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-08 02:10:24 +00:00
feat(tag): display tags sidebar with tree (#7428)
This commit is contained in:
@@ -162,3 +162,26 @@ export const getAllSubTreeNodes = (nodeIndex, tree) => {
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
1
frontend/src/tag/constants/sidebar-tree.js
Normal file
1
frontend/src/tag/constants/sidebar-tree.js
Normal file
@@ -0,0 +1 @@
|
||||
export const SIDEBAR_INIT_LEFT_INDENT = 40;
|
@@ -2,7 +2,7 @@ import React, { useCallback, useContext, useEffect, useState } from 'react';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import tagsAPI from '../api';
|
||||
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';
|
||||
|
||||
// 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 getChildTagsIds = useCallback((nodeKey) => {
|
||||
if (!nodeKey) return [];
|
||||
const displayNode = getTreeNodeByKey(nodeKey, tagsData.key_tree_node_map);
|
||||
const getChildTagsIds = useCallback((tagID, nodeKey) => {
|
||||
let displayNode = null;
|
||||
if (nodeKey) {
|
||||
displayNode = getTreeNodeByKey(nodeKey, tagsData.key_tree_node_map);
|
||||
}
|
||||
if (!displayNode) {
|
||||
displayNode = getTreeNodeById(tagID, tagsData.rows_tree);
|
||||
}
|
||||
return getAllChildTagsIdsFromNode(displayNode);
|
||||
}, [tagsData]);
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
const childTagsIds = getChildTagsIds(nodeKey);
|
||||
const childTagsIds = getChildTagsIds(tagID, nodeKey);
|
||||
let tagsIds = [tagID];
|
||||
if (Array.isArray(childTagsIds) && childTagsIds.length > 0) {
|
||||
tagsIds.push(...childTagsIds);
|
||||
|
@@ -21,6 +21,7 @@ export const TagsProvider = ({ repoID, currentPath, selectTagsView, children, ..
|
||||
const [isLoading, setLoading] = useState(true);
|
||||
const [isReloading, setReloading] = useState(false);
|
||||
const [tagsData, setTagsData] = useState(null);
|
||||
const [displayNodeKey, setDisplayNodeKey] = useState('');
|
||||
|
||||
const storeRef = 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
|
||||
}, [enableMetadata, enableTags]);
|
||||
|
||||
const handelSelectTag = useCallback((tag, isSelected) => {
|
||||
const handleSelectTag = useCallback((tag, nodeKey, isSelected) => {
|
||||
if (isSelected) return;
|
||||
const id = getTagId(tag);
|
||||
const node = {
|
||||
@@ -111,21 +112,22 @@ export const TagsProvider = ({ repoID, currentPath, selectTagsView, children, ..
|
||||
key: repoID,
|
||||
tag_id: id,
|
||||
};
|
||||
setDisplayNodeKey(nodeKey || '');
|
||||
selectTagsView(node);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [repoID, selectTagsView]);
|
||||
|
||||
const addTag = useCallback((row, callback) => {
|
||||
return storeRef.current.addTags([row], callback);
|
||||
}, []);
|
||||
}, [storeRef]);
|
||||
|
||||
const addTags = useCallback((rows, callback) => {
|
||||
return storeRef.current.addTags(rows, callback);
|
||||
}, []);
|
||||
}, [storeRef]);
|
||||
|
||||
const addChildTag = useCallback((tagData, parentTagId, callback = {}) => {
|
||||
return storeRef.current.addChildTag(tagData, parentTagId, callback);
|
||||
}, []);
|
||||
}, [storeRef]);
|
||||
|
||||
const modifyTags = useCallback((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, {
|
||||
success_callback: (operation) => {
|
||||
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 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 } = {}) => {
|
||||
storeRef.current.addTagLinks(columnKey, tagId, otherTagsIds, success_callback, fail_callback);
|
||||
}, []);
|
||||
}, [storeRef]);
|
||||
|
||||
const deleteTagLinks = useCallback((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 } = {}) => {
|
||||
storeRef.current.mergeTags(target_tag_id, merged_tags_ids, success_callback, fail_callback);
|
||||
}, []);
|
||||
}, [storeRef]);
|
||||
|
||||
const modifyColumnWidth = useCallback((columnKey, newWidth) => {
|
||||
storeRef.current.modifyColumnWidth(columnKey, newWidth);
|
||||
}, [storeRef]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!handelSelectTag) return;
|
||||
if (!handleSelectTag) return;
|
||||
if (isLoading) return;
|
||||
const { search } = window.location;
|
||||
const urlParams = new URLSearchParams(search);
|
||||
@@ -215,17 +217,17 @@ export const TagsProvider = ({ repoID, currentPath, selectTagsView, children, ..
|
||||
const tagId = urlParams.get('tag');
|
||||
if (tagId) {
|
||||
if (tagId === ALL_TAGS_ID) {
|
||||
handelSelectTag({ [PRIVATE_COLUMN_KEY.ID]: ALL_TAGS_ID });
|
||||
handleSelectTag({ [PRIVATE_COLUMN_KEY.ID]: ALL_TAGS_ID });
|
||||
return;
|
||||
}
|
||||
|
||||
const lastOpenedTag = getRowById(tagsData, tagId);
|
||||
if (lastOpenedTag) {
|
||||
handelSelectTag(lastOpenedTag);
|
||||
handleSelectTag(lastOpenedTag);
|
||||
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
|
||||
}, [isLoading]);
|
||||
@@ -262,6 +264,7 @@ export const TagsProvider = ({ repoID, currentPath, selectTagsView, children, ..
|
||||
isLoading,
|
||||
isReloading,
|
||||
tagsData,
|
||||
displayNodeKey,
|
||||
currentPath,
|
||||
store: storeRef.current,
|
||||
context: contextRef.current,
|
||||
@@ -279,7 +282,7 @@ export const TagsProvider = ({ repoID, currentPath, selectTagsView, children, ..
|
||||
deleteTagLinks,
|
||||
mergeTags,
|
||||
updateLocalTag,
|
||||
selectTag: handelSelectTag,
|
||||
selectTag: handleSelectTag,
|
||||
modifyColumnWidth,
|
||||
}}>
|
||||
{children}
|
||||
|
@@ -2,23 +2,19 @@ import React, { useCallback, useMemo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import { PRIVATE_FILE_TYPE } from '../../../constants';
|
||||
import { PRIVATE_COLUMN_KEY, ALL_TAGS_ID } from '../../constants';
|
||||
import { useTags } from '../../hooks';
|
||||
import { ALL_TAGS_ID } from '../../constants';
|
||||
import { gettext } from '../../../utils/constants';
|
||||
|
||||
import './index.css';
|
||||
|
||||
const AllTags = ({ currentPath }) => {
|
||||
const { selectTag } = useTags();
|
||||
|
||||
const AllTags = ({ currentPath, selectAllTags }) => {
|
||||
const path = useMemo(() => '/' + PRIVATE_FILE_TYPE.TAGS_PROPERTIES + '/' + ALL_TAGS_ID, []);
|
||||
const isSelected = useMemo(() => currentPath === path, [currentPath, path]);
|
||||
|
||||
const handelClick = useCallback(() => {
|
||||
selectTag({
|
||||
[PRIVATE_COLUMN_KEY.ID]: ALL_TAGS_ID,
|
||||
}, isSelected);
|
||||
}, [isSelected, selectTag]);
|
||||
selectAllTags(isSelected);
|
||||
|
||||
}, [isSelected, selectAllTags]);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
@@ -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 {
|
||||
height: 100%;
|
||||
line-height: 1.5;
|
||||
height: 26px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
@@ -1,39 +1,124 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import AllTags from './all-tags';
|
||||
import Tag from './tag';
|
||||
import { useTags } from '../hooks';
|
||||
import { getTagId } from '../utils/cell';
|
||||
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';
|
||||
|
||||
const LOCAL_KEY_TREE_NODE_EXPANDED = 'sidebar_key_tree_node_expanded_map';
|
||||
|
||||
const TagsTreeView = ({ currentPath }) => {
|
||||
const { tagsData, selectTag } = useTags();
|
||||
const [currSelectedNodeKey, setCurrSelectedNodeKey] = useState('');
|
||||
const [keyTreeNodeExpandedMap, setKeyTreeNodeExpandedMap] = useState({});
|
||||
|
||||
const tags = useMemo(() => {
|
||||
if (!tagsData) return [];
|
||||
return tagsData.rows;
|
||||
const recordsTree = useMemo(() => {
|
||||
return tagsData.rows_tree || [];
|
||||
}, [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 (
|
||||
<div className="tree-view tree metadata-tree-view metadata-tree-view-tag">
|
||||
<div className="tree-node">
|
||||
<div className="children">
|
||||
{tags.slice(0, 20).map(tag => {
|
||||
const id = getTagId(tag);
|
||||
const tagPath = '/' + PRIVATE_FILE_TYPE.TAGS_PROPERTIES + '/' + id;
|
||||
const isSelected = currentPath === tagPath;
|
||||
{visibleRoots.map((node) => {
|
||||
const nodeKey = getTreeNodeKey(node);
|
||||
return (
|
||||
<Tag
|
||||
key={id}
|
||||
tag={tag}
|
||||
isSelected={isSelected}
|
||||
onClick={(tag) => selectTag(tag, isSelected)}
|
||||
key={`sidebar-tree-node-${nodeKey}`}
|
||||
node={node}
|
||||
expanded={checkNodeExpanded(nodeKey)}
|
||||
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>
|
||||
|
@@ -2,7 +2,6 @@
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
border-radius: 50%;
|
||||
transform: translateY(2px);
|
||||
}
|
||||
|
||||
.tag-tree-node .tag-tree-node-text {
|
||||
|
@@ -1,15 +1,51 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
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';
|
||||
|
||||
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 tagColor = useMemo(() => getTagColor(tag), [tag]);
|
||||
const tagCount = useMemo(() => getTagFilesCount(tag), [tag]);
|
||||
const [highlight, setHighlight] = useState(false);
|
||||
const tagCount = useMemo(() => {
|
||||
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(() => {
|
||||
setHighlight(true);
|
||||
@@ -23,30 +59,62 @@ const Tag = ({ isSelected, tag, onClick }) => {
|
||||
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 (
|
||||
<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
|
||||
className={classnames('tree-node-inner text-nowrap tag-tree-node', { 'tree-node-inner-hover': highlight, 'tree-node-hight-light': isSelected })}
|
||||
title={`${tagName} (${tagCount})`}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseOver={onMouseOver}
|
||||
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-count">{tagCount}</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="tag-tree-node-color" style={{ backgroundColor: tagColor }}></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{hasChildren && renderChildren()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Tag.propTypes = {
|
||||
isSelected: PropTypes.bool,
|
||||
tag: PropTypes.object,
|
||||
onClick: PropTypes.func,
|
||||
};
|
||||
|
@@ -6,7 +6,7 @@ import AllTags from './all-tags';
|
||||
import { ALL_TAGS_ID } from '../constants';
|
||||
|
||||
const Views = ({ ...params }) => {
|
||||
const { isLoading } = useTags();
|
||||
const { isLoading, displayNodeKey } = useTags();
|
||||
if (isLoading) return (<CenteredLoading />);
|
||||
|
||||
if (params.tagID === ALL_TAGS_ID) {
|
||||
@@ -14,7 +14,7 @@ const Views = ({ ...params }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<TagViewProvider { ...params }>
|
||||
<TagViewProvider { ...params } nodeKey={displayNodeKey}>
|
||||
<View />
|
||||
</TagViewProvider>
|
||||
);
|
||||
|
Reference in New Issue
Block a user