mirror of
https://github.com/AykutSarac/jsoncrack.com.git
synced 2025-01-27 15:22:56 +08:00
Merge branch 'main' into add-shortcut-in-popup
This commit is contained in:
commit
11cb853a2b
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "json-visio",
|
"name": "json-visio",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "v1.9.0",
|
"version": "v1.9.2",
|
||||||
"homepage": "https://jsonvisio.com",
|
"homepage": "https://jsonvisio.com",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
@ -15,6 +15,7 @@
|
|||||||
"@monaco-editor/react": "^4.4.5",
|
"@monaco-editor/react": "^4.4.5",
|
||||||
"@sentry/nextjs": "^7.1.1",
|
"@sentry/nextjs": "^7.1.1",
|
||||||
"allotment": "^1.14.2",
|
"allotment": "^1.14.2",
|
||||||
|
"compress-json": "^2.0.1",
|
||||||
"next": "^12.1.5",
|
"next": "^12.1.5",
|
||||||
"next-transpile-modules": "^9.0.0",
|
"next-transpile-modules": "^9.0.0",
|
||||||
"parse-json": "^6.0.2",
|
"parse-json": "^6.0.2",
|
||||||
@ -27,7 +28,8 @@
|
|||||||
"react-zoom-pan-pinch": "^2.1.3",
|
"react-zoom-pan-pinch": "^2.1.3",
|
||||||
"reaflow": "^5.0.4",
|
"reaflow": "^5.0.4",
|
||||||
"save-html-as-image": "^1.7.1",
|
"save-html-as-image": "^1.7.1",
|
||||||
"styled-components": "^5.3.5"
|
"styled-components": "^5.3.5",
|
||||||
|
"usehooks-ts": "^2.5.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@testing-library/jest-dom": "^5.16.4",
|
"@testing-library/jest-dom": "^5.16.4",
|
||||||
|
@ -57,8 +57,10 @@ const Modal: React.FC<React.PropsWithChildren<ModalProps>> & ModalTypes = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!visible) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Styled.ModalWrapper visible={visible} onClick={onClick}>
|
<Styled.ModalWrapper onClick={onClick}>
|
||||||
<Styled.ModalInnerWrapper>{children}</Styled.ModalInnerWrapper>
|
<Styled.ModalInnerWrapper>{children}</Styled.ModalInnerWrapper>
|
||||||
</Styled.ModalWrapper>
|
</Styled.ModalWrapper>
|
||||||
);
|
);
|
||||||
|
@ -5,17 +5,17 @@ const appearAnimation = keyframes`
|
|||||||
to { transform: scale(1); opacity: 1; };
|
to { transform: scale(1); opacity: 1; };
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ModalWrapper = styled.div<{ visible: boolean }>`
|
export const ModalWrapper = styled.div`
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: ${({ visible }) => (visible ? "flex" : "none")};
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: rgba(0, 0, 0, 0.85);
|
background: rgba(0, 0, 0, 0.85);
|
||||||
z-index: 6;
|
z-index: 36;
|
||||||
|
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@ -24,6 +24,7 @@ export const ModalWrapper = styled.div<{ visible: boolean }>`
|
|||||||
|
|
||||||
export const ModalInnerWrapper = styled.div`
|
export const ModalInnerWrapper = styled.div`
|
||||||
min-width: 440px;
|
min-width: 440px;
|
||||||
|
max-width: 70%;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
animation: ${appearAnimation} 220ms ease-in-out;
|
animation: ${appearAnimation} 220ms ease-in-out;
|
||||||
`;
|
`;
|
||||||
|
@ -16,6 +16,7 @@ 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";
|
||||||
@ -24,6 +25,7 @@ import { useConfig } from "src/hocs/config";
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { ImportModal } from "src/containers/ImportModal";
|
import { ImportModal } from "src/containers/ImportModal";
|
||||||
import { ClearModal } from "src/containers/ClearModal";
|
import { ClearModal } from "src/containers/ClearModal";
|
||||||
|
import { ShareModal } from "src/containers/ShareModal";
|
||||||
import { IoAlertCircleSharp } from "react-icons/io5";
|
import { IoAlertCircleSharp } from "react-icons/io5";
|
||||||
|
|
||||||
const StyledSidebar = styled.div`
|
const StyledSidebar = styled.div`
|
||||||
@ -138,6 +140,7 @@ export const Sidebar: React.FC = () => {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [uploadVisible, setUploadVisible] = React.useState(false);
|
const [uploadVisible, setUploadVisible] = React.useState(false);
|
||||||
const [clearVisible, setClearVisible] = React.useState(false);
|
const [clearVisible, setClearVisible] = React.useState(false);
|
||||||
|
const [shareVisible, setShareVisible] = React.useState(false);
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
const a = document.createElement("a");
|
const a = document.createElement("a");
|
||||||
@ -197,16 +200,6 @@ export const Sidebar: React.FC = () => {
|
|||||||
{settings.expand ? <CgArrowsMergeAltH /> : <CgArrowsShrinkH />}
|
{settings.expand ? <CgArrowsMergeAltH /> : <CgArrowsShrinkH />}
|
||||||
</StyledElement>
|
</StyledElement>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip title="Clear JSON">
|
|
||||||
<StyledElement onClick={() => setClearVisible(true)}>
|
|
||||||
<AiOutlineDelete />
|
|
||||||
</StyledElement>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip title="Save JSON">
|
|
||||||
<StyledElement onClick={handleSave}>
|
|
||||||
<AiOutlineSave />
|
|
||||||
</StyledElement>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={`${
|
title={`${
|
||||||
settings.performance ? "Disable" : "Enable"
|
settings.performance ? "Disable" : "Enable"
|
||||||
@ -218,6 +211,21 @@ export const Sidebar: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
</StyledElement>
|
</StyledElement>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
<Tooltip title="Save JSON">
|
||||||
|
<StyledElement onClick={handleSave}>
|
||||||
|
<AiOutlineSave />
|
||||||
|
</StyledElement>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="Clear JSON">
|
||||||
|
<StyledElement onClick={() => setClearVisible(true)}>
|
||||||
|
<AiOutlineDelete />
|
||||||
|
</StyledElement>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="Share">
|
||||||
|
<StyledElement onClick={() => setShareVisible(true)}>
|
||||||
|
<AiOutlineLink />
|
||||||
|
</StyledElement>
|
||||||
|
</Tooltip>
|
||||||
</StyledTopWrapper>
|
</StyledTopWrapper>
|
||||||
<StyledBottomWrapper>
|
<StyledBottomWrapper>
|
||||||
<StyledElement>
|
<StyledElement>
|
||||||
@ -244,6 +252,7 @@ export const Sidebar: React.FC = () => {
|
|||||||
</StyledBottomWrapper>
|
</StyledBottomWrapper>
|
||||||
<ImportModal visible={uploadVisible} setVisible={setUploadVisible} />
|
<ImportModal visible={uploadVisible} setVisible={setUploadVisible} />
|
||||||
<ClearModal visible={clearVisible} setVisible={setClearVisible} />
|
<ClearModal visible={clearVisible} setVisible={setClearVisible} />
|
||||||
|
<ShareModal visible={shareVisible} setVisible={setShareVisible} />
|
||||||
</StyledSidebar>
|
</StyledSidebar>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -15,7 +15,7 @@ const StyledInput = styled.input`
|
|||||||
border: none;
|
border: none;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
line-height: 32px;
|
line-height: 32px;
|
||||||
padding: 16px;
|
padding: 12px 8px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
|
@ -7,7 +7,6 @@ import { ConfigActionType } from "src/reducer/reducer";
|
|||||||
import { useConfig } from "src/hocs/config";
|
import { useConfig } from "src/hocs/config";
|
||||||
import { Loading } from "src/components/Loading";
|
import { Loading } from "src/components/Loading";
|
||||||
import { loader } from "@monaco-editor/react";
|
import { loader } from "@monaco-editor/react";
|
||||||
import { CarbonAds } from "src/components/CarbonAds";
|
|
||||||
|
|
||||||
loader.config({ paths: { vs: "/monaco-editor/min/vs" } });
|
loader.config({ paths: { vs: "/monaco-editor/min/vs" } });
|
||||||
|
|
||||||
|
80
src/containers/ShareModal/index.tsx
Normal file
80
src/containers/ShareModal/index.tsx
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import React from "react";
|
||||||
|
import toast from "react-hot-toast";
|
||||||
|
import styled from "styled-components";
|
||||||
|
import { useCopyToClipboard } from "usehooks-ts";
|
||||||
|
import { Modal, ModalProps } from "src/components/Modal";
|
||||||
|
import { Button } from "src/components/Button";
|
||||||
|
import { BiErrorAlt } from "react-icons/bi";
|
||||||
|
import { compress } from "compress-json";
|
||||||
|
import { useConfig } from "src/hocs/config";
|
||||||
|
|
||||||
|
const StyledInput = styled.input`
|
||||||
|
background: ${({ theme }) => theme.BACKGROUND_TERTIARY};
|
||||||
|
color: ${({ theme }) => theme.INTERACTIVE_NORMAL};
|
||||||
|
outline: none;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
line-height: 32px;
|
||||||
|
padding: 12px 8px;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
height: 30px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledWarning = styled.p``;
|
||||||
|
|
||||||
|
const StyledErrorWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: ${({ theme }) => theme.TEXT_DANGER};
|
||||||
|
font-weight: 600;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const ShareModal: React.FC<ModalProps> = ({ visible, setVisible }) => {
|
||||||
|
const { json } = useConfig();
|
||||||
|
const [url, setURL] = React.useState("");
|
||||||
|
const [_, copy] = useCopyToClipboard();
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const jsonEncode = compress(JSON.parse(json));
|
||||||
|
const jsonString = JSON.stringify(jsonEncode);
|
||||||
|
|
||||||
|
setURL(
|
||||||
|
`https://jsonvisio.com/editor?json=${encodeURIComponent(jsonString)}`
|
||||||
|
);
|
||||||
|
}, [json]);
|
||||||
|
|
||||||
|
const handleShare = () => {
|
||||||
|
copy(url);
|
||||||
|
toast.success(`Link copied to clipboard.`);
|
||||||
|
setVisible(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal visible={visible} setVisible={setVisible}>
|
||||||
|
<Modal.Header>Create a Share Link</Modal.Header>
|
||||||
|
<Modal.Content>
|
||||||
|
{url.length > 5000 ? (
|
||||||
|
<StyledErrorWrapper>
|
||||||
|
<BiErrorAlt size={60} />
|
||||||
|
<StyledWarning>
|
||||||
|
Link size exceeds 5000 characters, unable to generate link for
|
||||||
|
file of this size!
|
||||||
|
</StyledWarning>
|
||||||
|
</StyledErrorWrapper>
|
||||||
|
) : (
|
||||||
|
<StyledInput value={url} type="url" readOnly />
|
||||||
|
)}
|
||||||
|
</Modal.Content>
|
||||||
|
<Modal.Controls setVisible={setVisible}>
|
||||||
|
{url.length < 5000 && (
|
||||||
|
<Button status="SECONDARY" onClick={handleShare}>
|
||||||
|
Copy
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Modal.Controls>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
@ -6,6 +6,9 @@ import {
|
|||||||
useConfigReducer,
|
useConfigReducer,
|
||||||
} from "src/reducer/reducer";
|
} from "src/reducer/reducer";
|
||||||
import { ReactComponent, StorageConfig } from "src/typings/global";
|
import { ReactComponent, StorageConfig } from "src/typings/global";
|
||||||
|
import { isValidJson } from "src/utils/isValidJson";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { Compressed, decompress } from "compress-json";
|
||||||
|
|
||||||
export interface AppConfig {
|
export interface AppConfig {
|
||||||
json: string;
|
json: string;
|
||||||
@ -42,7 +45,23 @@ const WithConfig: ReactComponent = ({ children }) => {
|
|||||||
settings: states.settings,
|
settings: states.settings,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const { json } = router.query;
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
const jsonStored = localStorage.getItem("json");
|
||||||
|
const isJsonValid =
|
||||||
|
typeof json === "string" && isValidJson(decodeURIComponent(json));
|
||||||
|
|
||||||
|
if (isJsonValid) {
|
||||||
|
const jsonDecoded = decompress(JSON.parse(isJsonValid));
|
||||||
|
const jsonString = JSON.stringify(jsonDecoded);
|
||||||
|
|
||||||
|
dispatch({ type: ConfigActionType.SET_JSON, payload: jsonString });
|
||||||
|
} else if (jsonStored) {
|
||||||
|
dispatch({ type: ConfigActionType.SET_JSON, payload: jsonStored });
|
||||||
|
}
|
||||||
|
|
||||||
const configStored = localStorage.getItem("config");
|
const configStored = localStorage.getItem("config");
|
||||||
|
|
||||||
if (configStored) {
|
if (configStored) {
|
||||||
@ -53,7 +72,7 @@ const WithConfig: ReactComponent = ({ children }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setRender(true);
|
setRender(true);
|
||||||
}, [dispatch]);
|
}, [dispatch, json]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (render)
|
if (render)
|
||||||
|
8
src/utils/isValidJson.ts
Normal file
8
src/utils/isValidJson.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export const isValidJson = (str: string) => {
|
||||||
|
try {
|
||||||
|
JSON.parse(str);
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
};
|
10
yarn.lock
10
yarn.lock
@ -2594,6 +2594,11 @@ commondir@^1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
||||||
integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
|
integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
|
||||||
|
|
||||||
|
compress-json@^2.0.1:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/compress-json/-/compress-json-2.0.1.tgz#8460d79e1a5612fab747c20408f997d55f11bc48"
|
||||||
|
integrity sha512-iwGwSGvFd1eN0LQAbbjvBy/liXXKhJpF87u4Rmoqth66o+HqOCwJT3Ok7xY0qCAJhpo4XNH8A699nOUl2G90TQ==
|
||||||
|
|
||||||
concat-map@0.0.1:
|
concat-map@0.0.1:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||||
@ -6612,6 +6617,11 @@ use-resize-observer@^9.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@juggle/resize-observer" "^3.3.1"
|
"@juggle/resize-observer" "^3.3.1"
|
||||||
|
|
||||||
|
usehooks-ts@^2.5.2:
|
||||||
|
version "2.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/usehooks-ts/-/usehooks-ts-2.6.0.tgz#aebab367da2350a0bee1c3749bc6dd4bcce3eaae"
|
||||||
|
integrity sha512-Kj/4oc2nOxRDGTDb2v1ZulF7+tpeXFuqI6cUesM0Vic7TPPDlFORxKh4ivsYg+NTvX/YbM+lhqqkfFTiIt23eg==
|
||||||
|
|
||||||
util-deprecate@~1.0.1:
|
util-deprecate@~1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user