Merge branch 'main' into add-shortcut-in-popup

This commit is contained in:
AykutSarac 2022-07-21 20:47:33 +03:00
commit 11cb853a2b
10 changed files with 149 additions and 19 deletions

View File

@ -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",

View File

@ -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>
); );

View File

@ -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;
`; `;

View File

@ -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>
); );
}; };

View File

@ -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;

View File

@ -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" } });

View 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>
);
};

View File

@ -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
View File

@ -0,0 +1,8 @@
export const isValidJson = (str: string) => {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return str;
};

View File

@ -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"