From e6cb91bb74b98e6ab669aa179fa380b889b4325a Mon Sep 17 00:00:00 2001 From: AykutSarac Date: Wed, 1 May 2024 13:17:29 +0300 Subject: [PATCH] feat: UI improvements --- src/components/Loading/index.tsx | 32 ++++++ src/components/MonacoEditor/index.tsx | 4 +- src/containers/Editor/LiveEditor.tsx | 39 +------ src/containers/Modals/TypeModal/index.tsx | 55 ++++----- src/containers/Toolbar/index.tsx | 36 +++++- .../Views/GraphView/CustomEdge/index.tsx | 7 +- .../Views/GraphView/CustomNode/index.tsx | 7 +- src/containers/Views/GraphView/index.tsx | 10 +- src/layout/Loading/index.tsx | 57 ---------- src/layout/Navbar/index.tsx | 104 ++++-------------- src/pages/_app.tsx | 5 +- src/pages/editor.tsx | 20 +--- src/pages/index.tsx | 100 ++++++----------- 13 files changed, 158 insertions(+), 318 deletions(-) create mode 100644 src/components/Loading/index.tsx delete mode 100644 src/layout/Loading/index.tsx diff --git a/src/components/Loading/index.tsx b/src/components/Loading/index.tsx new file mode 100644 index 0000000..d489d87 --- /dev/null +++ b/src/components/Loading/index.tsx @@ -0,0 +1,32 @@ +import React from "react"; +import { useRouter } from "next/router"; +import { LoadingOverlay } from "@mantine/core"; + +export const Loading = () => { + const router = useRouter(); + const [loading, setLoading] = React.useState(false); + + React.useEffect(() => { + const handleStart = (url: string) => { + return url !== router.asPath && url === "/editor" && setLoading(true); + }; + + const handleComplete = (url: string) => url === router.asPath && setLoading(false); + + router.events.on("routeChangeStart", handleStart); + router.events.on("routeChangeComplete", handleComplete); + router.events.on("routeChangeError", handleComplete); + + return () => { + router.events.off("routeChangeStart", handleStart); + router.events.off("routeChangeComplete", handleComplete); + router.events.off("routeChangeError", handleComplete); + }; + }); + + if (loading) { + return ; + } + + return null; +}; diff --git a/src/components/MonacoEditor/index.tsx b/src/components/MonacoEditor/index.tsx index 5845dd9..f4bfa4c 100644 --- a/src/components/MonacoEditor/index.tsx +++ b/src/components/MonacoEditor/index.tsx @@ -1,7 +1,7 @@ import React from "react"; +import { LoadingOverlay } from "@mantine/core"; import styled from "styled-components"; import Editor, { loader, useMonaco } from "@monaco-editor/react"; -import { Loading } from "src/layout/Loading"; import useConfig from "src/store/useConfig"; import useFile from "src/store/useFile"; @@ -81,7 +81,7 @@ export const MonacoEditor = () => { options={editorOptions} onValidate={errors => setError(errors[0]?.message)} onChange={contents => setContents({ contents, skipUpdate: true })} - loading={} + loading={} /> ); diff --git a/src/containers/Editor/LiveEditor.tsx b/src/containers/Editor/LiveEditor.tsx index 19af962..8ef5ce5 100644 --- a/src/containers/Editor/LiveEditor.tsx +++ b/src/containers/Editor/LiveEditor.tsx @@ -1,5 +1,4 @@ import React from "react"; -import { Menu, Text } from "@mantine/core"; import styled from "styled-components"; import { firaMono } from "src/constants/fonts"; import { Graph } from "src/containers/Views/GraphView"; @@ -32,44 +31,8 @@ const View = () => { }; const LiveEditor: React.FC = () => { - const [contextOpened, setContextOpened] = React.useState(false); - const [contextPosition, setContextPosition] = React.useState({ - x: 0, - y: 0, - }); - return ( - { - e.preventDefault(); - setContextOpened(true); - setContextPosition({ x: e.pageX, y: e.pageY }); - }} - onClick={() => setContextOpened(false)} - > -
- - - - Download as Image - - - Zoom to Fit - - - Rotate - - - -
- + e.preventDefault()}> ); diff --git a/src/containers/Modals/TypeModal/index.tsx b/src/containers/Modals/TypeModal/index.tsx index 02dbbc3..64ca6f3 100644 --- a/src/containers/Modals/TypeModal/index.tsx +++ b/src/containers/Modals/TypeModal/index.tsx @@ -55,41 +55,34 @@ export const TypeModal: React.FC = ({ opened, onClose }) => { return typeOptions[typeOptions.findIndex(o => o.value === selectedType)]?.lang; }, [selectedType]); - const transformer = React.useCallback( - async ({ value }) => { - const { run } = await import("json_typegen_wasm"); - return run( - "Root", - value, - JSON.stringify({ - output_mode: selectedType, - }) - ); - }, - [selectedType] - ); - React.useEffect(() => { if (opened) { - try { - setLoading(true); - if (selectedType === Language.Go) { - import("src/lib/utils/json2go").then(jtg => { - import("gofmt.js").then(gofmt => { - const types = jtg.default(getJson()); - setType(gofmt.default(types.go)); - }); - }); - } else { - transformer({ value: getJson() }).then(setType); + (async () => { + try { + setLoading(true); + const json = getJson(); + + if (selectedType === Language.Go) { + const jtg = await import("src/lib/utils/json2go"); + const gofmt = await import("gofmt.js"); + + const types = jtg.default(json); + setType(gofmt.default(types.go)); + } else { + const { run } = await import("json_typegen_wasm"); + const output_mode = selectedType; + const types = run("Root", json, JSON.stringify({ output_mode })); + + setType(types); + } + } catch (error) { + console.error(error); + } finally { + setLoading(false); } - } catch (error) { - console.error(error); - } finally { - setLoading(false); - } + })(); } - }, [getJson, opened, selectedType, transformer]); + }, [getJson, opened, selectedType]); return ( diff --git a/src/containers/Toolbar/index.tsx b/src/containers/Toolbar/index.tsx index 6f05fe0..e0d9ebe 100644 --- a/src/containers/Toolbar/index.tsx +++ b/src/containers/Toolbar/index.tsx @@ -1,5 +1,6 @@ import React from "react"; -import { Badge, Flex, Group, Select, Text } from "@mantine/core"; +import { Badge, Flex, Group, Indicator, Select, Text } from "@mantine/core"; +import { useSessionStorage } from "@mantine/hooks"; import toast from "react-hot-toast"; import { AiOutlineFullscreen } from "react-icons/ai"; import { AiFillGift } from "react-icons/ai"; @@ -35,6 +36,10 @@ export const Toolbar: React.FC<{ isWidget?: boolean }> = ({ isWidget = false }) const setFormat = useFile(state => state.setFormat); const format = useFile(state => state.format); const premium = useUser(state => state.premium); + const [seenPremium, setSeenPremium] = useSessionStorage({ + key: "seenPremium", + defaultValue: false, + }); return ( @@ -74,11 +79,30 @@ export const Toolbar: React.FC<{ isWidget?: boolean }> = ({ isWidget = false }) )} {!premium && !isWidget && ( - setVisible("premium")(true)}> - - - Get Premium - + { + setSeenPremium(true); + setVisible("premium")(true); + }} + > + + + + Get Premium + + )} diff --git a/src/containers/Views/GraphView/CustomEdge/index.tsx b/src/containers/Views/GraphView/CustomEdge/index.tsx index ab77e94..d1c5599 100644 --- a/src/containers/Views/GraphView/CustomEdge/index.tsx +++ b/src/containers/Views/GraphView/CustomEdge/index.tsx @@ -1,10 +1,5 @@ import React from "react"; -import dynamic from "next/dynamic"; -import { EdgeProps } from "reaflow/dist/symbols/Edge"; - -const Edge = dynamic(() => import("reaflow").then(r => r.Edge), { - ssr: false, -}); +import { Edge, EdgeProps } from "reaflow"; const CustomEdgeWrapper = (props: EdgeProps) => { return ; diff --git a/src/containers/Views/GraphView/CustomNode/index.tsx b/src/containers/Views/GraphView/CustomNode/index.tsx index 02718db..6404d7e 100644 --- a/src/containers/Views/GraphView/CustomNode/index.tsx +++ b/src/containers/Views/GraphView/CustomNode/index.tsx @@ -1,16 +1,11 @@ import React from "react"; -import dynamic from "next/dynamic"; -import { NodeProps } from "reaflow"; +import { Node, NodeProps } from "reaflow"; import useGraph from "src/store/useGraph"; import useModal from "src/store/useModal"; import { NodeData } from "src/types/graph"; import { ObjectNode } from "./ObjectNode"; import { TextNode } from "./TextNode"; -const Node = dynamic(() => import("reaflow").then(r => r.Node), { - ssr: false, -}); - export interface CustomNodeProps { node: NodeData; x: number; diff --git a/src/containers/Views/GraphView/index.tsx b/src/containers/Views/GraphView/index.tsx index 4ba5471..32cc304 100644 --- a/src/containers/Views/GraphView/index.tsx +++ b/src/containers/Views/GraphView/index.tsx @@ -1,22 +1,18 @@ import React from "react"; -import dynamic from "next/dynamic"; +import { LoadingOverlay } from "@mantine/core"; import styled from "styled-components"; import debounce from "lodash.debounce"; import { Space } from "react-zoomable-ui"; +import { Canvas } from "reaflow"; import { ElkRoot } from "reaflow/dist/layout/useLayout"; import { useLongPress } from "use-long-press"; import { CustomNode } from "src/containers/Views/GraphView/CustomNode"; import useToggleHide from "src/hooks/useToggleHide"; -import { Loading } from "src/layout/Loading"; import useConfig from "src/store/useConfig"; import useGraph from "src/store/useGraph"; import { CustomEdge } from "./CustomEdge"; import { PremiumView } from "./PremiumView"; -const Canvas = dynamic(() => import("reaflow").then(r => r.Canvas), { - ssr: false, -}); - interface GraphProps { isWidget?: boolean; } @@ -175,7 +171,7 @@ export const Graph = ({ isWidget = false }: GraphProps) => { return ( <> - + e.preventDefault()} diff --git a/src/layout/Loading/index.tsx b/src/layout/Loading/index.tsx deleted file mode 100644 index 2e154d9..0000000 --- a/src/layout/Loading/index.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from "react"; -import { Center, Stack, Text } from "@mantine/core"; -import styled, { keyframes } from "styled-components"; -import { JSONCrackLogo } from "../JsonCrackLogo"; - -interface LoadingProps { - loading?: boolean; - message?: string; -} - -const fadeIn = keyframes` - 99% { - visibility: hidden; - } - 100% { - visibility: visible; - } -`; - -const StyledLoading = styled.div<{ $visible: boolean }>` - display: ${({ $visible }) => ($visible ? "grid" : "none")}; - position: fixed; - top: 0; - left: 0; - place-content: center; - width: 100%; - height: 100vh; - text-align: center; - z-index: 100; - pointer-events: visiblePainted; - animation: 200ms ${fadeIn}; - animation-fill-mode: forwards; - visibility: hidden; - background: ${({ theme }) => theme.BACKGROUND_NODE}; - opacity: 0.8; - color: ${({ theme }) => theme.INTERACTIVE_HOVER}; - cursor: wait; - - img { - transform: rotate(45deg); - } -`; - -export const Loading = ({ loading = false, message }: LoadingProps) => { - return ( -
- - - - - {message ?? "Preparing the environment for you..."} - - - -
- ); -}; diff --git a/src/layout/Navbar/index.tsx b/src/layout/Navbar/index.tsx index a1ba333..44d7858 100644 --- a/src/layout/Navbar/index.tsx +++ b/src/layout/Navbar/index.tsx @@ -1,6 +1,6 @@ import React from "react"; import Link from "next/link"; -import { Box, Burger, Button, Flex, Overlay } from "@mantine/core"; +import { Button } from "@mantine/core"; import { useDisclosure } from "@mantine/hooks"; import styled from "styled-components"; import useUser from "src/store/useUser"; @@ -26,18 +26,6 @@ const StyledNavbar = styled.nav` @media only screen and (max-width: 768px) { padding: 16px 24px; } - - @media only screen and (max-width: 1024px) { - .desktop { - display: none; - } - } - - @media only screen and (max-width: 768px) { - .hide-mobile { - display: none; - } - } `; const Left = styled.div``; @@ -98,74 +86,28 @@ export const Navbar = () => { - {!hasSession && ( - <> - - - - )} - {hasSession && ( - - )} - - {opened && ( - - - - - - - - )} + + diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 84c9664..419e24c 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -8,12 +8,14 @@ import "@mantine/core/styles.css"; import "@mantine/code-highlight/styles.css"; import { ThemeProvider } from "styled-components"; import ReactGA from "react-ga4"; +import { Loading } from "src/components/Loading"; import GlobalStyle from "src/constants/globalStyle"; import { lightTheme } from "src/constants/theme"; import { supabase } from "src/lib/api/supabase"; import useUser from "src/store/useUser"; const Toaster = dynamic(() => import("react-hot-toast").then(c => c.Toaster)); +const ExternalMode = dynamic(() => import("src/layout/ExternalMode")); const mantineTheme = createTheme({ primaryShade: 8, @@ -24,8 +26,6 @@ const GA_TRACKING_ID = process.env.NEXT_PUBLIC_GA_ID; ReactGA.initialize(GA_TRACKING_ID, { testMode: isDevelopment }); -const ExternalMode = dynamic(() => import("src/layout/ExternalMode")); - function JsonCrack({ Component, pageProps }: AppProps) { const router = useRouter(); const setSession = useUser(state => state.setSession); @@ -72,6 +72,7 @@ function JsonCrack({ Component, pageProps }: AppProps) { }} /> + diff --git a/src/pages/editor.tsx b/src/pages/editor.tsx index e0e9904..5451c12 100644 --- a/src/pages/editor.tsx +++ b/src/pages/editor.tsx @@ -1,16 +1,12 @@ import React from "react"; -import dynamic from "next/dynamic"; import Head from "next/head"; import { useRouter } from "next/router"; import styled from "styled-components"; import { BottomBar } from "src/containers/Editor/BottomBar"; +import Panes from "src/containers/Editor/Panes"; import { Toolbar } from "src/containers/Toolbar"; import { EditorWrapper } from "src/layout/EditorWrapper"; -import { Loading } from "src/layout/Loading"; import useFile from "src/store/useFile"; -import useJson from "src/store/useJson"; - -const Panes = dynamic(() => import("src/containers/Editor/Panes")); export const StyledPageWrapper = styled.div` height: calc(100vh - 27px); @@ -29,7 +25,6 @@ export const StyledEditorWrapper = styled.div` const EditorPage: React.FC = () => { const { query, isReady } = useRouter(); - const loading = useJson(state => state.loading); const hasQuery = React.useMemo(() => Object.keys(query).length > 0, [query]); const checkEditorSession = useFile(state => state.checkEditorSession); @@ -37,24 +32,13 @@ const EditorPage: React.FC = () => { if (isReady) checkEditorSession(query?.json); }, [checkEditorSession, isReady, query]); - if (loading) { - return ( - - - Editor | JSON Crack - {hasQuery && } - - - - ); - } - return ( Editor | JSON Crack + {hasQuery && } diff --git a/src/pages/index.tsx b/src/pages/index.tsx index bbb9228..6e96359 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -8,7 +8,6 @@ import { Center, Flex, Grid, - Group, Image, Paper, Stack, @@ -21,7 +20,6 @@ import { Carousel } from "@mantine/carousel"; import "@mantine/carousel/styles.css"; import styled from "styled-components"; import { BiChevronDown } from "react-icons/bi"; -import { FaRocket } from "react-icons/fa"; import { MdChevronRight, MdCompare, @@ -78,19 +76,19 @@ const StyledHeroSectionBody = styled.div` const StyledHeroText = styled.p` font-size: 0.8rem; - color: #414141; + color: #5b5b5b; font-weight: 400; max-width: 100%; min-width: 400px; text-align: center; @media only screen and (min-width: 576px) { - font-size: 1.3rem; + font-size: 1.2rem; max-width: 80%; } @media only screen and (min-width: 1400px) { - font-size: 1.4rem; + font-size: 1.3rem; max-width: 60%; } `; @@ -212,77 +210,51 @@ export const HomePage = () => { Experience the ultimate online editor designed to empower you in visualizing, refining, and formatting data effortlessly. - - - - - - - + JSON - + YAML - + CSV - + XML - + TOML + +