Explorar o código

add light theme

AykutSarac %!s(int64=3) %!d(string=hai) anos
pai
achega
73e042b98b

+ 5 - 1
src/components/Input/index.tsx

@@ -39,9 +39,13 @@ const StyledInput = styled.input`
 const StyledSearchButton = styled.button`
   display: grid;
   background: none;
-  color: inherit;
+  color: ${({ theme }) => theme.INTERACTIVE_NORMAL};
   padding: 0;
   min-height: unset;
+
+  &:hover {
+    box-shadow: none;
+  }
 `;
 
 export const Input = () => {

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

@@ -36,7 +36,7 @@ const StyledSidebar = styled.div`
   width: 42px;
   background: ${({ theme }) => theme.BACKGROUND_TERTIARY};
   padding: 8px;
-  border-right: 1px solid ${({ theme }) => theme.SILVER_DARK};
+  border-right: 1px solid ${({ theme }) => theme.BACKGROUND_MODIFIER_ACCENT};
 `;
 
 const StyledElement = styled.div`
@@ -61,7 +61,7 @@ const StyledElement = styled.div`
 
 const StyledText = styled.span<{ secondary?: boolean }>`
   color: ${({ theme, secondary }) =>
-    secondary ? theme.FULL_WHITE : theme.ORANGE};
+    secondary ? theme.INTERACTIVE_NORMAL : theme.ORANGE};
 `;
 
 const StyledTopWrapper = styled.nav`

+ 4 - 1
src/components/Tooltip/index.tsx

@@ -17,7 +17,7 @@ const StyledTooltip = styled.div<{ visible: boolean }>`
   right: 0;
   transform: translate(calc(100% + 15px), 25%);
   z-index: 5;
-  background: ${({ theme }) => theme.SILVER_DARK};
+  background: ${({ theme }) => theme.BACKGROUND_PRIMARY};
   color: ${({ theme }) => theme.TEXT_NORMAL};
   border-radius: 5px;
   padding: 4px 8px;
@@ -27,6 +27,9 @@ const StyledTooltip = styled.div<{ visible: boolean }>`
   font-size: 16px;
   user-select: none;
   font-weight: 600;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.07), 0 2px 4px rgba(0, 0, 0, 0.07),
