Merge pull request #26 from burakozarslan/feature-highlight-multiple-search-results

Feature highlight multiple search results
This commit is contained in:
Aykut Saraç 2022-04-29 13:28:30 +03:00 committed by GitHub
commit 0cc84f7138
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 67 additions and 6645 deletions

View File

@ -13,6 +13,10 @@ const StyledInputWrapper = styled.div`
padding: 4px 6px; padding: 4px 6px;
`; `;
const StyledForm = styled.form`
display: flex;
`;
const StyledInput = styled.input` const StyledInput = styled.input`
background: none; background: none;
color: ${({ theme }) => theme.TEXT_NORMAL}; color: ${({ theme }) => theme.TEXT_NORMAL};
@ -48,10 +52,16 @@ const StyledSearchButton = styled.button`
`; `;
export const Input: React.FC = () => { export const Input: React.FC = () => {
const [content, setContent] = useFocusNode(); const [content, setContent, skip] = useFocusNode();
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
skip();
};
return ( return (
<StyledInputWrapper> <StyledInputWrapper>
<StyledForm onSubmit={onSubmit}>
<StyledInput <StyledInput
type="text" type="text"
value={content.value} value={content.value}
@ -60,6 +70,7 @@ export const Input: React.FC = () => {
} }
placeholder="Search Node" placeholder="Search Node"
/> />
</StyledForm>
<StyledSearchButton <StyledSearchButton
aria-label="search" aria-label="search"
onClick={() => setContent({ value: "", debounced: "" })} onClick={() => setContent({ value: "", debounced: "" })}

View File

@ -1,9 +1,14 @@
import React from "react"; import React from "react";
import { useConfig } from "src/hocs/config"; import { useConfig } from "src/hocs/config";
type Content = { value: string; debounced: string }; import {
searchQuery,
cleanupHighlight,
highlightMatchedNodes,
} from "src/utils/search";
export const useFocusNode = () => { export const useFocusNode = () => {
const [selectedNode, setSelectedNode] = React.useState(0);
const [content, setContent] = React.useState({ const [content, setContent] = React.useState({
value: "", value: "",
debounced: "", debounced: "",
@ -13,6 +18,8 @@ export const useFocusNode = () => {
states: { settings }, states: { settings },
} = useConfig(); } = useConfig();
const skip = () => setSelectedNode((current) => current + 1);
React.useEffect(() => { React.useEffect(() => {
const debouncer = setTimeout(() => { const debouncer = setTimeout(() => {
setContent((val) => ({ ...val, debounced: content.value })); setContent((val) => ({ ...val, debounced: content.value }));
@ -25,33 +32,38 @@ export const useFocusNode = () => {
if (!settings.zoomPanPinch) return; if (!settings.zoomPanPinch) return;
const zoomPanPinch = settings.zoomPanPinch.instance.wrapperComponent; const zoomPanPinch = settings.zoomPanPinch.instance.wrapperComponent;
const node = document.querySelector( const matchedNodes: NodeListOf<Element> = searchQuery(
`span[data-key*='${content.debounced}' i]` `span[data-key*='${content.debounced}' i]`
); );
const matchedNode: Element | null = matchedNodes[selectedNode] || null;
document cleanupHighlight();
.querySelector("foreignObject.searched")
?.classList.remove("searched");
if (zoomPanPinch && node && node.parentElement) { if (zoomPanPinch && matchedNode && matchedNode.parentElement) {
const newScale = 1; const newScale = 1;
const x = Number(node.getAttribute("data-x")); const x = Number(matchedNode.getAttribute("data-x"));
const y = Number(node.getAttribute("data-y")); const y = Number(matchedNode.getAttribute("data-y"));
const newPositionX = const newPositionX =
(zoomPanPinch.offsetLeft - x) * newScale + (zoomPanPinch.offsetLeft - x) * newScale +
node.getBoundingClientRect().width; zoomPanPinch.clientWidth / 2 -
matchedNode.getBoundingClientRect().width / 2;
const newPositionY = const newPositionY =
(zoomPanPinch.offsetTop - y) * newScale + (zoomPanPinch.offsetLeft - y) * newScale +
node.getBoundingClientRect().height; zoomPanPinch.clientHeight / 2 -
matchedNode.getBoundingClientRect().height / 2;
node.parentElement.parentElement highlightMatchedNodes(matchedNodes);
?.closest("foreignObject")
?.classList.toggle("searched");
settings.zoomPanPinch?.setTransform(newPositionX, newPositionY, newScale); settings.zoomPanPinch?.setTransform(newPositionX, newPositionY, newScale);
} else {
setSelectedNode(0);
} }
}, [content.debounced, settings.zoomPanPinch]);
return [content, setContent] as const; return () => {
if (!content.value) setSelectedNode(0);
};
}, [content.debounced, settings.zoomPanPinch, selectedNode, setSelectedNode]);
return [content, setContent, skip] as const;
}; };

19
src/utils/search.ts Normal file
View File

@ -0,0 +1,19 @@
export const searchQuery = (param: string) => {
return document.querySelectorAll(`${param}`);
};
export const cleanupHighlight = () => {
const nodes = document.querySelectorAll("foreignObject.searched");
nodes?.forEach((node) => {
node.classList.remove("searched");
});
};
export const highlightMatchedNodes = (nodes: NodeListOf<Element>) => {
nodes?.forEach((node) => {
node.parentElement?.parentElement
?.closest("foreignObject")
?.classList.add("searched");
});
};

6620
yarn.lock

File diff suppressed because it is too large Load Diff