瀏覽代碼

new design implementations

AykutSarac 2 年之前
父節點
當前提交
4f74321303

+ 17 - 0
src/components/Button/index.tsx

@@ -28,14 +28,31 @@ const StyledButton = styled.button<{
   color: #ffffff;
   padding: 8px 16px;
   min-width: 60px;
+  min-height: 32px;
+  border-radius: 3px;
+  font-size: 14px;
+  font-weight: 500;
+  font-family: "Catamaran", sans-serif;
   width: ${({ block }) => (block ? "100%" : "fit-content")};
   height: 40px;
+  background-image: none;
 
   :disabled {
     cursor: not-allowed;
     opacity: 0.5;
   }
 
+  div {
+    white-space: normal;
+    margin: 0 auto;
+    text-overflow: ellipsis;
+    overflow: hidden;
+  }
+
+  &:hover {
+    background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0);
+  }
+
   @media only screen and (max-width: 768px) {
     font-size: 14px;
   }

+ 2 - 1
src/components/ErrorContainer/ErrorContainer.tsx

@@ -12,6 +12,7 @@ const StyledErrorWrapper = styled.div`
 `;
 
 const StyledErrorExpand = styled.button<{ error: boolean }>`
+  position: relative;
   display: flex;
   width: 100%;
   padding: 4px 16px;
@@ -25,10 +26,10 @@ const StyledErrorExpand = styled.button<{ error: boolean }>`
   background: ${({ theme }) => theme.BACKGROUND_SECONDARY};
   box-shadow: 0 1px 0px ${({ theme }) => theme.BACKGROUND_TERTIARY};
   cursor: pointer;
+  z-index: 100;
 
   &:hover {
     color: ${({ theme }) => theme.TEXT_DANGER};
-    box-shadow: none;
   }
 `;
 

+ 3 - 0
src/components/Graph/index.tsx

@@ -24,6 +24,9 @@ const StyledEditorWrapper = styled.div<{ isWidget: boolean }>`
   width: 100%;
   height: ${({ isWidget }) => (isWidget ? "100vh" : "calc(100vh - 36px)")};
   background: ${({ theme }) => theme.BACKGROUND_SECONDARY};
