mirror of
https://github.com/AykutSarac/jsoncrack.com.git
synced 2025-01-20 05:12:54 +08:00
add option to expand & collapse
This commit is contained in:
parent
da5ae26949
commit
4b53b08868
@ -3,7 +3,7 @@ import Link from "next/link";
|
|||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { useLocalStorage } from "usehooks-ts";
|
import { useLocalStorage } from "usehooks-ts";
|
||||||
import { FaFileImport, FaMap } from "react-icons/fa";
|
import { FaFileImport, FaMap } from "react-icons/fa";
|
||||||
import { MdAutoGraph } from "react-icons/md";
|
import { MdAutoGraph, MdFormatLineSpacing } from "react-icons/md";
|
||||||
import {
|
import {
|
||||||
AiFillHome,
|
AiFillHome,
|
||||||
AiOutlineClear,
|
AiOutlineClear,
|
||||||
@ -96,7 +96,7 @@ export const Sidebar: React.FC<{
|
|||||||
const [jsonFile, setJsonFile] = React.useState<File | null>(null);
|
const [jsonFile, setJsonFile] = React.useState<File | null>(null);
|
||||||
const [config, setConfig] = useLocalStorage<StorageConfig>("config", {
|
const [config, setConfig] = useLocalStorage<StorageConfig>("config", {
|
||||||
layout: "LEFT",
|
layout: "LEFT",
|
||||||
minimap: true,
|
expand: true,
|
||||||
controls: true,
|
controls: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ export const Sidebar: React.FC<{
|
|||||||
if (e.target.files) setJsonFile(e.target.files?.item(0));
|
if (e.target.files) setJsonFile(e.target.files?.item(0));
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggle = (setting: "minimap" | "controls") => {
|
const toggle = (setting: "expand" | "controls") => {
|
||||||
setConfig((c) => ({
|
setConfig((c) => ({
|
||||||
...c,
|
...c,
|
||||||
[setting]: !c[setting],
|
[setting]: !c[setting],
|
||||||
@ -125,16 +125,14 @@ export const Sidebar: React.FC<{
|
|||||||
return (
|
return (
|
||||||
<StyledSidebar>
|
<StyledSidebar>
|
||||||
<StyledTopWrapper>
|
<StyledTopWrapper>
|
||||||
<StyledElement>
|
<Link href="/">
|
||||||
<Link href="/">
|
<StyledElement as="a">
|
||||||
<a>
|
<StyledLogo>
|
||||||
<StyledLogo>
|
<StyledText>J</StyledText>
|
||||||
<StyledText>J</StyledText>
|
<StyledText secondary>V</StyledText>
|
||||||
<StyledText secondary>V</StyledText>
|
</StyledLogo>
|
||||||
</StyledLogo>
|
</StyledElement>
|
||||||
</a>
|
</Link>
|
||||||
</Link>
|
|
||||||
</StyledElement>
|
|
||||||
<StyledElement title="Home">
|
<StyledElement title="Home">
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<a>
|
<a>
|
||||||
@ -164,14 +162,6 @@ export const Sidebar: React.FC<{
|
|||||||
>
|
>
|
||||||
<MdAutoGraph />
|
<MdAutoGraph />
|
||||||
</StyledElement>
|
</StyledElement>
|
||||||
<StyledElement
|
|
||||||
title="Toggle Minimap"
|
|
||||||
as="a"
|
|
||||||
onClick={() => toggle("minimap")}
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
<FaMap />
|
|
||||||
</StyledElement>
|
|
||||||
<StyledElement
|
<StyledElement
|
||||||
title="Toggle Controls"
|
title="Toggle Controls"
|
||||||
as="a"
|
as="a"
|
||||||
@ -179,18 +169,24 @@ export const Sidebar: React.FC<{
|
|||||||
>
|
>
|
||||||
<AiFillControl />
|
<AiFillControl />
|
||||||
</StyledElement>
|
</StyledElement>
|
||||||
<StyledElement title="Import JSON File">
|
|
||||||
<a>
|
<StyledElement
|
||||||
<StyledImportFile>
|
as="a"
|
||||||
<input
|
title="Toggle Expand/Collapse"
|
||||||
key={jsonFile?.name}
|
onClick={() => toggle("expand")}
|
||||||
onChange={handleFileChange}
|
>
|
||||||
type="file"
|
<MdFormatLineSpacing />
|
||||||
accept="application/JSON"
|
</StyledElement>
|
||||||
/>
|
<StyledElement as="a" title="Import JSON File">
|
||||||
<FaFileImport />
|
<StyledImportFile>
|
||||||
</StyledImportFile>
|
<input
|
||||||
</a>
|
key={jsonFile?.name}
|
||||||
|
onChange={handleFileChange}
|
||||||
|
type="file"
|
||||||
|
accept="application/JSON"
|
||||||
|
/>
|
||||||
|
<FaFileImport />
|
||||||
|
</StyledImportFile>
|
||||||
</StyledElement>
|
</StyledElement>
|
||||||
</StyledTopWrapper>
|
</StyledTopWrapper>
|
||||||
<StyledBottomWrapper>
|
<StyledBottomWrapper>
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
import React, { memo } from "react";
|
import React from "react";
|
||||||
import { Label, Node, Port, NodeChildProps, NodeProps } from "reaflow";
|
import { Label, Node, Port, NodeProps } from "reaflow";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
const StyledNode = styled(Node)`
|
const StyledNode = styled(Node)``;
|
||||||
stroke: black;
|
|
||||||
stroke-width: 1;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledTextWrapper = styled.div`
|
const StyledTextWrapper = styled.div`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -15,9 +12,18 @@ const StyledTextWrapper = styled.div`
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
stroke: white !important;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledText = styled.pre<{ width: number; height: number }>`
|
const StyledText = styled.pre<{ width: number; height: number }>`
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
width: ${({ width }) => width};
|
width: ${({ width }) => width};
|
||||||
height: ${({ height }) => height};
|
height: ${({ height }) => height};
|
||||||
color: ${({ theme }) => theme.SILVER};
|
color: ${({ theme }) => theme.SILVER};
|
||||||
@ -33,7 +39,10 @@ const StyledForeignObject = styled.foreignObject<{
|
|||||||
height: ${({ height }) => height + "px"};
|
height: ${({ height }) => height + "px"};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledKey = styled.span<{ bond?: boolean; arrayValue?: boolean }>`
|
const StyledKey = styled.span<{
|
||||||
|
bond?: boolean;
|
||||||
|
arrayValue?: boolean;
|
||||||
|
}>`
|
||||||
color: ${({ theme, bond, arrayValue }) =>
|
color: ${({ theme, bond, arrayValue }) =>
|
||||||
bond ? theme.SEAGREEN : arrayValue ? theme.ORANGE : theme.BLURPLE};
|
bond ? theme.SEAGREEN : arrayValue ? theme.ORANGE : theme.BLURPLE};
|
||||||
`;
|
`;
|
||||||
@ -64,13 +73,13 @@ const CustomNode = ({ nodeProps }) => {
|
|||||||
const entries = Object.entries(data.text);
|
const entries = Object.entries(data.text);
|
||||||
|
|
||||||
if (Object.keys(data.text).every((val) => !isNaN(+val))) {
|
if (Object.keys(data.text).every((val) => !isNaN(+val))) {
|
||||||
|
const text = Object.values(data.text).join("");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledForeignObject width={width} height={height} x={0} y={0}>
|
<StyledForeignObject width={width} height={height} x={0} y={0}>
|
||||||
<StyledTextWrapper>
|
<StyledTextWrapper>
|
||||||
<StyledText width={width} height={height}>
|
<StyledText width={width} height={height}>
|
||||||
<StyledKey arrayValue>
|
<StyledKey arrayValue>{text}</StyledKey>
|
||||||
{Object.values(data.text).join("")}
|
|
||||||
</StyledKey>
|
|
||||||
</StyledText>
|
</StyledText>
|
||||||
</StyledTextWrapper>
|
</StyledTextWrapper>
|
||||||
</StyledForeignObject>
|
</StyledForeignObject>
|
||||||
@ -84,7 +93,14 @@ const CustomNode = ({ nodeProps }) => {
|
|||||||
{entries.map((val) => (
|
{entries.map((val) => (
|
||||||
<div
|
<div
|
||||||
key={nodeProps.id}
|
key={nodeProps.id}
|
||||||
style={{ height: "fit-content" }}
|
style={{
|
||||||
|
height: "fit-content",
|
||||||
|
overflow: "hidden",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
padding: '0 auto',
|
||||||
|
width: width - 20
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<StyledKey>{val[0]}: </StyledKey>
|
<StyledKey>{val[0]}: </StyledKey>
|
||||||
{val[1]}
|
{val[1]}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { CanvasDirection, NodeData, EdgeData } from "reaflow";
|
import { CanvasDirection, NodeData, EdgeData } from "reaflow";
|
||||||
import { parser } from "src/utils/json-editor-parser";
|
import { parser } from "src/utils/json-editor-parser";
|
||||||
|
|
||||||
export function getEdgeNodes(graph: any): any {
|
export function getEdgeNodes(graph: any, isExpanded: boolean = true): any {
|
||||||
graph = JSON.parse(graph);
|
graph = JSON.parse(graph);
|
||||||
const elements = parser(graph);
|
const elements = parser(graph);
|
||||||
|
|
||||||
@ -22,8 +22,8 @@ export function getEdgeNodes(graph: any): any {
|
|||||||
nodes.push({
|
nodes.push({
|
||||||
id: el.id,
|
id: el.id,
|
||||||
text: el.text,
|
text: el.text,
|
||||||
width: 35 + longestLine * 8,
|
width: isExpanded ? (35 + longestLine * 8) : 180,
|
||||||
height: 30 + lines.length * 10,
|
height: isExpanded ? (30 + lines.length * 10) : 50,
|
||||||
data: { type: "special" },
|
data: { type: "special" },
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -9,7 +9,6 @@ import { useLocalStorage } from "usehooks-ts";
|
|||||||
import { Canvas, CanvasRef } from "reaflow";
|
import { Canvas, CanvasRef } from "reaflow";
|
||||||
|
|
||||||
import { StorageConfig } from "src/typings/global";
|
import { StorageConfig } from "src/typings/global";
|
||||||
import { defaultValue } from "../JsonEditor";
|
|
||||||
import { getEdgeNodes } from "./helpers";
|
import { getEdgeNodes } from "./helpers";
|
||||||
import { NodeWrapper } from "./CustomNode";
|
import { NodeWrapper } from "./CustomNode";
|
||||||
import { Button } from "src/components/Button";
|
import { Button } from "src/components/Button";
|
||||||
@ -37,7 +36,11 @@ const StyledControls = styled.div`
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
bottom: 8px;
|
bottom: 8px;
|
||||||
right: 8px;
|
right: 8px;
|
||||||
opacity: 0.8;
|
opacity: 0.9;
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const LiveEditor: React.FC<{
|
export const LiveEditor: React.FC<{
|
||||||
@ -48,7 +51,7 @@ export const LiveEditor: React.FC<{
|
|||||||
const wrapperRef = React.useRef<ReactZoomPanPinchRef | null>(null);
|
const wrapperRef = React.useRef<ReactZoomPanPinchRef | null>(null);
|
||||||
const [config] = useLocalStorage<StorageConfig>("config", {
|
const [config] = useLocalStorage<StorageConfig>("config", {
|
||||||
layout: "LEFT",
|
layout: "LEFT",
|
||||||
minimap: true,
|
expand: true,
|
||||||
controls: true,
|
controls: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -56,7 +59,7 @@ export const LiveEditor: React.FC<{
|
|||||||
if (wrapperRef.current) wrapperRef.current?.resetTransform();
|
if (wrapperRef.current) wrapperRef.current?.resetTransform();
|
||||||
}, [json, wrapperRef]);
|
}, [json, wrapperRef]);
|
||||||
|
|
||||||
const { nodes, edges } = getEdgeNodes(json);
|
const { nodes, edges } = getEdgeNodes(json, config.expand);
|
||||||
|
|
||||||
const zoomIn = (scale: number) => {
|
const zoomIn = (scale: number) => {
|
||||||
if (
|
if (
|
||||||
@ -120,16 +123,16 @@ export const LiveEditor: React.FC<{
|
|||||||
{config.controls && (
|
{config.controls && (
|
||||||
<StyledControls>
|
<StyledControls>
|
||||||
<Button onClick={() => zoomIn(0.5)}>
|
<Button onClick={() => zoomIn(0.5)}>
|
||||||
<AiOutlineZoomIn size={20} />
|
<AiOutlineZoomIn size={24} />
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => zoomOut(0.4)}>
|
<Button onClick={() => zoomOut(0.4)}>
|
||||||
<AiOutlineZoomOut size={20} />
|
<AiOutlineZoomOut size={24} />
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => wrapperRef.current?.resetTransform()}>
|
<Button onClick={() => wrapperRef.current?.resetTransform()}>
|
||||||
<AiOutlineFullscreen size={20} />
|
<AiOutlineFullscreen size={24} />
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => localStorage.setItem("json", json)}>
|
<Button onClick={() => localStorage.setItem("json", json)}>
|
||||||
<AiFillSave size={20} />
|
<AiFillSave size={24} />
|
||||||
</Button>
|
</Button>
|
||||||
</StyledControls>
|
</StyledControls>
|
||||||
)}
|
)}
|
||||||
|
@ -2,6 +2,6 @@ import { CanvasDirection } from "reaflow";
|
|||||||
|
|
||||||
export interface StorageConfig {
|
export interface StorageConfig {
|
||||||
layout: CanvasDirection;
|
layout: CanvasDirection;
|
||||||
minimap: boolean;
|
expand: boolean;
|
||||||
controls: boolean;
|
controls: boolean;
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user