+    0 4px 8px rgba(0, 0, 0, 0.07), 0 8px 16px rgba(0, 0, 0, 0.07),
+    0 16px 32px rgba(0, 0, 0, 0.07), 0 32px 64px rgba(0, 0, 0, 0.07);
 `;
 
 const StyledChildren = styled.div``;

+ 1 - 0
src/constants/data.ts

@@ -40,4 +40,5 @@ export const defaultConfig: StorageConfig = {
   hideEditor: false,
   searchNode: "",
   zoomPanPinch: null,
+  lightmode: false
 };

+ 32 - 1
src/constants/theme.ts

@@ -20,11 +20,42 @@ export const darkTheme: DefaultTheme = {
   INTERACTIVE_NORMAL: "#b9bbbe",
   INTERACTIVE_HOVER: "#dcddde",
   INTERACTIVE_ACTIVE: "#fff",
+  BACKGROUND_NODE: "#2B2C3E",
   BACKGROUND_TERTIARY: "#202225",
   BACKGROUND_SECONDARY: "#2f3136",
   BACKGROUND_PRIMARY: "#36393f",
   BACKGROUND_MODIFIER_ACCENT: "rgba(79,84,92,0.48)",
   TEXT_NORMAL: "#dcddde",
   TEXT_POSITIVE: "hsl(139,calc(var(--saturation-factor, 1)*51.6%),52.2%)",
-  TEXT_DANGER: "hsl(359,calc(var(--saturation-factor, 1)*82%),73.9%)"
+  TEXT_DANGER: "hsl(359,calc(var(--saturation-factor, 1)*82%),73.9%)",
+} as const;
+
+export const lightTheme: DefaultTheme = {
+  BLURPLE: "#5865F2",
+  FULL_WHITE: "#FFFFFF",
+  BLACK: "#202225",
+  BLACK_DARK: "#2C2F33",
+  BLACK_LIGHT: "#2F3136",
+  BLACK_PRIMARY: "#36393f",
+  BLACK_SECONDARY: "#23272A",
+  CRIMSON: "#DC143C",
+  DARK_SALMON: "#E9967A",
+  DANGER: "#db662e",
+  LIGHTGREEN: "#90EE90",
+  SEAGREEN: "#3BA55D",
+  ORANGE: "#FAA81A",
+  SILVER: "#B9BBBE",
+  SILVER_DARK: "#4D4D4D",
+
+  INTERACTIVE_NORMAL: "#4f5660",
+  INTERACTIVE_HOVER: "#2e3338",
+  INTERACTIVE_ACTIVE: "#060607",
+  BACKGROUND_NODE: "#FFFFFF",
+  BACKGROUND_TERTIARY: "#e3e5e8",
+  BACKGROUND_SECONDARY: "#f2f3f5",
+  BACKGROUND_PRIMARY: "#FFFFFF",
+  BACKGROUND_MODIFIER_ACCENT: "rgba(106,116,128,0.24)",
+  TEXT_NORMAL: "#2e3338",
+  TEXT_POSITIVE: "hsl(139,calc(var(--saturation-factor, 1)*51.6%),52.2%)",
+  TEXT_DANGER: "hsl(359,calc(var(--saturation-factor, 1)*82%),73.9%)",
 } as const;

+ 8 - 1
src/containers/Editor/Tools.tsx

@@ -6,6 +6,7 @@ import {
   AiOutlineMinus,
   AiOutlinePlus,
 } from "react-icons/ai";
+import { HiOutlineSun, HiOutlineMoon } from "react-icons/hi";
 import { MdCenterFocusWeak } from "react-icons/md";
 import { Input } from "src/components/Input";
 import { useConfig } from "src/hocs/config";
@@ -19,9 +20,10 @@ export const StyledTools = styled.div`
   flex-direction: row-reverse;
   height: 28px;
   padding: 4px 16px;
-  border-bottom: 1px solid #1f2124;
   background: ${({ theme }) => theme.BACKGROUND_PRIMARY};
   color: ${({ theme }) => theme.SILVER};
+
+  box-shadow: 0 1px 0px ${({ theme }) => theme.BACKGROUND_TERTIARY};
 `;
 
 const StyledToolElement = styled.button`