+  background-image:  ${({ theme }) => `radial-gradient(#505050 0.5px, ${theme.BACKGROUND_SECONDARY} 0.5px)`}; 
+    background-size: 10px 10px;
+    z-index: 0;
 
   :active {
     cursor: move;

+ 1 - 1
src/components/Modal/styles.tsx

@@ -15,7 +15,7 @@ export const ModalWrapper = styled.div`
   justify-content: center;
   align-items: center;
   background: rgba(0, 0, 0, 0.85);
-  z-index: 36;
+  z-index: 100;
 
   * {
     box-sizing: border-box;

+ 34 - 19
src/components/Sidebar/index.tsx

@@ -13,6 +13,7 @@ import {
   AiOutlineLink,
   AiOutlineEdit,
 } from "react-icons/ai";
+import { FiDownload } from "react-icons/fi";
 
 import { Tooltip } from "src/components/Tooltip";
 import { useRouter } from "next/router";
@@ -24,6 +25,7 @@ import { HiHeart } from "react-icons/hi";
 import shallow from "zustand/shallow";
 import { MdCenterFocusWeak } from "react-icons/md";
 import { getNextLayout } from "src/utils/getNextLayout";
+import { DownloadModal } from "src/containers/Modals/DownloadModal";
 
 const StyledSidebar = styled.div`
   display: flex;
@@ -41,19 +43,19 @@ const StyledSidebar = styled.div`
   }
 `;
 
-const StyledElement = styled.div<{ beta?: boolean }>`
+const StyledElement = styled.button`
   position: relative;
   display: flex;
   justify-content: center;
   text-align: center;
   font-size: 26px;
   font-weight: 600;
-  width: 100%;
-  color: ${({ theme }) => theme.INTERACTIVE_NORMAL};
+  width: fit-content;
+  color: ${({ theme }) => theme.SIDEBAR_ICONS};
   cursor: pointer;
 
   svg {
-    padding: 8px;
+    padding: 12px 8px;
     vertical-align: middle;
   }
 
@@ -64,11 +66,20 @@ const StyledElement = styled.div<{ beta?: boolean }>`
   &:hover :is(a, svg) {
     color: ${({ theme }) => theme.INTERACTIVE_HOVER};
   }
+
+  @media only screen and (max-width: 768px) {
+    font-size: 22px;
+
+    svg {
+      padding: 8px 4px;
+      vertical-align: middle;
+    }
+  }
 `;
 
 const StyledText = styled.span<{ secondary?: boolean }>`
   color: ${({ theme, secondary }) =>
-    secondary ? theme.INTERACTIVE_NORMAL : theme.ORANGE};
+    secondary ? theme.INTERACTIVE_HOVER : theme.ORANGE};
 `;
 
 const StyledFlowIcon = styled(TiFlowMerge)<{ rotate: number }>`
@@ -82,21 +93,14 @@ const StyledTopWrapper = styled.nav`
   align-items: center;
   width: 100%;
 
-  & > div:nth-child(n + 1) {
-    border-bottom: 1px solid ${({ theme }) => theme.BACKGROUND_MODIFIER_ACCENT};
-  }
-
   .mobile {
     display: none;
   }
 
   @media only screen and (max-width: 768px) {
+    justify-content: space-evenly;
     flex-direction: row;
 
-    & > div:nth-child(n + 1) {
-      border-bottom: none;
-    }
-
     .mobile {
       display: initial;
     }
@@ -114,18 +118,19 @@ const StyledBottomWrapper = styled.nav`
   align-items: center;
   width: 100%;
 
-  & > div,
-  a:nth-child(0) {
-    border-top: 1px solid ${({ theme }) => theme.BACKGROUND_MODIFIER_ACCENT};
-  }
-
   @media only screen and (max-width: 768px) {
     display: none;
   }
 `;
 
-const StyledLogo = styled.div`
+const StyledLogo = styled.a`
   color: ${({ theme }) => theme.FULL_WHITE};
+  padding: 8px 4px;
+  border-bottom: 1px solid ${({ theme }) => theme.BACKGROUND_MODIFIER_ACCENT};
+
+  @media only screen and (max-width: 768px) {
+    border-bottom: 0;
+  }
 `;
 
 function rotateLayout(layout: "LEFT" | "RIGHT" | "DOWN" | "UP") {
@@ -142,6 +147,7 @@ export const Sidebar: React.FC = () => {
   const [uploadVisible, setUploadVisible] = React.useState(false);
   const [clearVisible, setClearVisible] = React.useState(false);
   const [shareVisible, setShareVisible] = React.useState(false);
+  const [isDownloadVisible, setDownloadVisible] = React.useState(false);
   const { push } = useRouter();
 
   const [expand, layout, hideEditor] = useConfig(
@@ -215,6 +221,11 @@ export const Sidebar: React.FC = () => {
             <AiOutlineSave />
           </StyledElement>
         </Tooltip>
+        <Tooltip className="mobile" title="Download Image">
+          <StyledElement onClick={() => setDownloadVisible(true)}>
+            <FiDownload />
+          </StyledElement>
+        </Tooltip>
         <Tooltip title="Clear JSON">
           <StyledElement onClick={() => setClearVisible(true)}>
             <AiOutlineDelete />
@@ -252,6 +263,10 @@ export const Sidebar: React.FC = () => {
       <ImportModal visible={uploadVisible} setVisible={setUploadVisible} />
       <ClearModal visible={clearVisible} setVisible={setClearVisible} />
       <ShareModal visible={shareVisible} setVisible={setShareVisible} />
+      <DownloadModal
+        visible={isDownloadVisible}
+        setVisible={setDownloadVisible}
+      />
     </StyledSidebar>
   );
 };

+ 2 - 2
src/components/Tooltip/index.tsx

@@ -7,7 +7,7 @@ interface TooltipProps extends React.ComponentPropsWithoutRef<"div"> {
 
 const StyledTooltipWrapper = styled.div`
   position: relative;
-  width: 100%;
+  width: fit-content;
   height: 100%;
 `;
 
@@ -15,7 +15,7 @@ const StyledTooltip = styled.div<{ visible: boolean }>`
   position: absolute;
   top: 0;
   right: 0;
-  transform: translate(calc(100% + 15px), 10%);
+  transform: translate(calc(100% + 15px), 25%);
   z-index: 5;
   background: ${({ theme }) => theme.BACKGROUND_PRIMARY};
   color: ${({ theme }) => theme.TEXT_NORMAL};

+ 2 - 2
src/constants/data.ts

@@ -1,5 +1,5 @@
 // Example taken from https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json
-export const defaultJson = {
+export const defaultJson = JSON.stringify({
   squadName: "Super hero squad",
   homeTown: "Metro City",
   formed: 2016,
@@ -35,4 +35,4 @@ export const defaultJson = {
       ],
     },
   ],
-};
+});

+ 5 - 17
src/constants/globalStyle.ts

