mirror of
https://github.com/AykutSarac/jsoncrack.com.git
synced 2025-01-27 15:22:56 +08:00
add focusNode custom hook
This commit is contained in:
parent
7e2c760951
commit
f573565348
@ -1,8 +1,7 @@
|
||||
import React from "react";
|
||||
import { AiOutlineSearch } from "react-icons/ai";
|
||||
import { IoCloseSharp } from "react-icons/io5";
|
||||
import { useConfig } from "src/hocs/config";
|
||||
import { ConfigActionType } from "src/reducer/reducer";
|
||||
import { useFocusNode } from "src/hooks/useFocusNode";
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledInputWrapper = styled.div`
|
||||
@ -48,32 +47,28 @@ const StyledSearchButton = styled.button`
|
||||
}
|
||||
`;
|
||||
|
||||
export const Input = () => {
|
||||
const { dispatch } = useConfig();
|
||||
const [value, setValue] = React.useState("");
|
||||
|
||||
React.useEffect(() => {
|
||||
const debouncer = setTimeout(() => {
|
||||
dispatch({ type: ConfigActionType.SET_SEARCH_NODE, payload: value });
|
||||
}, 1500);
|
||||
|
||||
return () => clearTimeout(debouncer);
|
||||
}, [value, dispatch]);
|
||||
|
||||
const handleClick = () => {
|
||||
setValue("");
|
||||
};
|
||||
export const Input: React.FC = () => {
|
||||
const [content, setContent] = useFocusNode();
|
||||
|
||||
return (
|
||||
<StyledInputWrapper>
|
||||
<StyledInput
|
||||
type="text"
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
value={content.value}
|
||||
onChange={(e) =>
|
||||
setContent((val) => ({ ...val, value: e.target.value }))
|
||||
}
|
||||
placeholder="Search Node"
|
||||
/>
|
||||
<StyledSearchButton aria-label="search" onClick={handleClick}>
|
||||
{value ? <IoCloseSharp size={18} /> : <AiOutlineSearch size={18} />}
|
||||
<StyledSearchButton
|
||||
aria-label="search"
|
||||
onClick={() => setContent({ value: "", debounced: "" })}
|
||||
>
|
||||
{content.value ? (
|
||||
<IoCloseSharp size={18} />
|
||||
) : (
|
||||
<AiOutlineSearch size={18} />
|
||||
)}
|
||||
</StyledSearchButton>
|
||||
</StyledInputWrapper>
|
||||
);
|
||||
|
@ -38,7 +38,6 @@ export const defaultConfig: StorageConfig = {
|
||||
expand: true,
|
||||
autoformat: true,
|
||||
hideEditor: false,
|
||||
searchNode: "",
|
||||
zoomPanPinch: null,
|
||||
lightmode: false
|
||||
};
|
||||
|
@ -13,6 +13,7 @@ import { useLoading } from "src/hooks/useLoading";
|
||||
import { useConfig } from "src/hocs/config";
|
||||
import { Tools } from "../Editor/Tools";
|
||||
import { ConfigActionType } from "src/reducer/reducer";
|
||||
import { useFocusNode } from "src/hooks/useFocusNode";
|
||||
|
||||
const StyledLiveEditor = styled.div`
|
||||
position: relative;
|
||||
@ -51,49 +52,16 @@ export const LiveEditor: React.FC = React.memo(function LiveEditor() {
|
||||
setData({ nodes, edges });
|
||||
}, [json, settings.expand]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!settings.zoomPanPinch) return;
|
||||
const zoomPanPinch = settings.zoomPanPinch.instance.wrapperComponent;
|
||||
|
||||
const node = document.querySelector(
|
||||
`span[data-key*='${settings.searchNode}' i]`
|
||||
);
|
||||
|
||||
document
|
||||
.querySelector("foreignObject.searched")
|
||||
?.classList.remove("searched");
|
||||
|
||||
if (zoomPanPinch && node && node.parentElement) {
|
||||
const newScale = 1;
|
||||
const x = Number(node.getAttribute("data-x"));
|
||||
const y = Number(node.getAttribute("data-y"));
|
||||
|
||||
const newPositionX =
|
||||
(zoomPanPinch.offsetLeft - x) * newScale +
|
||||
node.getBoundingClientRect().width;
|
||||
const newPositionY =
|
||||
(zoomPanPinch.offsetTop - y) * newScale +
|
||||
node.getBoundingClientRect().height;
|
||||
|
||||
node.parentElement.parentElement
|
||||
?.closest("foreignObject")
|
||||
?.classList.toggle("searched");
|
||||
|
||||
settings.zoomPanPinch?.setTransform(newPositionX, newPositionY, newScale);
|
||||
}
|
||||
}, [settings.searchNode, settings.zoomPanPinch]);
|
||||
|
||||
const onCanvasClick = () => {
|
||||
const input = document.querySelector("input:focus") as HTMLInputElement;
|
||||
if (input) input.blur();
|
||||
};
|
||||
|
||||
const onInit = (ref: ReactZoomPanPinchRef) => {
|
||||
const onInit = (ref: ReactZoomPanPinchRef) =>
|
||||
dispatch({
|
||||
type: ConfigActionType.SET_ZOOM_PAN_PICNH_REF,
|
||||
payload: ref,
|
||||
});
|
||||
};
|
||||
|
||||
if (pageLoaded)
|
||||
return (
|
||||
|
57
src/hooks/useFocusNode.tsx
Normal file
57
src/hooks/useFocusNode.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
import React from "react";
|
||||
import { useConfig } from "src/hocs/config";
|
||||
|
||||
type Content = { value: string; debounced: string };
|
||||
|
||||
export const useFocusNode = () => {
|
||||
const [content, setContent] = React.useState({
|
||||
value: "",
|
||||
debounced: "",
|
||||
});
|
||||
|
||||
const {
|
||||
states: { settings },
|
||||
} = useConfig();
|
||||
|
||||
React.useEffect(() => {
|
||||
const debouncer = setTimeout(() => {
|
||||
setContent((val) => ({ ...val, debounced: content.value }));
|
||||
}, 1500);
|
||||
|
||||
return () => clearTimeout(debouncer);
|
||||
}, [content.value]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!settings.zoomPanPinch) return;
|
||||
const zoomPanPinch = settings.zoomPanPinch.instance.wrapperComponent;
|
||||
|
||||
const node = document.querySelector(
|
||||
`span[data-key*='${content.debounced}' i]`
|
||||
);
|
||||
|
||||
document
|
||||
.querySelector("foreignObject.searched")
|
||||
?.classList.remove("searched");
|
||||
|
||||
if (zoomPanPinch && node && node.parentElement) {
|
||||
const newScale = 1;
|
||||
const x = Number(node.getAttribute("data-x"));
|
||||
const y = Number(node.getAttribute("data-y"));
|
||||
|
||||
const newPositionX =
|
||||
(zoomPanPinch.offsetLeft - x) * newScale +
|
||||
node.getBoundingClientRect().width;
|
||||
const newPositionY =
|
||||
(zoomPanPinch.offsetTop - y) * newScale +
|
||||
node.getBoundingClientRect().height;
|
||||
|
||||
node.parentElement.parentElement
|
||||
?.closest("foreignObject")
|
||||
?.classList.toggle("searched");
|
||||
|
||||
settings.zoomPanPinch?.setTransform(newPositionX, newPositionY, newScale);
|
||||
}
|
||||
}, [content, settings.zoomPanPinch]);
|
||||
|
||||
return [content, setContent] as const;
|
||||
};
|
@ -13,7 +13,6 @@ export enum ConfigActionType {
|
||||
ZOOM_OUT,
|
||||
CENTER_VIEW,
|
||||
SET_JSON,
|
||||
SET_SEARCH_NODE,
|
||||
SET_ZOOM_PAN_PICNH_REF,
|
||||
}
|
||||
|
||||
@ -51,15 +50,6 @@ export const useConfigReducer: React.Reducer<AppConfig, ReducerAction> = (
|
||||
},
|
||||
};
|
||||
|
||||
case ConfigActionType.SET_SEARCH_NODE:
|
||||
return {
|
||||
...state,
|
||||
settings: {
|
||||
...state.settings,
|
||||
searchNode: action.payload,
|
||||
},
|
||||
};
|
||||
|
||||
case ConfigActionType.CENTER_VIEW:
|
||||
state.settings.zoomPanPinch?.resetTransform();
|
||||
return state;
|
||||
|
@ -7,7 +7,6 @@ export interface StorageConfig {
|
||||
expand: boolean;
|
||||
autoformat: boolean;
|
||||
hideEditor: boolean;
|
||||
searchNode: string;
|
||||
zoomPanPinch: ReactZoomPanPinchRef | null;
|
||||
lightmode: boolean;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user