mirror of
https://github.com/AykutSarac/jsoncrack.com.git
synced 2025-02-04 01:32:54 +08:00
Merge pull request #26 from burakozarslan/feature-highlight-multiple-search-results
Feature highlight multiple search results
This commit is contained in:
commit
0cc84f7138
@ -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: "" })}
|
||||||
|
@ -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
19
src/utils/search.ts
Normal 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");
|
||||||
|
});
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user