Skip to content

Commit 1a4f7f0

Browse files
authored
Merge pull request #80 from webdoc-labs/feature/e-search
Feature: Explorer search!
2 parents 8d711cf + c7e6d19 commit 1a4f7f0

File tree

13 files changed

+5521
-4632
lines changed

13 files changed

+5521
-4632
lines changed

packages/webdoc-default-template/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@
8181
"sass-loader": "^9.0.3",
8282
"webpack": "^4.43.0",
8383
"webpack-stream": "^5.2.1",
84-
"redux": "~4.0.5",
85-
"react-redux": "~7.2.2"
84+
"redux": "^4.0.5",
85+
"react-redux": "^7.2.2",
86+
"lodash": "^4.17.20"
8687
}
8788
}

packages/webdoc-default-template/src/app/components/Explorer/ExplorerCategoryItem.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ export default function ExplorerCategoryItem(props) {
2323
label={props.title}
2424
>
2525
{props.data.map(
26-
(explorerTarget, i) => (<ExplorerItem key={i} data={explorerTarget} />),
26+
(explorerTarget, i) => (
27+
<ExplorerItem key={i} data={explorerTarget} toggle={props.toggle} />
28+
),
2729
)}
2830
</TreeItem>
2931
);
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import React from "react";
2+
import {connect} from "react-redux";
3+
import debounce from "lodash/debounce";
4+
import store from "../../store";
5+
6+
function filterTree(query, collector, data) {
7+
data.$match = !query || (!!data.title && data.title.toLowerCase().includes(query));
8+
9+
if (data.children) {
10+
for (const [key, value] of Object.entries(data.children)) {
11+
if (key.charAt(0) === "$") {
12+
continue;
13+
}
14+
filterTree(query, collector, value);
15+
data.$match = data.$match || value.$match;
16+
}
17+
}
18+
19+
if (query && data.$match) {
20+
collector.add(data.$nodeId);
21+
}
22+
}
23+
24+
export default connect(() => ({
25+
nextQuery: (query, items) => store.dispatch({
26+
type: "setQuery",
27+
value: query,
28+
items,
29+
}),
30+
}))(function ExplorerFilter(props) {
31+
const onNativeChange = React.useCallback(
32+
debounce((e) => {
33+
const query = e.target.value;
34+
const items = new Set();
35+
36+
const startish = Date.now();
37+
filterTree(query.toLowerCase(), items, props.data);
38+
console.log("Search took: " + Date.now() - startish + " ms");
39+
props.nextQuery(query, items);
40+
}, 200, {
41+
maxWait: 1000,
42+
}),
43+
[props.data],
44+
);
45+
46+
const onChange = React.useCallback(
47+
(e) => onNativeChange(e.nativeEvent),
48+
[onNativeChange],
49+
);
50+
51+
return (
52+
<input
53+
onChange={onChange}
54+
placeholder="Filter"
55+
type="text"
56+
/>
57+
);
58+
});

packages/webdoc-default-template/src/app/components/Explorer/ExplorerItem.js

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import {useExplorerCategoryStyles, useExplorerStyles} from "./useExplorerStyles";
2-
import ExplorerTargetGroup from "./ExplorerCategoryItem";
2+
import ExplorerCategoryItem from "./ExplorerCategoryItem";
33
import Link from "@material-ui/core/Link";
4-
//import React from "react";
4+
import React from "react";
55
import TreeItem from "@material-ui/lab/TreeItem";
6-
import cuid from "cuid";
76

