react-d3-tree
react-d3-tree copied to clipboard
Search capabilities?
Is there a feature to search the tree and reposition the node searched into the center of container? Doesn't seem like it but I am wondering how I could start making one.
Search would be based off name or other key fields from nodeData
I would love this 👍
I have implemented search by searching all node labels for its .innerHTML and then center the container to that position retreived by .getBoundingClientRect()
Code:
class DeviceTopology extends React.Component<DeviceTopologyProps, DeviceTopologyState> {
treeContainer: HTMLDivElement | null;
constructor(props: DeviceTopologyProps) {
super(props);
this.state = {
translate: { x: 0, y: 0 },
data: {},
nodeCount: 0,
hoveredNode: {},
searchFor: '',
currentSearch: 0,
};
}
handleSearchChange = (event: React.FormEvent<HTMLInputElement>) => {
this.setState({
...this.state,
searchFor: event.currentTarget.value
});
};
handelSearchSubmit = () => {
const searchResults = [];
const allBaseNodes = document.getElementsByClassName("nodeBase");
for (let i = 0; i < allBaseNodes.length; i = i + 1) {
const currentNode = allBaseNodes[i].children[1].children[0].children[0];
if (currentNode.textContent === this.state.searchFor) {
searchResults.push(currentNode.getBoundingClientRect());
}
}
const allLeafNodes = document.getElementsByClassName("leafNodeBase");
for (let i = 0; i < allLeafNodes.length; i = i + 1) {
const currentNode = allLeafNodes[i].children[1].children[0].children[0];
if (currentNode.textContent === this.state.searchFor) {
searchResults.push(currentNode.getBoundingClientRect());
}
}
if (searchResults.length && this.treeContainer) {
const { translate } = this.state
let { currentSearch, } = this.state
if (currentSearch >= searchResults.length) {
currentSearch = 0;
}
const dimensions = this.treeContainer.getBoundingClientRect();
const x = translate.x - searchResults[currentSearch].left + dimensions.width / 2;
const y = translate.y - searchResults[currentSearch].top + dimensions.height / 2 + 100;
this.setState({
translate: {
x,
y
},
currentSearch: currentSearch + 1,
});
}
};
handleUpdate = (data: any) => {
const { translate } = data;
if (translate !== this.state.translate) this.setState({ translate });
}
render() {
return (
<div id="treeWrapper" style={{ width: '100%', height: '100%' }} ref={tc => (this.treeContainer = tc)}>
<TopologieViewSearch
onChange={this.handleSearchChange}
onSearch={this.handelSearchSubmit}
/>
<Tree
data={this.state.data}
translate={this.state.translate}
orientation='vertical'
allowForeignObjects={true}
onUpdate={this.handleUpdate}
nodeLabelComponent={{
render: <DeviceTopologyNodeLabel
nodeData={this.state.data}
hoveredNode={this.state.hoveredNode}
removeUnit={this.props.removeUnit}
/>,
foreignObjectWrapper: {
y: -23,
width: 70,
height: 50,
}
}}
onMouseOver={(nodeData) => { this.setState({ hoveredNode: nodeData }) }}
styles={{
links: {
stroke: '#00376c',
strokeWidth: 0.3,
}
}}
/>
</div>
);
}
}
TopologieViewSearch.tsx
import SearchInput from './SearchInput';
import * as React from 'react';
import { faSearch } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
export interface TopologieViewSearchProps {
onChange: (event: React.FormEvent<HTMLInputElement>) => void;
onSearch: () => void;
}
export interface TopologieViewSearchState { }
export default class TopologieViewSearch extends React.Component<
TopologieViewSearchProps,
TopologieViewSearchState
> {
render() {
return (
<div className="tree__search">
<SearchInput
onChange={this.props.onChange}
name='tree-search'
/>
<button
onClick={this.props.onSearch}>
<FontAwesomeIcon
icon={faSearch}
size="sm"
className="tree__controls__icon" />
</button>
</div >
);
}
}
SearchInput.tsx
import * as React from 'react';
import { Translate } from 'react-localize-redux';
export interface SearchInputProps {
onChange: (event: React.FormEvent<HTMLInputElement>) => void;
name: string;
value?: string;
}
export interface SearchInputState { }
export default class SearchInput extends React.Component<
SearchInputProps,
SearchInputState
> {
render() {
const { onChange, name, value } = this.props;
return (
<div className="search__item">
<Translate>
{({ translate }) =>
<input
type="text"
placeholder={translate("search") as string}
className="filterbar__item__select"
onChange={onChange}
name={name}
value={value}
/>}
</Translate>
</div>
);
}
}