mirror of
https://github.com/AykutSarac/jsoncrack.com.git
synced 2025-01-27 15:22:56 +08:00
modify import from url implementation
This commit is contained in:
parent
cc77dde5dd
commit
7be8e3dbee
@ -4,8 +4,6 @@ import Link from "next/link";
|
|||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { CanvasDirection } from "reaflow";
|
import { CanvasDirection } from "reaflow";
|
||||||
import { TiFlowMerge } from "react-icons/ti";
|
import { TiFlowMerge } from "react-icons/ti";
|
||||||
import { BsList } from "react-icons/bs";
|
|
||||||
import { MdUploadFile } from "react-icons/md";
|
|
||||||
import { RiPatreonFill } from "react-icons/ri";
|
import { RiPatreonFill } from "react-icons/ri";
|
||||||
import { CgArrowsMergeAltH, CgArrowsShrinkH } from "react-icons/cg";
|
import { CgArrowsMergeAltH, CgArrowsShrinkH } from "react-icons/cg";
|
||||||
import {
|
import {
|
||||||
@ -14,23 +12,22 @@ import {
|
|||||||
AiOutlineTwitter,
|
AiOutlineTwitter,
|
||||||
AiOutlineSave,
|
AiOutlineSave,
|
||||||
AiOutlineFileAdd,
|
AiOutlineFileAdd,
|
||||||
AiOutlineLink,
|
|
||||||
} from "react-icons/ai";
|
} from "react-icons/ai";
|
||||||
|
|
||||||
import { Tooltip } from "src/components/Tooltip";
|
import { Tooltip } from "src/components/Tooltip";
|
||||||
import { Modal } from "src/components/Modal/index";
|
|
||||||
import { ConfigActionType } from "src/reducer/reducer";
|
import { ConfigActionType } from "src/reducer/reducer";
|
||||||
import { useConfig } from "src/hocs/config";
|
import { useConfig } from "src/hocs/config";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import { ImportModal } from "src/containers/ImportModal";
|
||||||
|
|
||||||
const StyledSidebar = styled.div`
|
const StyledSidebar = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 36px;
|
width: fit-content;
|
||||||
background: ${({ theme }) => theme.BACKGROUND_TERTIARY};
|
background: ${({ theme }) => theme.BACKGROUND_TERTIARY};
|
||||||
padding: 8px;
|
padding: 4px;
|
||||||
border-right: 1px solid ${({ theme }) => theme.BACKGROUND_MODIFIER_ACCENT};
|
border-right: 1px solid ${({ theme }) => theme.BACKGROUND_MODIFIER_ACCENT};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -39,18 +36,22 @@ const StyledElement = styled.div`
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
font-weight: 700;
|
font-weight: 600;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: ${({ theme }) => theme.INTERACTIVE_NORMAL};
|
color: ${({ theme }) => theme.INTERACTIVE_NORMAL};
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover :is(a, svg) {
|
svg {
|
||||||
color: ${({ theme }) => theme.INTERACTIVE_HOVER};
|
padding: 8px;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg {
|
a {
|
||||||
padding: 8px 0;
|
display: flex;
|
||||||
vertical-align: middle;
|
}
|
||||||
|
|
||||||
|
&:hover :is(a, svg) {
|
||||||
|
color: ${({ theme }) => theme.INTERACTIVE_HOVER};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -92,14 +93,6 @@ const StyledLogo = styled.div`
|
|||||||
color: ${({ theme }) => theme.FULL_WHITE};
|
color: ${({ theme }) => theme.FULL_WHITE};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledImportFile = styled.label`
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
input[type="file"] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
function rotateLayout(layout: CanvasDirection) {
|
function rotateLayout(layout: CanvasDirection) {
|
||||||
if (layout === "LEFT") return 90;
|
if (layout === "LEFT") return 90;
|
||||||
if (layout === "UP") return 180;
|
if (layout === "UP") return 180;
|
||||||
@ -111,16 +104,12 @@ export const Sidebar: React.FC = () => {
|
|||||||
const { json, settings, dispatch } = useConfig();
|
const { json, settings, dispatch } = useConfig();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [jsonFile, setJsonFile] = React.useState<File | null>(null);
|
const [jsonFile, setJsonFile] = React.useState<File | null>(null);
|
||||||
const [visible, setVisible] = React.useState(false);
|
const [modalVisible, setModalVisible] = React.useState(false);
|
||||||
|
|
||||||
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
if (e.target.files) setJsonFile(e.target.files?.item(0));
|
if (e.target.files) setJsonFile(e.target.files?.item(0));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleImportJSONFromURL = () => {
|
|
||||||
setVisible(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClear = () => {
|
const handleClear = () => {
|
||||||
dispatch({ type: ConfigActionType.SET_JSON, payload: "{}" });
|
dispatch({ type: ConfigActionType.SET_JSON, payload: "{}" });
|
||||||
localStorage.removeItem("json");
|
localStorage.removeItem("json");
|
||||||
@ -154,7 +143,7 @@ export const Sidebar: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<StyledSidebar>
|
<StyledSidebar>
|
||||||
<StyledTopWrapper>
|
<StyledTopWrapper>
|
||||||
<Modal visible={visible} setVisible={setVisible} />
|
<ImportModal visible={modalVisible} setVisible={setModalVisible} />
|
||||||
<Link passHref href="/">
|
<Link passHref href="/">
|
||||||
<StyledElement onClick={() => router.push("/")}>
|
<StyledElement onClick={() => router.push("/")}>
|
||||||
<StyledLogo>
|
<StyledLogo>
|
||||||
@ -164,21 +153,8 @@ export const Sidebar: React.FC = () => {
|
|||||||
</StyledElement>
|
</StyledElement>
|
||||||
</Link>
|
</Link>
|
||||||
<Tooltip title="Import File">
|
<Tooltip title="Import File">
|
||||||
<StyledElement>
|
<StyledElement onClick={() => setModalVisible(true)}>
|
||||||
<StyledImportFile>
|
<AiOutlineFileAdd />
|
||||||
<input
|
|
||||||
key={jsonFile?.name}
|
|
||||||
onChange={handleFileChange}
|
|
||||||
type="file"
|
|
||||||
accept="application/JSON"
|
|
||||||
/>
|
|
||||||
<AiOutlineFileAdd />
|
|
||||||
</StyledImportFile>
|
|
||||||
</StyledElement>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip title="Fetch JSON from URL">
|
|
||||||
<StyledElement onClick={handleImportJSONFromURL}>
|
|
||||||
<AiOutlineLink />
|
|
||||||
</StyledElement>
|
</StyledElement>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip title="Rotate Layout">
|
<Tooltip title="Rotate Layout">
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
import { FiDownload } from "react-icons/fi";
|
import { FiDownload } from "react-icons/fi";
|
||||||
import { HiOutlineSun, HiOutlineMoon } from "react-icons/hi";
|
import { HiOutlineSun, HiOutlineMoon } from "react-icons/hi";
|
||||||
import { MdCenterFocusWeak } from "react-icons/md";
|
import { MdCenterFocusWeak } from "react-icons/md";
|
||||||
import { Input } from "src/components/Input";
|
import { SearchInput } from "src/containers/SearchInput";
|
||||||
import { useConfig } from "src/hocs/config";
|
import { useConfig } from "src/hocs/config";
|
||||||
import { ConfigActionType } from "src/reducer/reducer";
|
import { ConfigActionType } from "src/reducer/reducer";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
@ -68,7 +68,7 @@ export const Tools: React.FC = () => {
|
|||||||
<StyledToolElement aria-label="switch theme" onClick={toggleTheme}>
|
<StyledToolElement aria-label="switch theme" onClick={toggleTheme}>
|
||||||
{settings.lightmode ? <HiOutlineMoon /> : <HiOutlineSun />}
|
{settings.lightmode ? <HiOutlineMoon /> : <HiOutlineSun />}
|
||||||
</StyledToolElement>
|
</StyledToolElement>
|
||||||
<Input />
|
<SearchInput />
|
||||||
<StyledToolElement aria-label="save" onClick={exportAsImage}>
|
<StyledToolElement aria-label="save" onClick={exportAsImage}>
|
||||||
<FiDownload />
|
<FiDownload />
|
||||||
</StyledToolElement>
|
</StyledToolElement>
|
||||||
|
134
src/containers/ImportModal/index.tsx
Normal file
134
src/containers/ImportModal/index.tsx
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
import React from "react";
|
||||||
|
import styled from "styled-components";
|
||||||
|
import toast from "react-hot-toast";
|
||||||
|
|
||||||
|
import { useConfig } from "src/hocs/config";
|
||||||
|
import { ConfigActionType } from "src/reducer/reducer";
|
||||||
|
import { Modal } from "src/components/Modal";
|
||||||
|
import { Button } from "src/components/Button";
|
||||||
|
import { AiOutlineUpload } from "react-icons/ai";
|
||||||
|
|
||||||
|
const StyledInput = styled.input`
|
||||||
|
background: ${({ theme }) => theme.BACKGROUND_TERTIARY};
|
||||||
|
color: ${({ theme }) => theme.INTERACTIVE_NORMAL};
|
||||||
|
outline: none;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 6px 8px;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledModalContent = styled(Modal.Content)`
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledUploadWrapper = styled.label`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background: ${({ theme }) => theme.BACKGROUND_SECONDARY};
|
||||||
|
border: 2px dashed ${({ theme }) => theme.BACKGROUND_TERTIARY};
|
||||||
|
border-radius: 5px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
input[type="file"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledFileName = styled.span`
|
||||||
|
color: ${({ theme }) => theme.INTERACTIVE_NORMAL};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledUploadMessage = styled.h3`
|
||||||
|
color: ${({ theme }) => theme.INTERACTIVE_ACTIVE};
|
||||||
|
margin-bottom: 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const ImportModal = ({ visible, setVisible }) => {
|
||||||
|
const { dispatch } = useConfig();
|
||||||
|
const [url, setURL] = React.useState("");
|
||||||
|
const [jsonFile, setJsonFile] = React.useState<File | null>(null);
|
||||||
|
|
||||||
|
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
if (e.target.files) setJsonFile(e.target.files?.item(0));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleImportFile = () => {
|
||||||
|
if (url) {
|
||||||
|
setJsonFile(null);
|
||||||
|
|
||||||
|
return fetch(url)
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((json) => {
|
||||||
|
dispatch({
|
||||||
|
type: ConfigActionType.SET_JSON,
|
||||||
|
payload: JSON.stringify(json),
|
||||||
|
});
|
||||||
|
|
||||||
|
setVisible(false);
|
||||||
|
})
|
||||||
|
.catch(() => toast.error("Failed to fetch JSON!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jsonFile) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
reader.readAsText(jsonFile, "UTF-8");
|
||||||
|
reader.onload = function (data) {
|
||||||
|
dispatch({
|
||||||
|
type: ConfigActionType.SET_JSON,
|
||||||
|
payload: data.target?.result as string,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
setJsonFile(null);
|
||||||
|
setURL("");
|
||||||
|
};
|
||||||
|
}, [visible]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal visible={visible} setVisible={setVisible}>
|
||||||
|
<Modal.Header>Select An Import Method</Modal.Header>
|
||||||
|
<StyledModalContent>
|
||||||
|
<StyledInput
|
||||||
|
value={url}
|
||||||
|
onChange={(e) => setURL(e.target.value)}
|
||||||
|
type="url"
|
||||||
|
placeholder="URL of JSON to fetch"
|
||||||
|
/>
|
||||||
|
<StyledUploadWrapper>
|
||||||
|
<input
|
||||||
|
key={jsonFile?.name}
|
||||||
|
onChange={handleFileChange}
|
||||||
|
type="file"
|
||||||
|
accept="application/JSON"
|
||||||
|
/>
|
||||||
|
<AiOutlineUpload size={48} />
|
||||||
|
<StyledUploadMessage>Click Here to Upload JSON</StyledUploadMessage>
|
||||||
|
<StyledFileName>{jsonFile?.name ?? "None"}</StyledFileName>
|
||||||
|
</StyledUploadWrapper>
|
||||||
|
</StyledModalContent>
|
||||||
|
<Modal.Controls setVisible={setVisible}>
|
||||||
|
<Button
|
||||||
|
status="SECONDARY"
|
||||||
|
onClick={handleImportFile}
|
||||||
|
disabled={!(jsonFile || url)}
|
||||||
|
>
|
||||||
|
Import
|
||||||
|
</Button>
|
||||||
|
</Modal.Controls>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
@ -51,7 +51,7 @@ const StyledSearchButton = styled.button`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Input: React.FC = () => {
|
export const SearchInput: React.FC = () => {
|
||||||
const [content, setContent, skip] = useFocusNode();
|
const [content, setContent, skip] = useFocusNode();
|
||||||
|
|
||||||
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
@ -3,12 +3,12 @@ import { Button } from "src/components/Button";
|
|||||||
import { Container } from "src/components/Container";
|
import { Container } from "src/components/Container";
|
||||||
import { Navbar } from "src/components/Navbar";
|
import { Navbar } from "src/components/Navbar";
|
||||||
import { Image } from "src/components/Image";
|
import { Image } from "src/components/Image";
|
||||||
import styled from "styled-components";
|
|
||||||
import { AiFillGithub } from "react-icons/ai";
|
import { AiFillGithub } from "react-icons/ai";
|
||||||
import { Footer } from "src/components/Footer";
|
import { Footer } from "src/components/Footer";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import { Producthunt } from "src/components/Producthunt";
|
import { Producthunt } from "src/components/Producthunt";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
const StyledHome = styled.div`
|
const StyledHome = styled.div`
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user