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:
parent
d0e25e90b0
commit
09a009049f
@ -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",
|
||||||
|
|||||||
@ -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%",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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" />}
|
||||||
>
|
>
|
||||||
|
|||||||
12
yarn.lock
12
yarn.lock
@ -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"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user