Skip to content

Commit 3e898fb

Browse files
committed
Support Material-UI tree
1 parent ab8d4da commit 3e898fb

File tree

19 files changed

+627
-18
lines changed

19 files changed

+627
-18
lines changed
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { useEffect, useState } from "react";
2+
3+
const useTreeHeight = ({ ref }) => {
4+
const [treeHeight, setTreeHeight] = useState(0);
5+
6+
useEffect(() => {
7+
ref.current && setTreeHeight(ref.current.getBoundingClientRect().height);
8+
}, [ref]);
9+
10+
return treeHeight;
11+
};
12+
13+
export default useTreeHeight;

packages/core/src/items/items.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/** @jsx jsx */
2+
import { jsx } from "@emotion/core";
3+
import React from "react";
4+
5+
const ItemsRenderer = props => {
6+
const {
7+
getStyles,
8+
children
9+
} = props;
10+
return (
11+
<div css={getStyles("items", props)}>
12+
{children}
13+
</div>
14+
);
15+
};
16+
17+
export default ItemsRenderer;

packages/core/src/styles/styles.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import treeCss from "../treeStyle";
1+
import treeCss from "../tree_container/treeStyle";
22
import {
33
wrapperCss as headerWrapperCss,
44
backIconCss as headerBackIconCss

packages/core/src/tree.js

+14-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/** @jsx jsx */
22
import { jsx } from "@emotion/core";
3-
import React from "react";
3+
import React, { useRef } from "react";
44

55
import { defaultStyles } from "./styles/styles";
66
import ItemDefault from "./item/item";
@@ -10,23 +10,26 @@ import NoResultsDefault from "./no_results/no_results";
1010

1111
import useLeavesManager from "./hooks/use_leaves_manager";
1212
import useItemCallbacks from "./hooks/use_item_callbacks";
13+
import TreeContainerRenderer from "./tree_container/tree_container";
14+
import ItemsRenderer from "./items/items";
15+
import useTreeHeight from "./hooks/use_ref_height";
1316

1417
const Tree = props => {
1518
const {
1619
structure = [],
1720
title,
1821
onSelect,
19-
className,
2022
noResultsText = "No matching results",
21-
styles,
2223
headerRenderer: Header = HeaderDefault,
2324
backIconRenderer,
2425
inputRenderer: Input = InputDefault,
2526
inputIconRenderer,
2627
noResultsRenderer: NoResults = NoResultsDefault,
2728
noResultsIconRenderer,
2829
itemRenderer: Item = ItemDefault,
29-
forwardIconRenderer
30+
itemsRenderer: Items = ItemsRenderer,
31+
forwardIconRenderer,
32+
treeContainerRenderer: TreeContainer = TreeContainerRenderer
3033
} = props;
3134

3235
const getStyles = (key, props = {}) => {
@@ -36,6 +39,9 @@ const Tree = props => {
3639
return custom ? custom(base, props) : base;
3740
};
3841

42+
const ref = useRef();
43+
const treeHeight = useTreeHeight({ ref });
44+
3945
const { onClick, onBackClick, currentDepth, parents } = useItemCallbacks(
4046
onSelect
4147
);
@@ -47,7 +53,7 @@ const Tree = props => {
4753
});
4854

4955
return (
50-
<div css={getStyles("tree", props)}>
56+
<TreeContainer getStyles={getStyles} innerRef={ref}>
5157
<Header
5258
parents={parents}
5359
title={title}
@@ -63,7 +69,7 @@ const Tree = props => {
6369
setSearchTerm={setSearchTerm}
6470
inputIconRenderer={inputIconRenderer}
6571
/>
66-
<div css={getStyles("items", props)}>
72+
<Items getStyles={getStyles} treeHeight={treeHeight}>
6773
{leaves &&
6874
leaves.length > 0 &&
6975
leaves.map(item => (
@@ -82,8 +88,8 @@ const Tree = props => {
8288
noResultsIconRenderer={noResultsIconRenderer}
8389
/>
8490
)}
85-
</div>
86-
</div>
91+
</Items>
92+
</TreeContainer>
8793
);
8894
};
8995

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/** @jsx jsx */
2+
import { jsx } from "@emotion/core";
3+
import React from "react";
4+
5+
const TreeContainerRenderer = props => {
6+
const {
7+
getStyles,
8+
children
9+
} = props;
10+
return (
11+
<div css={getStyles("tree", props)}>
12+
{children}
13+
</div>
14+
);
15+
};
16+
17+
export default TreeContainerRenderer;

packages/docs/stories/core.stories.js

+37-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
11
import React from "react";
2+
import MaterialUITree from "@kenshooui/material_ui_tree";
23
import Tree from "@kenshooui/react-tree";
4+
import makeStyles from "@material-ui/core/styles/makeStyles";
5+
6+
const useStyles = makeStyles({
7+
root: {
8+
display: "flex",
9+
flexDirection: "row"
10+
},
11+
tree: {
12+
padding: 20
13+
}
14+
});
315

416
export default {
517
title: "Tree",
6-
component: Tree
18+
component: MaterialUITree
719
};
820

921
const structure = [
@@ -29,13 +41,30 @@ const structure = [
2941
["Keywords", "one level"]
3042
];
3143

32-
export const Basic = () => (
33-
<Tree
34-
structure={structure}
35-
title={"Choose an item"}
36-
onSelect={item => alert(item)}
37-
/>
38-
);
44+
export const Basic = () => {
45+
const classes = useStyles();
46+
47+
return (
48+
<div className={classes.root}>
49+
<div className={classes.tree}>
50+
<MaterialUITree
51+
structure={structure}
52+
title={"Choose an item"}
53+
onSelect={item => alert(item)}
54+
width={300}
55+
height={400}
56+
/>
57+
</div>
58+
<div className={classes.tree}>
59+
<Tree
60+
structure={structure}
61+
title={"Choose an item"}
62+
onSelect={item => alert(item)}
63+
/>
64+
</div>
65+
</div>
66+
);
67+
};
3968

4069
Basic.story = {
4170
name: "basic configuration"
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "@kenshooui/material_ui_tree",
3+
"version": "1.0.0",
4+
"main": "dist/material_ui_tree.cjs.js",
5+
"module": "dist/material_ui_tree.esm.js",
6+
"scripts": {},
7+
"repository": "https://github.com/kenshoo/react-tree.git",
8+
"author": "ui@kenshoo.com",
9+
"license": "MIT",
10+
"peerDependencies": {
11+
"@kenshooui/react-tree": "^1.0.0",
12+
"react": "^16.12.0"
13+
},
14+
"devDependencies": {
15+
"react": "^16.12.0"
16+
},
17+
"dependencies": {
18+
"@material-ui/core": "^4.11.2",
19+
"@material-ui/icons": "^4.11.2",
20+
"classnames": "^2.2.6"
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React from "react";
2+
import { ChevronLeft } from "@material-ui/icons";
3+
import Typography from "@material-ui/core/Typography";
4+
import CardActions from "@material-ui/core/CardActions";
5+
import Divider from "@material-ui/core/Divider";
6+
import IconButton from "@material-ui/core/IconButton";
7+
8+
const MaterialUIHeader = ({ parents = [], onClick, title = "" }) => {
9+
return (
10+
<>
11+
<CardActions>
12+
{parents.length > 0 && (
13+
<IconButton size={"small"}>
14+
<ChevronLeft onClick={onClick} />
15+
</IconButton>
16+
)}
17+
<Typography variant="subtitle1">
18+
{parents.length > 0 ? parents[parents.length - 1] : title}
19+
</Typography>
20+
</CardActions>
21+
<Divider />
22+
</>
23+
);
24+
};
25+
26+
export default MaterialUIHeader;
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import MaterialUITree from "./material_ui_tree";
2+
3+
export default MaterialUITree;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import React from "react";
2+
import { Clear, Search } from "@material-ui/icons";
3+
import InputAdornment from "@material-ui/core/InputAdornment";
4+
import InputBase from "@material-ui/core/InputBase";
5+
import Divider from "@material-ui/core/Divider";
6+
import CardActions from "@material-ui/core/CardActions";
7+
import IconButton from "@material-ui/core/IconButton";
8+
9+
const MaterialUIInputRenderer = ({
10+
searchTerm = "",
11+
setSearchTerm = () => {}
12+
}) => {
13+
return (
14+
<>
15+
<CardActions>
16+
<InputBase
17+
id="input-with-icon-adornment"
18+
value={searchTerm}
19+
onChange={e => setSearchTerm(e.target.value)}
20+
startAdornment={
21+
<InputAdornment position="start">
22+
<Search />
23+
</InputAdornment>
24+
}
25+
endAdornment={
26+
searchTerm !== "" && (
27+
<IconButton size={"small"}>
28+
<Clear onClick={() => setSearchTerm("")} />
29+
</IconButton>
30+
)
31+
}
32+
/>
33+
</CardActions>
34+
<Divider />
35+
</>
36+
);
37+
};
38+
39+
export default MaterialUIInputRenderer;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import React from "react";
2+
import ListItemText from "@material-ui/core/ListItemText";
3+
4+
const BasicItem = ({ label = "" }) => {
5+
return <ListItemText primary={label} />;
6+
};
7+
8+
export default BasicItem;
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React from "react";
2+
import BasicItem from "./basic_item";
3+
import { ChevronRight } from "@material-ui/icons";
4+
import ListItem from "@material-ui/core/ListItem";
5+
import SearchedItem from "./searched_item";
6+
7+
const MaterialUIItemRenderer = props => {
8+
const {
9+
searchTerm = "",
10+
item: { item, hasChild, currentDepth } = {
11+
item: [""],
12+
hasChild: false,
13+
currentDepth: 0
14+
},
15+
onClick = () => {}
16+
} = props;
17+
const searchIndex = item[item.length - 1]
18+
.toLowerCase()
19+
.indexOf(searchTerm.trim().toLowerCase());
20+
return (
21+
<ListItem
22+
button
23+
onClick={() => onClick(item[currentDepth], item, hasChild)}
24+
>
25+
{searchTerm !== "" && (
26+
<SearchedItem
27+
item={item}
28+
searchIndex={searchIndex}
29+
searchTerm={searchTerm.trim()}
30+
/>
31+
)}
32+
{searchTerm === "" && <BasicItem label={item[currentDepth]} />}
33+
{hasChild && <ChevronRight />}
34+
</ListItem>
35+
);
36+
};
37+
38+
export default MaterialUIItemRenderer;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import React from "react";
2+
import ListItemText from "@material-ui/core/ListItemText";
3+
import { makeStyles } from "@material-ui/core/styles";
4+
import Typography from "@material-ui/core/Typography";
5+
6+
const useStyles = makeStyles(() => ({
7+
boldText: {
8+
fontWeight: "bold"
9+
}
10+
}));
11+
12+
const SearchedItem = ({ item = [""], searchIndex = 0, searchTerm = "" }) => {
13+
const classes = useStyles();
14+
const leaf = item[item.length - 1];
15+
const parents = item.slice(0, item.length - 1).join(" / ");
16+
17+
return (
18+
<ListItemText
19+
primary={
20+
<>
21+
<Typography component="span">
22+
{leaf.substring(0, searchIndex)}
23+
</Typography>
24+
<Typography component="span" className={classes.boldText}>
25+
{leaf.substring(searchIndex, searchIndex + searchTerm.length)}
26+
</Typography>
27+
<Typography component="span">
28+
{leaf.substring(searchIndex + searchTerm.length)}
29+
</Typography>
30+
</>
31+
}
32+
secondary={parents}
33+
/>
34+
);
35+
};
36+
37+
export default SearchedItem;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import React from "react";
2+
import List from "@material-ui/core/List";
3+
import makeStyles from "@material-ui/core/styles/makeStyles";
4+
5+
const useStyles = makeStyles(({ treeHeight }) => ({
6+
root: {
7+
overflow: "auto",
8+
maxHeight: treeHeight - 48 - 42
9+
}
10+
}));
11+
12+
const MaterialUIItemsRenderer = ({ children, treeHeight }) => {
13+
const classes = useStyles({ treeHeight });
14+
return (
15+
<List className={classes.root} component="nav">
16+
{children}
17+
</List>
18+
);
19+
};
20+
21+
export default MaterialUIItemsRenderer;

0 commit comments

Comments
 (0)