87
export default function ExplorerItem(props) {
9-
if (!props.data.nodeId) {
10-
props.data.nodeId = cuid();
8+
if (!props.data.$nodeId) {
9+
throw new Error("Ids must be assigned");
1110
}
1211

1312
const classesItem = useExplorerStyles();
@@ -17,13 +16,23 @@ export default function ExplorerItem(props) {
1716
let i = 0;
1817
for (const [key, value] of Object.entries(props.data.children || {})) {
1918
targetChildren.push(Array.isArray(value) ?
20-
(<ExplorerTargetGroup key={i} title={key} data={value} />) :
21-
(<ExplorerItem key={i} data={value} />),
19+
(<ExplorerCategoryItem key={i} title={key} data={value} toggle={props.toggle} />) :
20+
(<ExplorerItem key={i} data={value} toggle={props.toggle} />),
2221
);
2322
i++;
2423
}
2524

2625
const classes = i > 0 ? classesCategory : classesItem;
26+
const nodeId = props.data.$nodeId;
27+
28+
const toggle = React.useCallback(
29+
() => props.toggle(nodeId),
30+
[nodeId],
31+
);
32+
33+
if (props.data.title !== "(overview)" && props.data.$match === false) {
34+
return null;
35+
}
2736

2837
return (
2938
<TreeItem
@@ -33,7 +42,8 @@ export default function ExplorerItem(props) {
3342
iconContainer: classes.iconContainer,
3443
selected: classes.selected,
3544
}}
36-
nodeId={props.data.nodeId}
45+
onClick={toggle}
46+
nodeId={nodeId}
3747
label={
3848
props.data.page ?
3949
(

packages/webdoc-default-template/src/app/components/Explorer/index.js

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,63 @@
11
import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown";
22
import ArrowRightIcon from "@material-ui/icons/ArrowRight";
3+
import ExplorerFilter from "./ExplorerFilter";
34
import ExplorerHeader from "./ExplorerHeader";
45
import ExplorerTarget from "./ExplorerItem";
56
import ExplorerTargetGroup from "./ExplorerCategoryItem";
67
import React from "react";
78
import TreeView from "@material-ui/lab/TreeView";
89
import {connect} from "react-redux";
10+
import cuid from "cuid";
911
import store from "../../store";
1012
import {useExplorerStyles} from "./useExplorerStyles";
1113

1214
let fetched = false;
1315

14-
export default connect(({explorerOpen}) => ({
16+
function makeIds(data) {
17+
data.$nodeId = cuid();
18+
19+
if (data.children) {
20+
for (const [, value] of Object.entries(data.children)) {
21+
makeIds(value);
22+
}
23+
}
24+
}
25+
26+
export default connect(({
27+
expandedItems,
28+
explorerOpen,
29+
query,
30+
}) => ({
1531
isOpen: explorerOpen,
16-
setOpen: (isOpen) => store.dispatch({type: "setExplorerOpen", value: isOpen}),
32+
query, /* Added for re-rendering when query updates */
33+
setOpen: (isOpen) => store.dispatch({
34+
type: "setExplorerOpen",
35+
value: isOpen,
36+
}),
37+
expandedItems: Array.from(expandedItems),
38+
toggleItem: (nodeId, optValue) => store.dispatch({
39+
type: "toggleItem",
40+
nodeId,
41+
value: optValue,
42+
}),
1743
}))(function Explorer({
1844
isOpen,
1945
setOpen,
46+
expandedItems,
47+
toggleItem,
2048
}) {
2149
const [data, setData] = React.useState(null);
2250
const {root} = useExplorerStyles();
2351
const toggleOpen = React.useCallback(() => setOpen(!isOpen), [isOpen]);
2452
const children = [];
53+
const sitePrefix = window.appData.siteRoot ? "/" + window.appData.siteRoot + "/" : "/";
2554

2655
if (!fetched) {
27-
fetch("/" + window.appData.siteRoot + "/explorer/reference.json")
56+
fetch(sitePrefix + "explorer/reference.json")
2857
.then((response) => {
2958
if (response.ok) {
3059
response.json().then((idata) => {
31-
// console.log(idata);
60+
makeIds(idata);
3261
setData(idata || {});
3362
});
3463
} else {
@@ -44,9 +73,13 @@ export default connect(({explorerOpen}) => ({
4473
let i = 0;
4574
if (data) {
4675
for (const [key, value] of Object.entries(data.children || {})) {
76+
if (value.$match === false) {
77+
continue;
78+
}
79+
4780
children.push(Array.isArray(value) ?
48-
(<ExplorerTargetGroup key={i} title={key} data={value} />) :
49-
(<ExplorerTarget key={i} data={value} />),
81+
(<ExplorerTargetGroup key={i} title={key} data={value} toggle={toggleItem} />) :
82+
(<ExplorerTarget key={i} data={value} toggle={toggleItem} />),
5083
);
5184
i++;
5285
}
@@ -66,7 +99,9 @@ export default connect(({explorerOpen}) => ({
6699
}),
67100
}}>
68101
<ExplorerHeader isOpen={isOpen} toggleOpen={toggleOpen} />
102+
{data && <ExplorerFilter data={data} />}
69103
<TreeView
104+
expanded={expandedItems}
70105
className={"explorer__tree " + (
71106
isOpen ? "explorer__tree-open" : "explorer__tree-open"
72107
)}

packages/webdoc-default-template/src/app/components/Explorer/useExplorerStyles.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ import {makeStyles} from "@material-ui/core/styles";
22

33
const itemStyle = {
44
label: {
5-
alignItems: "center",
6-
display: "flex",
7-
color: "#333333",
8-
fontSize: 12,
9-
height: "24px",
10-
lineHeight: "14px",
5+
alignItems: "center !important",
6+
display: "flex !important",
7+
color: "#333333 !important",
8+
fontSize: "12px !important",
9+
height: "24px !important",
10+
lineHeight: "14px !important",
1111
},
1212
labelLinks: {
13-
color: "#333333",
13+
color: "#333333 !important",
1414
},
1515
iconContainer: {
1616
color: "#333333",
@@ -31,6 +31,6 @@ export const useExplorerCategoryStyles = makeStyles({
3131
...itemStyle,
3232
label: {
3333
...itemStyle.label,
34-
fontWeight: "bold",
34+
fontWeight: "bold !important",
3535
},
3636
});

packages/webdoc-default-template/src/app/store.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,47 @@ function globalReducer(state = {}, action) {
77
...state,
88
explorerOpen: action.value,
99
};
10+
case "setQuery":
11+
if (!state.query && action.value) {
12+
state.expandedItemsBeforeQuery = state.expandedItems;
13+
} else if (state.query && !action.value) {
14+
state.expandedItems = state.expandedItemsBeforeQuery;
15+
}
16+
17+
if (action.value) {
18+
return {
19+
...state,
20+
query: action.value,
21+
expandedItems: action.items,
22+
};
23+
} else {
24+
return {
25+
...state,
26+
query: action.value,
27+
};
28+
}
29+
case "toggleItem": {
30+
const expandedItems = new Set(state.expandedItems);
31+
32+
if (action.value !== undefined) {
33+
expandedItems[action.value ? "add" : "delete"](action.nodeId);
34+
} else {
35+
expandedItems[expandedItems.has(action.nodeId) ? "delete" : "add"](action.nodeId);
36+
}
37+
38+
return {
39+
...state,
40+
expandedItems,
41+
};
42+
}
1043
default:
1144
return state;
1245
}
1346
}
1447

1548
export default createStore(globalReducer, {
49+
expandedItems: new Set(),
1650
explorerOpen: true,
51+
expandedItemsBeforeQuery: new Set(),
52+
query: "",
1753
});

packages/webdoc-default-template/src/styles/explorer.scss

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
background-color: $colorSheetSecondary;
66
display: flex;
77
flex-direction: column;
8-
padding-bottom: 8px;
98
margin: 0;
9+
min-height: 100%;
1010
max-height: 100%;
1111
min-width: 291px;
1212

@@ -15,9 +15,27 @@
1515
overflow: auto;
1616
}
1717

18+
input {
19+
border: 1px solid rgba(0, 0, 0, 0.04);
20+
border-radius: 4px;
21+
font: 12px Arial;
22+
margin: 8px 24px 0 24px;
23+
min-height: 32px;
24+
padding: 0 8px;
25+
}
26+
27+
input:focus {
28+
border: 1px solid rgba(0, 0, 0, 0.4);
29+
outline: none;
30+
}
31+
1832
&__tree > *:first-child {
1933
margin-top: 8px;
2034
}
35+
36+
&__tree {
37+
margin-bottom: 8px;
38+
}
2139
}
2240

2341
/* This appears outside of .explorer when its closed. */

0 commit comments

Comments
 (0)