@@ -35,26 +35,14 @@ const GlobalStyle = createGlobalStyle`
   }
 
   button {
-    min-height: 32px;
     border: none;
-    border-radius: 3px;
     outline: none;
-    font-family: 'Catamaran', sans-serif;
-    font-weight: 500;
-    font-size: 14px;
-    background-image: none;
+    -webkit-tap-highlight-color: transparent;
+    background: transparent;
+    width: fit-content;
+    margin: 0;
+    padding: 0;
     cursor: pointer;
-
-    div {
-      white-space: normal;
-      margin: 0 auto;
-      text-overflow: ellipsis;
-      overflow: hidden;
-    }
-
-    &:hover {
-      background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0);
-    }
   }
 
   #carbonads * {

+ 2 - 0
src/constants/theme.ts

@@ -24,6 +24,7 @@ export const darkTheme: DefaultTheme = {
   SILVER_DARK: "#4D4D4D",
   NODE_KEY: "#FAA81A",
   OBJECT_KEY: "#59b8ff",
+  SIDEBAR_ICONS: "#8B8E90",
 
   INTERACTIVE_NORMAL: "#b9bbbe",
   INTERACTIVE_HOVER: "#dcddde",
@@ -44,6 +45,7 @@ export const lightTheme: DefaultTheme = {
   SILVER_DARK: "#CCCCCC",
   NODE_KEY: "#DC3790",
   OBJECT_KEY: "#0260E8",
+  SIDEBAR_ICONS: "#6D6E70",
 
   INTERACTIVE_NORMAL: "#4f5660",
   INTERACTIVE_HOVER: "#2e3338",

+ 2 - 3
src/containers/Editor/JsonEditor/index.tsx

@@ -1,8 +1,7 @@
 import React from "react";
 import styled from "styled-components";
-import Editor from "@monaco-editor/react";
 import parseJson from "parse-json";
-import { loader } from "@monaco-editor/react";
+import MonacoEditor, { loader } from "@monaco-editor/react";
 import { ErrorContainer } from "src/components/ErrorContainer/ErrorContainer";
 import useConfig from "src/hooks/store/useConfig";
 import { Loading } from "src/components/Loading";
@@ -76,7 +75,7 @@ export const JsonEditor: React.FC = () => {
     <StyledEditorWrapper>
       <ErrorContainer error={error} />
       <StyledWrapper>
-        <Editor
+        <MonacoEditor
           height="100%"
           defaultLanguage="json"
           value={value}

+ 13 - 11
src/containers/Editor/Tools.tsx

@@ -5,18 +5,16 @@ import {
   AiOutlinePlus,
 } from "react-icons/ai";
 import { FiDownload } from "react-icons/fi";
-import { HiOutlineSun, HiOutlineMoon } from "react-icons/hi";
 import { MdCenterFocusWeak } from "react-icons/md";
 import { SearchInput } from "src/components/SearchInput";
 import styled from "styled-components";
 import useConfig from "src/hooks/store/useConfig";
-import shallow from "zustand/shallow";
 import { DownloadModal } from "../Modals/DownloadModal";
-import useStored from "src/hooks/store/useStored";
 import { TbSettings } from "react-icons/tb";
-import { Settings } from "./Settings";
+import { SettingsModal } from "src/containers/Modals/SettingsModal";
 
 export const StyledTools = styled.div`
+  position: relative;
   display: flex;
   align-items: center;
   gap: 4px;
@@ -26,6 +24,7 @@ export const StyledTools = styled.div`
   background: ${({ theme }) => theme.BACKGROUND_PRIMARY};
   color: ${({ theme }) => theme.SILVER};
   box-shadow: 0 1px 0px ${({ theme }) => theme.BACKGROUND_TERTIARY};
+  z-index: 36;
 
   @media only screen and (max-width: 768px) {
     display: none;
@@ -38,6 +37,12 @@ const StyledToolElement = styled.button`
   font-size: 20px;
   background: none;
   color: ${({ theme }) => theme.INTERACTIVE_NORMAL};
