Skip to content

Commit ab8d4da

Browse files
authored
Basics (#60)
* Basics * prettier * fix comments * create renderers for icons
1 parent 347e29a commit ab8d4da

28 files changed

+717
-18
lines changed

babel.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
module.exports = {
2+
plugins: ["emotion"],
23
presets: ["@babel/preset-env", "@babel/preset-react"]
34
};

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"postinstall": "preconstruct dev",
5050
"build": "preconstruct build",
5151
"test": "jest --coverage",
52+
"storybook": "yarn workspace @kenshooui/react-tree-docs storybook",
5253
"prettier": "prettier --check ./packages/**/*.js --ignore-path .gitignore",
5354
"prettier:fix": "prettier --check ./packages/**/*.js --ignore-path .gitignore --write",
5455
"release": "auto shipit"

packages/core/package.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,9 @@
1212
"devDependencies": {
1313
"react": "^16.12.0"
1414
},
15-
"scripts": {}
15+
"scripts": {},
16+
"dependencies": {
17+
"@emotion/core": "^10.0.28",
18+
"classnames": "^2.2.6"
19+
}
1620
}

packages/core/src/header/header.js

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/** @jsx jsx */
2+
import { jsx } from "@emotion/core";
3+
import React from "react";
4+
5+
export const BackIconRenderer = () => <>⬅️</>;
6+
7+
const Header = props => {
8+
const {
9+
parents = [],
10+
onClick,
11+
title = "",
12+
getStyles,
13+
backIconRenderer: BackIcon = BackIconRenderer
14+
} = props;
15+
return (
16+
<div css={getStyles("header", props)}>
17+
{parents.length > 0 && (
18+
<>
19+
<span css={getStyles("headerBackIcon", props)} onClick={onClick}>
20+
<BackIcon />
21+
</span>
22+
{parents[parents.length - 1]}
23+
</>
24+
)}
25+
{parents.length === 0 && <>{title}</>}
26+
</div>
27+
);
28+
};
29+
30+
export default Header;
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export const wrapperCss = () => ({
2+
height: "40px",
3+
display: "flex",
4+
alignItems: "center",
5+
padding: "0 6px"
6+
});
7+
8+
export const backIconCss = () => ({
9+
marginRight: "3px",
10+
cursor: "pointer"
11+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { useState } from "react";
2+
3+
const useItemCallbacks = onSelect => {
4+
const [currentDepth, setCurrentDepth] = useState(0);
5+
const [parents, setParents] = useState([]);
6+
7+
const onClick = (label, item, hasChild) => {
8+
if (hasChild) {
9+
setParents(parents.concat(label));
10+
setCurrentDepth(currentDepth + 1);
11+
} else {
12+
onSelect(item);
13+
}
14+
};
15+
16+
const onBackClick = () => {
17+
setParents(parents.filter((parent, index) => index < parents.length - 1));
18+
setCurrentDepth(currentDepth - 1);
19+
};
20+
21+
return {
22+
onClick,
23+
onBackClick,
24+
currentDepth,
25+
setCurrentDepth,
26+
parents,
27+
setParents
28+
};
29+
};
30+
31+
export default useItemCallbacks;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import {
2+
containsAllParents,
3+
filterBySearchTerm,
4+
buildLeafForItemRenderer,
5+
removeDuplicateLeafs
6+
} from "../util";
7+
import { useState, useEffect } from "react";
8+
9+
const useLeavesManager = ({ structure, parents, currentDepth }) => {
10+
const [searchTerm, setSearchTerm] = useState("");
11+
const [leaves, setLeaves] = useState([]);
12+
13+
useEffect(() => {
14+
const leaves = structure
15+
.filter(
16+
leaf =>
17+
filterBySearchTerm(leaf, searchTerm) &&
18+
containsAllParents(leaf, parents)
19+
)
20+
.map(leaf => buildLeafForItemRenderer(leaf, currentDepth, searchTerm));
21+
22+
setLeaves(removeDuplicateLeafs(leaves));
23+
}, [searchTerm, parents, currentDepth]);
24+
25+
return { searchTerm, setSearchTerm, leaves };
26+
};
27+
28+
export default useLeavesManager;

packages/core/src/index.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import React from "react";
1+
import Tree from "./tree";
22

3-
const HelloWorld = () => <div>Hello World</div>;
4-
5-
export default HelloWorld;
3+
export default Tree;

packages/core/src/input/input.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/** @jsx jsx */
2+
import { jsx } from "@emotion/core";
3+
import React from "react";
4+
5+
export const InputIconRenderer = () => <>🔍</>;
6+
7+
const Input = props => {
8+
const {
9+
searchTerm,
10+
setSearchTerm,
11+
getStyles,
12+
inputIconRenderer: InputIcon = InputIconRenderer
13+
} = props;
14+
return (
15+
<div css={getStyles("inputWrapper", props)}>
16+
<span css={getStyles("searchInput", props)}>
17+
<InputIcon />
18+
</span>
19+
<input
20+
css={getStyles("input", props)}
21+
value={searchTerm}
22+
onChange={e => setSearchTerm(e.target.value)}
23+
/>
24+
</div>
25+
);
26+
};
27+
28+
export default Input;

packages/core/src/input/inputStyle.js

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
export const css = () => ({
2+
width: "100%",
3+
height: "35px",
4+
paddingLeft: "35px",
5+
border: "1px solid #cfd1d2",
6+
borderRight: "none",
7+
borderLeft: "none",
8+
backgroundColor: "#F8F9FA",
9+
10+
"&:focus": {
11+
outline: "none !important"
12+
},
13+
14+
"&:active": {
15+
outline: "none !important"
16+
}
17+
});
18+
19+
export const searchIconCss = () => ({
20+
position: "absolute",
21+
top: "8px",
22+
left: "8px"
23+
});
24+
25+
export const wrapperCss = () => ({
26+
position: "relative"
27+
});

packages/core/src/item/basic_item.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import React from "react";
2+
3+
const BasicItem = ({ label = "" }) => {
4+
return <span>{label}</span>;
5+
};
6+
7+
export default BasicItem;

packages/core/src/item/item.js

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/** @jsx jsx */
2+
import { jsx } from "@emotion/core";
3+
import React from "react";
4+
5+
import SearchedItem from "./searched_item/searched_item";
6+
import BasicItem from "./basic_item";
7+
8+
export const ForwardIconRenderer = () => <>➡️</>;
9+
10+
const ItemRenderer = props => {
11+
const {
12+
getStyles,
13+
searchTerm = "",
14+
item: { item, hasChild, currentDepth } = {
15+
item: [""],
16+
hasChild: false,
17+
currentDepth: 0
18+
},
19+
onClick,
20+
forwardIconRenderer: ForwardIcon = ForwardIconRenderer
21+
} = props;
22+
const searchIndex = item[item.length - 1]
23+
.toLowerCase()
24+
.indexOf(searchTerm.trim().toLowerCase());
25+
return (
26+
<div
27+
onClick={() => onClick(item[currentDepth], item, hasChild)}
28+
css={getStyles("item", props)}
29+
>
30+
{searchTerm !== "" && (
31+
<SearchedItem
32+
item={item}
33+
searchIndex={searchIndex}
34+
searchTerm={searchTerm.trim()}
35+
getStyles={getStyles}
36+
/>
37+
)}
38+
{searchTerm === "" && (
39+
<BasicItem
40+
label={item[currentDepth]}
41+
searchIndex={searchIndex}
42+
searchTerm={searchTerm.trim()}
43+
/>
44+
)}
45+
{hasChild && (
46+
<span css={getStyles("forwardIcon", props)}>
47+
<ForwardIcon />
48+
</span>
49+
)}
50+
</div>
51+
);
52+
};
53+
54+
export default ItemRenderer;

packages/core/src/item/itemStyle.js

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
export const css = props => ({
2+
display: "flex",
3+
justifyContent: "space-between",
4+
alignItems: "center",
5+
cursor: "pointer",
6+
height: props.searchTerm !== "" ? "50px" : "30px",
7+
marginTop: "3px",
8+
padding: "0 6px",
9+
fontSize: "14px",
10+
11+
"&:hover": {
12+
backgroundColor: "#ECEEF3"
13+
},
14+
15+
"&:active": {
16+
backgroundColor: "#E1E4EB"
17+
}
18+
});
19+
20+
export const forwardIconCss = () => ({});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export const initialCss = props => ({
2+
color: "#545769"
3+
});
4+
5+
export const highlightCss = props => ({
6+
fontWeight: 600,
7+
color: "#2020e1"
8+
});
9+
10+
export const parentsCss = props => ({
11+
color: "#98A1B8",
12+
marginTop: "2px"
13+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/** @jsx jsx */
2+
import { jsx } from "@emotion/core";
3+
import React from "react";
4+
5+
const SearchedItem = props => {
6+
const { item = [""], searchIndex = 0, searchTerm = "", getStyles } = props;
7+
const leaf = item[item.length - 1];
8+
const parents = item.slice(0, item.length - 1).join(" / ");
9+
10+
return (
11+
<div>
12+
<div>
13+
<span css={getStyles("searchItemInitial", props)}>
14+
{leaf.substring(0, searchIndex)}
15+
</span>
16+
<span css={getStyles("highlight", props)}>
17+
{leaf.substring(searchIndex, searchIndex + searchTerm.length)}
18+
</span>
19+
<span css={getStyles("searchItemInitial", props)}>
20+
{leaf.substring(searchIndex + searchTerm.length)}
21+
</span>
22+
</div>
23+
<div css={getStyles("parents", props)}>{parents}</div>
24+
</div>
25+
);
26+
};
27+
28+
export default SearchedItem;

packages/core/src/items/itemsStyle.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export default () => ({
2+
maxHeight: "203px",
3+
overflowY: "auto"
4+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export const css = () => ({
2+
display: "flex",
3+
flexDirection: "column",
4+
alignItems: "center",
5+
marginTop: "30px"
6+
});
7+
8+
export const icon = () => ({
9+
fontSize: "20px",
10+
marginBottom: "5px"
11+
});
12+
13+
export const text = () => ({
14+
fontSize: "14px"
15+
});
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/** @jsx jsx */
2+
import { jsx } from "@emotion/core";
3+
import React from "react";
4+
5+
export const NoResultsIconRenderer = () => <>⚠️</>;
6+
7+
const NoResults = props => {
8+
const {
9+
text,
10+
getStyles,
11+
noResultsIconRenderer: NoResultsIcon = NoResultsIconRenderer
12+
} = props;
13+
return (
14+
<div css={getStyles("noResults", props)}>
15+
<div css={getStyles("noResultsIcon", props)}>
16+
<NoResultsIcon />
17+
</div>
18+
<div css={getStyles("noResultsText", props)}>{text}</div>
19+
</div>
20+
);
21+
};
22+
23+
export default NoResults;

0 commit comments

Comments
 (0)