add option to expand & collapse

This commit is contained in:
Aykut Saraç 2022-02-14 14:36:16 +03:00
parent da5ae26949
commit 4b53b08868
5 changed files with 71 additions and 56 deletions

View File

@ -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>

View File

@ -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]}

View File

@ -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 {

View File

@ -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>
)} )}

View File

@ -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;
} }