+  padding: 6px;
+  border-radius: 3px;
+
+  &:hover {
+    background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0);
+  }
 
   &:hover {
     color: ${({ theme }) => theme.INTERACTIVE_HOVER};
@@ -49,8 +54,6 @@ const StyledToolElement = styled.button`
 export const Tools: React.FC = () => {
   const [settingsVisible, setSettingsVisible] = React.useState(false);
   const [isDownloadVisible, setDownloadVisible] = React.useState(false);
-  const lightmode = useStored((state) => state.lightmode);
-  const setLightTheme = useStored((state) => state.setLightTheme);
 
   const hideEditor = useConfig((state) => state.hideEditor);
   const setConfig = useConfig((state) => state.setConfig);
@@ -59,7 +62,6 @@ export const Tools: React.FC = () => {
   const zoomOut = useConfig((state) => state.zoomOut);
   const centerView = useConfig((state) => state.centerView);
   const toggleEditor = () => setConfig("hideEditor", !hideEditor);
-  const toggleTheme = () => setLightTheme(!lightmode);
 
   return (
     <StyledTools>
@@ -72,9 +74,6 @@ export const Tools: React.FC = () => {
       >
         <TbSettings />
       </StyledToolElement>
-      <StyledToolElement aria-label="switch theme" onClick={toggleTheme}>
-        {lightmode ? <HiOutlineMoon /> : <HiOutlineSun />}
-      </StyledToolElement>
       <SearchInput />
       <StyledToolElement
         aria-label="save"
@@ -95,7 +94,10 @@ export const Tools: React.FC = () => {
         visible={isDownloadVisible}
         setVisible={setDownloadVisible}
       />
-      <Settings visible={settingsVisible} setVisible={setSettingsVisible} />
+      <SettingsModal
+        visible={settingsVisible}
+        setVisible={setSettingsVisible}
+      />
     </StyledTools>
   );
 };

+ 1 - 0
src/containers/Modals/NodeModal/index.tsx

@@ -53,6 +53,7 @@ export const NodeModal = ({
             },
             2
           )}
+          readOnly
         />
       </Modal.Content>
       <Modal.Controls setVisible={closeModal}>

+ 9 - 1
src/containers/Editor/Settings.tsx → src/containers/Modals/SettingsModal/index.tsx

@@ -16,10 +16,12 @@ const StyledModalWrapper = styled.div`
   gap: 20px;
 `;
 
-export const Settings: React.FC<{
+export const SettingsModal: React.FC<{
   visible: boolean;
   setVisible: React.Dispatch<React.SetStateAction<boolean>>;
 }> = ({ visible, setVisible }) => {
+  const lightmode = useStored((state) => state.lightmode);
+  const setLightTheme = useStored((state) => state.setLightTheme);
   const [toggleHideCollapse, hideCollapse] = useStored(
     (state) => [state.toggleHideCollapse, state.hideCollapse],
     shallow
@@ -33,6 +35,12 @@ export const Settings: React.FC<{
           <StyledToggle onChange={toggleHideCollapse} checked={hideCollapse}>
             Hide Collapse/Expand Button
           </StyledToggle>
+          <StyledToggle
+            onChange={() => setLightTheme(!lightmode)}
+            checked={lightmode}
+          >
+            Enable Light Theme
+          </StyledToggle>
         </StyledModalWrapper>
       </Modal.Content>
       <Modal.Controls setVisible={setVisible} />

+ 1 - 1
src/hooks/store/useConfig.tsx

@@ -22,7 +22,7 @@ export interface Config {
 }
 
 const initialStates: Config = {
-  json: JSON.stringify(defaultJson),
+  json: defaultJson,
   cursorMode: "move",
   layout: "RIGHT",
   expand: true,

+ 2 - 2
src/hooks/store/useStored.tsx

@@ -36,9 +36,9 @@ const useStored = create(
         users: [],
         nextDate: Date.now(),
       },
-      setLightTheme: (enabled: boolean) =>
+      setLightTheme: (value: boolean) =>
         set({
-          lightmode: enabled,
+          lightmode: value,
         }),
       setSponsors: (users) =>
         set({

+ 4 - 1
src/hooks/useFocusNode.tsx

@@ -8,6 +8,7 @@ import {
 import useConfig from "./store/useConfig";
 
 export const useFocusNode = () => {
+  const setConfig = useConfig((state) => state.setConfig);
   const zoomPanPinch = useConfig((state) => state.zoomPanPinch);
   const [selectedNode, setSelectedNode] = React.useState(0);
   const [content, setContent] = React.useState({
@@ -18,12 +19,14 @@ export const useFocusNode = () => {
   const skip = () => setSelectedNode((current) => current + 1);
 
   React.useEffect(() => {
+    setConfig("performanceMode", !content.value.length);
+
     const debouncer = setTimeout(() => {
       setContent((val) => ({ ...val, debounced: content.value }));
     }, 1500);
 
     return () => clearTimeout(debouncer);
-  }, [content.value]);
+  }, [content.value, setConfig]);
 
   React.useEffect(() => {
     if (!zoomPanPinch) return;

+ 1 - 1
src/pages/Widget/index.tsx

@@ -43,7 +43,7 @@ const WidgetPage = () => {
 
   React.useEffect(() => {
     if (!query.json) {
-      setJson(JSON.stringify(defaultJson));
+      setJson(defaultJson);
     } else {
       const jsonURI = decodeURIComponent(query.json as string);
       const isJsonValid = isValidJson(jsonURI);

+ 1 - 0
src/typings/styled.d.ts

@@ -20,6 +20,7 @@ declare module "styled-components" {
     PRIMARY: string;
     NODE_KEY: string;
     OBJECT_KEY: string;
+    SIDEBAR_ICONS: string;
 
     INTERACTIVE_NORMAL: string;
     INTERACTIVE_HOVER: string;