1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Expanding parent nodes when scrolling along child ones

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
Alex Andreev 2021-03-09 11:26:02 +03:00
parent d0e25e90b0
commit 09a009049f
4 changed files with 67 additions and 15 deletions

View File

@ -306,6 +306,7 @@
"concurrently": "^5.2.0", "concurrently": "^5.2.0",
"css-element-queries": "^1.2.3", "css-element-queries": "^1.2.3",
"css-loader": "^3.5.3", "css-loader": "^3.5.3",
"deepdash-es": "^5.3.5",
"dompurify": "^2.0.11", "dompurify": "^2.0.11",
"electron": "^9.4.0", "electron": "^9.4.0",
"electron-builder": "^22.7.0", "electron-builder": "^22.7.0",

View File

@ -29,6 +29,7 @@ export function ScrollSpy(props: Props) {
sections.forEach(section => { sections.forEach(section => {
const id = section.getAttribute("id"); const id = section.getAttribute("id");
const parentId = section.parentElement.id;
const name = section.querySelector("h1, h2, h3, h4, h5, h6").textContent; const name = section.querySelector("h1, h2, h3, h4, h5, h6").textContent;
const selected = id === activeElementId; const selected = id === activeElementId;
@ -38,6 +39,7 @@ export function ScrollSpy(props: Props) {
children.push({ children.push({
id, id,
parentId,
name, name,
selected, selected,
children: getNavigation(section) children: getNavigation(section)
@ -56,6 +58,9 @@ export function ScrollSpy(props: Props) {
const observeSections = () => { const observeSections = () => {
const options: IntersectionObserverInit = { const options: IntersectionObserverInit = {
threshold: [0], threshold: [0],
// Shrinking root area from the bottom
// Allows to fire observer event only if target scrolled up to top of the page)
// https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#creating_an_intersection_observer
rootMargin: "0px 0px -85%", rootMargin: "0px 0px -85%",
}; };

View File

@ -1,14 +1,15 @@
import "./tree-view.scss"; import "./tree-view.scss";
import React from "react"; import React, { useEffect, useRef } from "react";
import { Icon } from "../icon"; import { Icon } from "../icon";
import TreeView from "@material-ui/lab/TreeView"; import TreeView from "@material-ui/lab/TreeView";
import TreeItem from "@material-ui/lab/TreeItem"; import TreeItem from "@material-ui/lab/TreeItem";
import { cssNames } from "../../utils"; import { cssNames } from "../../utils";
import flattenDeep from "lodash/flattenDeep"; import findDeep from "deepdash-es/findDeep";
export interface NavigationTree { export interface NavigationTree {
id: string; id: string;
parentId: string;
name: string; name: string;
selected?: boolean; selected?: boolean;
children?: NavigationTree[]; children?: NavigationTree[];
@ -18,14 +19,49 @@ interface Props {
data: NavigationTree[] data: NavigationTree[]
} }
const scrollToItem = (id: string) => {
const element = document.getElementById(id);
element?.scrollIntoView();
};
const getSelectedNode = (data: NavigationTree[]) => {
return findDeep(data, (value, key) => key === "selected" && value === true)?.parent;
};
export function RecursiveTreeView({ data }: Props) { export function RecursiveTreeView({ data }: Props) {
const [expanded, setExpanded] = React.useState<string[]>([]);
const prevData = useRef<NavigationTree[]>(data);
const handleToggle = (event: React.ChangeEvent<{}>, nodeIds: string[]) => {
setExpanded(nodeIds);
};
const expandTopLevelNodes = () => {
setExpanded(data.map(node => node.id));
};
const expandParentNode = () => {
const node = getSelectedNode(data) as any as NavigationTree;
const id = node?.parentId;
if (id && !expanded.includes(id)) {
setExpanded([...expanded, id]);
}
};
const onLabelClick = (event: React.MouseEvent, nodeId: string) => {
event.preventDefault();
scrollToItem(nodeId);
};
const renderTree = (nodes: NavigationTree[]) => { const renderTree = (nodes: NavigationTree[]) => {
return nodes.map(node => ( return nodes.map(node => (
<TreeItem <TreeItem
key={node.id} key={node.id}
nodeId={node.id} nodeId={node.id}
label={node.name} label={node.name}
onLabelClick={() => scrollToItem(node.id)} onLabelClick={(event) => onLabelClick(event, node.id)}
className={cssNames({selected: node.selected})} className={cssNames({selected: node.selected})}
> >
{Array.isArray(node.children) ? node.children.map((node) => renderTree([node])) : null} {Array.isArray(node.children) ? node.children.map((node) => renderTree([node])) : null}
@ -33,17 +69,14 @@ export function RecursiveTreeView({ data }: Props) {
)); ));
}; };
const scrollToItem = (id: string) => { useEffect(() => {
const element = document.getElementById(id); if (!prevData.current.length) {
expandTopLevelNodes();
element?.scrollIntoView(); } else {
}; expandParentNode();
}
const getAllNodeIds = (nodes: NavigationTree[]): string[] => { prevData.current = data;
return flattenDeep(nodes.map(node => { }, [data]);
return [node.id, ...node.children.map(item => getAllNodeIds([item]))];
}));
};
if (!data.length) { if (!data.length) {
return null; return null;
@ -52,7 +85,8 @@ export function RecursiveTreeView({ data }: Props) {
return ( return (
<TreeView <TreeView
className="TreeView" className="TreeView"
defaultExpanded={getAllNodeIds(data)} expanded={expanded}
onNodeToggle={handleToggle}
defaultCollapseIcon={<Icon material="expand_more"/>} defaultCollapseIcon={<Icon material="expand_more"/>}
defaultExpandIcon={<Icon material="chevron_right" />} defaultExpandIcon={<Icon material="chevron_right" />}
> >

View File

@ -4381,6 +4381,13 @@ deep-is@^0.1.3, deep-is@~0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
deepdash-es@^5.3.5:
version "5.3.5"
resolved "https://registry.yarnpkg.com/deepdash-es/-/deepdash-es-5.3.5.tgz#1217365f6508bb47f1f90def6b4f1c4b5f4ff6eb"
integrity sha512-bezxT+LqAu1Ly8I2LAEFle3fdEAc/bHld9cMAbgYzY+69+P9qTkGtNvC2ZQJEP4C1C2Fx9gVn/TCoQlAivWPDA==
dependencies:
lodash-es "^4.17.15"
deepmerge@^4.2.2: deepmerge@^4.2.2:
version "4.2.2" version "4.2.2"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
@ -8637,6 +8644,11 @@ lockfile@^1.0.4:
dependencies: dependencies:
signal-exit "^3.0.2" signal-exit "^3.0.2"
lodash-es@^4.17.15:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
lodash._baseuniq@~4.6.0: lodash._baseuniq@~4.6.0:
version "4.6.0" version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8" resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8"