From effac67edd362d9fe5daa43d34c0fac11be63d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aykut=20Sara=C3=A7?= Date: Sat, 13 Jan 2024 15:56:04 +0300 Subject: [PATCH] feat: add ability to edit through tree view (#376) --- package.json | 4 + pnpm-lock.yaml | 32 ++++++++ src/containers/Views/TreeView.tsx | 74 ------------------ src/containers/Views/TreeView/EditModal.tsx | 87 +++++++++++++++++++++ src/containers/Views/TreeView/Label.tsx | 69 ++++++++++++++++ src/containers/Views/TreeView/Value.tsx | 41 ++++++++++ src/containers/Views/TreeView/index.tsx | 60 ++++++++++++++ 7 files changed, 293 insertions(+), 74 deletions(-) delete mode 100644 src/containers/Views/TreeView.tsx create mode 100644 src/containers/Views/TreeView/EditModal.tsx create mode 100644 src/containers/Views/TreeView/Label.tsx create mode 100644 src/containers/Views/TreeView/Value.tsx create mode 100644 src/containers/Views/TreeView/index.tsx diff --git a/package.json b/package.json index 8325c814b7d..63c10d3343f 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,8 @@ "lodash.debounce": "^4.0.8", "lodash.get": "^4.4.2", "lodash.set": "^4.3.2", + "lodash.unset": "^4.5.2", + "lodash.update": "^4.10.2", "maketypes": "^1.1.2", "million": "^2.6.4", "next": "13.5.6", @@ -67,6 +69,8 @@ "@types/lodash.debounce": "^4.0.9", "@types/lodash.get": "^4.4.9", "@types/lodash.set": "^4.3.9", + "@types/lodash.unset": "^4.5.9", + "@types/lodash.update": "^4.10.9", "@types/node": "^20.4.7", "@types/react": "18.2.45", "@types/react-dom": "^18.2.18", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 00113437ce6..9cae76cf157 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -83,6 +83,12 @@ dependencies: lodash.set: specifier: ^4.3.2 version: 4.3.2 + lodash.unset: + specifier: ^4.5.2 + version: 4.5.2 + lodash.update: + specifier: ^4.10.2 + version: 4.10.2 maketypes: specifier: ^1.1.2 version: 1.1.2 @@ -160,6 +166,12 @@ devDependencies: '@types/lodash.set': specifier: ^4.3.9 version: 4.3.9 + '@types/lodash.unset': + specifier: ^4.5.9 + version: 4.5.9 + '@types/lodash.update': + specifier: ^4.10.9 + version: 4.10.9 '@types/node': specifier: ^20.4.7 version: 20.4.7 @@ -1232,6 +1244,18 @@ packages: '@types/lodash': 4.14.202 dev: true + /@types/lodash.unset@4.5.9: + resolution: {integrity: sha512-uyYP6JMM30vKXJtDrPtJyK/t3TdyjhZQ7woJAk1biI/o/sXZvBVqQtiMWsIlYLcn6jSPrG4sjYWTZM34qNM7dA==} + dependencies: + '@types/lodash': 4.14.202 + dev: true + + /@types/lodash.update@4.10.9: + resolution: {integrity: sha512-5qFtUDaJKM33gBYNRO7HwJfCSY0H7V133E1N7uKH6YbEIOqcxRfrH8WGp7qQ37C5OeTJOOKcEgWjC9M77NZVIw==} + dependencies: + '@types/lodash': 4.14.202 + dev: true + /@types/lodash@4.14.202: resolution: {integrity: sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==} @@ -3412,6 +3436,14 @@ packages: resolution: {integrity: sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg==} dev: false + /lodash.unset@4.5.2: + resolution: {integrity: sha512-bwKX88k2JhCV9D1vtE8+naDKlLiGrSmf8zi/Y9ivFHwbmRfA8RxS/aVJ+sIht2XOwqoNr4xUPUkGZpc1sHFEKg==} + dev: false + + /lodash.update@4.10.2: + resolution: {integrity: sha512-0d/9zvc/+lN6QruE81yEuoslwtgKSDStWJY5rgTolfSKiRMonCD3OYAR1Ea2hewjY+KDYzRw73+10Jxs7HwZsQ==} + dev: false + /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} dev: true diff --git a/src/containers/Views/TreeView.tsx b/src/containers/Views/TreeView.tsx deleted file mode 100644 index 0a371ffab42..00000000000 --- a/src/containers/Views/TreeView.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React from "react"; -import { DefaultTheme, useTheme } from "styled-components"; -import { JSONTree } from "react-json-tree"; -import { TextRenderer } from "src/containers/Views/GraphView/CustomNode/TextRenderer"; -import useJson from "src/store/useJson"; - -type TextColorFn = { - theme: DefaultTheme; - $value?: string | unknown; -}; - -function getValueColor({ $value, theme }: TextColorFn) { - if ($value && !Number.isNaN(+$value)) return theme.NODE_COLORS.INTEGER; - if ($value === "true") return theme.NODE_COLORS.BOOL.TRUE; - if ($value === "false") return theme.NODE_COLORS.BOOL.FALSE; - if ($value === "null") return theme.NODE_COLORS.NULL; - - // default - return theme.NODE_COLORS.NODE_VALUE; -} - -function getLabelColor({ $type, theme }: { $type?: string; theme: DefaultTheme }) { - if ($type === "Object") return theme.NODE_COLORS.PARENT_OBJ; - if ($type === "Array") return theme.NODE_COLORS.PARENT_ARR; - return theme.NODE_COLORS.PARENT_OBJ; -} - -export const TreeView = () => { - const theme = useTheme(); - const json = useJson(state => state.json); - - return ( - { - return ( - - {keyPath[0]}: - - ); - }} - valueRenderer={(valueAsString, value) => { - return ( - - {JSON.stringify(value)} - - ); - }} - theme={{ - extend: { - overflow: "scroll", - height: "100%", - scheme: "monokai", - author: "wimer hazenberg (http://www.monokai.nl)", - base00: theme.GRID_BG_COLOR, - }, - }} - /> - ); -}; diff --git a/src/containers/Views/TreeView/EditModal.tsx b/src/containers/Views/TreeView/EditModal.tsx new file mode 100644 index 00000000000..878e30d18da --- /dev/null +++ b/src/containers/Views/TreeView/EditModal.tsx @@ -0,0 +1,87 @@ +import React from "react"; +import { Button, Divider, Group, Modal, TextInput } from "@mantine/core"; +import _unset from "lodash.unset"; +import _update from "lodash.update"; +import { VscLock } from "react-icons/vsc"; +import useFile from "src/store/useFile"; +import useJson from "src/store/useJson"; +import useModal from "src/store/useModal"; +import useUser from "src/store/useUser"; + +interface EditModalProps { + opened: boolean; + setOpened: React.Dispatch>; + selectedValue: string | number | null; + path: (string | number)[]; + value: string; + setValue: React.Dispatch>; + errorMessage: string | null; + setErrorMessage: React.Dispatch>; +} + +export const EditModal = ({ + opened, + setOpened, + selectedValue, + path, + value, + setValue, + errorMessage, + setErrorMessage, +}: EditModalProps) => { + const setContents = useFile(state => state.setContents); + const getJson = useJson(state => state.getJson); + const showPremiumModal = useModal(state => state.setVisible("premium")); + const premium = useUser(state => state.premium); + + return ( + setOpened(false)}> + { + setValue(e.currentTarget.value); + }} + error={errorMessage} + /> + + + + + + + ); +}; diff --git a/src/containers/Views/TreeView/Label.tsx b/src/containers/Views/TreeView/Label.tsx new file mode 100644 index 00000000000..2ae57c304ff --- /dev/null +++ b/src/containers/Views/TreeView/Label.tsx @@ -0,0 +1,69 @@ +import React from "react"; +import { Button, HoverCard } from "@mantine/core"; +import { styled, DefaultTheme } from "styled-components"; +import _get from "lodash.get"; +import { VscEdit } from "react-icons/vsc"; +import { KeyPath } from "react-json-tree"; +import useJson from "src/store/useJson"; + +interface LabelProps { + keyPath: KeyPath; + nodeType: string; + setOpened: React.Dispatch>; + setSelectedValue: React.Dispatch>; + setPath: React.Dispatch>; + setValue: React.Dispatch>; +} + +function getLabelColor({ $type, theme }: { $type?: string; theme: DefaultTheme }) { + if ($type === "Object") return theme.NODE_COLORS.PARENT_OBJ; + if ($type === "Array") return theme.NODE_COLORS.PARENT_ARR; + return theme.NODE_COLORS.PARENT_OBJ; +} + +const StyledLabel = styled.span<{ $nodeType?: string }>` + color: ${({ theme, $nodeType }) => getLabelColor({ theme, $type: $nodeType })}; + + &:hover { + filter: brightness(1.5); + transition: filter 0.2s ease-in-out; + } +`; + +export const Label = ({ + keyPath, + nodeType, + setOpened, + setSelectedValue, + setPath, + setValue, +}: LabelProps) => { + const getJson = useJson(state => state.getJson); + + return ( + + + {keyPath[0]}: + + + + + + ); +}; diff --git a/src/containers/Views/TreeView/Value.tsx b/src/containers/Views/TreeView/Value.tsx new file mode 100644 index 00000000000..c4cf7835783 --- /dev/null +++ b/src/containers/Views/TreeView/Value.tsx @@ -0,0 +1,41 @@ +import React from "react"; +import { DefaultTheme, useTheme } from "styled-components"; +import { TextRenderer } from "src/containers/Views/GraphView/CustomNode/TextRenderer"; + +type TextColorFn = { + theme: DefaultTheme; + $value?: string | unknown; +}; + +function getValueColor({ $value, theme }: TextColorFn) { + if ($value && !Number.isNaN(+$value)) return theme.NODE_COLORS.INTEGER; + if ($value === "true") return theme.NODE_COLORS.BOOL.TRUE; + if ($value === "false") return theme.NODE_COLORS.BOOL.FALSE; + if ($value === "null") return theme.NODE_COLORS.NULL; + + // default + return theme.NODE_COLORS.NODE_VALUE; +} + +interface ValueProps { + valueAsString: unknown; + value: unknown; +} + +export const Value = (props: ValueProps) => { + const theme = useTheme(); + const { valueAsString, value } = props; + + return ( + + {JSON.stringify(value)} + + ); +}; diff --git a/src/containers/Views/TreeView/index.tsx b/src/containers/Views/TreeView/index.tsx new file mode 100644 index 00000000000..59993a96f19 --- /dev/null +++ b/src/containers/Views/TreeView/index.tsx @@ -0,0 +1,60 @@ +import React from "react"; +import { useTheme } from "styled-components"; +import { JSONTree } from "react-json-tree"; +import useJson from "src/store/useJson"; +import { EditModal } from "./EditModal"; +import { Label } from "./Label"; +import { Value } from "./Value"; + +export const TreeView = () => { + const theme = useTheme(); + const json = useJson(state => state.json); + const [opened, setOpened] = React.useState(false); + const [selectedValue, setSelectedValue] = React.useState(null); + const [path, setPath] = React.useState<(string | number)[]>([]); + const [value, setValue] = React.useState(""); + const [errorMessage, setErrorMessage] = React.useState(null); + + return ( + <> + } + labelRenderer={(keyPath, nodeType) => ( +