@@ -53,11 +55,16 @@ export const Tools: React.FC = () => {
 
   const toggleEditor = () => dispatch({ type: ConfigActionType.TOGGLE_DOCK });
 
+  const toggleTheme = () => dispatch({ type: ConfigActionType.TOGGLE_THEME });
+
   return (
     <StyledTools>
       <StyledToolElement aria-label="fullscreen" onClick={toggleEditor}>
         <AiOutlineFullscreen />
       </StyledToolElement>
+      <StyledToolElement aria-label="switch theme" onClick={toggleTheme}>
+        {states.settings.lightmode ? <HiOutlineMoon /> : <HiOutlineSun />}
+      </StyledToolElement>
       <Input />
       <StyledToolElement aria-label="save" onClick={handleSave}>
         <AiOutlineSave />

+ 3 - 3
src/containers/Editor/styles.tsx

@@ -21,7 +21,7 @@ export const StyledEditor = styled(SplitPane)`
   background: ${({ theme }) => theme.BACKGROUND_SECONDARY};
 
   .Resizer {
-    background: ${({ theme }) => theme.BLACK};
+    background: ${({ theme }) => theme.BACKGROUND_MODIFIER_ACCENT};
     box-sizing: border-box;
     background-clip: padding-box;
     z-index: 1;
@@ -50,12 +50,12 @@ export const StyledEditor = styled(SplitPane)`
   }
 
   .Resizer.vertical {
-    width: 16px;
+    width: 14px;
     margin: 0 -5px;
     border-left: 5px solid rgba(255, 255, 255, 0);
     border-right: 5px solid rgba(255, 255, 255, 0);
     cursor: col-resize;
-    z-index: 0 !important;
+    z-index: 1;
   }
 
   .Resizer.vertical:hover {

+ 6 - 3
src/containers/JsonEditor/ErrorContainer.tsx

@@ -17,13 +17,16 @@ interface ErrorContainerProps {
   setError: React.Dispatch<React.SetStateAction<Error>>;
 }
 
-const StyledErrorWrapper = styled.div``;
+const StyledErrorWrapper = styled.div`
+  z-index: 5;
+`;
 
 const StyledErrorHeader = styled.div`
   height: 28px;
   padding: 4px 16px;
-  border-bottom: 1px solid #1f2124;
-  background: ${({ theme }) => theme.BLACK_DARK};
+  background: ${({ theme }) => theme.BACKGROUND_SECONDARY};
+
+  box-shadow: 0 1px 0px ${({ theme }) => theme.BACKGROUND_TERTIARY};
 `;
 
 const StyledErrorExpand = styled.button<{ error: boolean }>`

+ 8 - 2
src/containers/JsonEditor/index.tsx

@@ -2,11 +2,12 @@ import React from "react";
 import AceEditor from "react-ace";
 import parseJson from "parse-json";
 import styled from "styled-components";
-import { Error, ErrorContainer } from "./ErrorContainer";
+import { ErrorContainer } from "./ErrorContainer";
 import { ConfigActionType } from "src/reducer/reducer";
 import { useConfig } from "src/hocs/config";
 require("ace-builds/src-noconflict/mode-json");
 require("ace-builds/src-noconflict/theme-tomorrow_night");
+require("ace-builds/src-noconflict/theme-github");
 
 const StyledEditorWrapper = styled.div`
   display: flex;
@@ -32,6 +33,11 @@ const JsonEditor: React.FC = () => {
     isExpanded: true,
   });
 
+  const editorTheme = React.useMemo(
+    () => (settings.lightmode ? "github" : "tomorrow_night"),
+    [settings.lightmode]
+  );
+
   React.useEffect(() => {
     const resizeObserver = new ResizeObserver((observed) => {
       const width = observed[0].contentRect.width;
@@ -83,7 +89,7 @@ const JsonEditor: React.FC = () => {
         value={value}
         onChange={setValue}
         mode="json"
-        theme="tomorrow_night"
+        theme={editorTheme}
         width={editorWidth}
         height="100%"
         fontSize={12}

+ 3 - 11
src/containers/LiveEditor/CustomNode/index.tsx

@@ -18,19 +18,11 @@ const baseLabelStyle = {
   strokeWidth: 0,
 };
 
-const basePortStyle = {
-  fill: "black",
-};
-
-export const CustomNode = (nodeProps: NodeProps) => {
+export const CustomNode = React.memo((nodeProps: NodeProps) => {
   const { properties: data } = nodeProps;
 
   return (
-    <Node
-      {...nodeProps}
-      label={<Label style={baseLabelStyle} />}
-      port={<Port style={basePortStyle} rx={10} ry={10} />}
-    >
+    <Node {...nodeProps} label={<Label style={baseLabelStyle} />}>
       {(nodeProps: NodeProps) => {
         const { width, height } = nodeProps;
 
@@ -60,4 +52,4 @@ export const CustomNode = (nodeProps: NodeProps) => {
       }}
     </Node>
   );
-};
+});

+ 4 - 15
src/containers/LiveEditor/index.tsx

@@ -20,20 +20,9 @@ const StyledLiveEditor = styled.div`
 
 const StyledEditorWrapper = styled.div`
   position: absolute;
-`;
 
-const StyledControls = styled.div`
-  position: fixed;
-  display: grid;
-  grid-template-columns: 1fr 1fr;
-  grid-template-rows: 1fr 1fr;
-  gap: 8px;
-  bottom: 8px;
-  right: 8px;
-  opacity: 0.9;
-
-  button:hover {
-    opacity: 0.7;
+  rect {
+    fill: ${({ theme }) => theme.BACKGROUND_NODE};
   }
 `;
 
@@ -118,7 +107,7 @@ export const LiveEditor: React.FC = React.memo(function LiveEditor() {
             <TransformComponent>
               <Canvas
                 nodes={data.nodes}
-                node={CustomNode}
+                node={(props) => <CustomNode {...props} />}
                 edges={data.edges}
                 maxWidth={20000}
                 maxHeight={20000}
@@ -127,7 +116,7 @@ export const LiveEditor: React.FC = React.memo(function LiveEditor() {
                 fit={true}
                 direction={settings.layout}
                 readonly
-                key={settings.layout}
+                key={settings.layout || settings.lightmode}
                 onCanvasClick={onCanvasClick}
               />
             </TransformComponent>

+ 9 - 1
src/hocs/config.tsx

@@ -37,4 +37,12 @@ const WithConfig: React.FC = ({ children }) => {
   );
 };
 
-export { WithConfig, useConfig, ConfigContext };
+const withConfig = (Component) => {
+  return (props) => (
+    <WithConfig>
+      <Component {...props} />
+    </WithConfig>
+  );
+};
+
+export { WithConfig, useConfig, ConfigContext, withConfig };

+ 8 - 3
src/pages/_app.tsx

@@ -3,13 +3,18 @@ import type { AppProps } from "next/app";
 import { ThemeProvider } from "styled-components";
 
 import GlobalStyle from "src/constants/globalStyle";
-import { darkTheme } from "src/constants/theme";
+import { darkTheme, lightTheme } from "src/constants/theme";
 import { Toaster } from "react-hot-toast";
+import { useConfig, withConfig, WithConfig } from "src/hocs/config";
 
 function JsonVisio({ Component, pageProps }: AppProps) {
+  const {
+    states: { settings },
+  } = useConfig();
+
   return (
     <>
-      <ThemeProvider theme={darkTheme}>
+      <ThemeProvider theme={settings.lightmode ? lightTheme : darkTheme}>
         <GlobalStyle />
         <Component {...pageProps} />
         <Toaster
@@ -26,4 +31,4 @@ function JsonVisio({ Component, pageProps }: AppProps) {
   );
 }
 
-export default JsonVisio;
+export default withConfig(JsonVisio);

+ 1 - 4
src/pages/editor/index.tsx

@@ -1,7 +1,6 @@
 import React from "react";
 import Head from "next/head";
 import { Editor } from "src/containers/Editor";
-import { WithConfig } from "src/hocs/config";
 
 const EditorPage: React.FC = () => {
   return (
@@ -13,9 +12,7 @@ const EditorPage: React.FC = () => {
           content="View your JSON data in graphs instantly."
         />
       </Head>
-      <WithConfig>
-        <Editor />
-      </WithConfig>
+      <Editor />
     </>
   );
 };

+ 10 - 0
src/reducer/reducer.ts

@@ -8,6 +8,7 @@ export enum ConfigActionType {
   TOGGLE_EXPAND,
   TOGGLE_AUTOFORMAT,
   TOGGLE_DOCK,
+  TOGGLE_THEME,
   ZOOM_IN,
   ZOOM_OUT,
   CENTER_VIEW,
@@ -29,6 +30,15 @@ export const useConfigReducer: React.Reducer<AppConfig, ReducerAction> = (
     case ConfigActionType.SET_CONFIG:
       return { ...state, settings: action.payload };
 
+    case ConfigActionType.TOGGLE_THEME:
+      return {
+        ...state,
+        settings: {
+          ...state.settings,
+          lightmode: !state.settings.lightmode,
+        },
+      };
+
     case ConfigActionType.SET_ZOOM_PAN_PICNH_REF:
       return {
         ...state,

+ 1 - 0
src/typings/global.ts

@@ -8,4 +8,5 @@ export interface StorageConfig {
   hideEditor: boolean;
   searchNode: string;
   zoomPanPinch: ReactZoomPanPinchRef | null;
+  lightmode: boolean;
 }

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

@@ -28,6 +28,7 @@ declare module "styled-components" {
     INTERACTIVE_NORMAL: string;
     INTERACTIVE_HOVER: string;
     INTERACTIVE_ACTIVE: string;
+    BACKGROUND_NODE: string;
     BACKGROUND_TERTIARY: string;
     BACKGROUND_SECONDARY: string;
     BACKGROUND_PRIMARY: string;