mirror of
https://github.com/AykutSarac/jsoncrack.com.git
synced 2025-01-12 19:02:53 +08:00
Merge branch 'main' into mona-sans
This commit is contained in:
commit
5da20fa5be
@ -65,16 +65,16 @@ const TextNode: React.FC<CustomNodeProps> = ({
|
|||||||
x={0}
|
x={0}
|
||||||
y={0}
|
y={0}
|
||||||
hideCollapse={hideCollapse}
|
hideCollapse={hideCollapse}
|
||||||
hasCollapse={data.isParent && hasCollapse}
|
hasCollapse={data.parent && hasCollapse}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
>
|
>
|
||||||
<StyledTextNodeWrapper hasCollapse={data.isParent && !hideCollapse}>
|
<StyledTextNodeWrapper hasCollapse={data.parent && !hideCollapse}>
|
||||||
{(!performanceMode || inViewport) && (
|
{(!performanceMode || inViewport) && (
|
||||||
<Styled.StyledKey
|
<Styled.StyledKey
|
||||||
data-x={x}
|
data-x={x}
|
||||||
data-y={y}
|
data-y={y}
|
||||||
data-key={JSON.stringify(text)}
|
data-key={JSON.stringify(text)}
|
||||||
parent={data.isParent}
|
parent={data.parent}
|
||||||
>
|
>
|
||||||
<Styled.StyledLinkItUrl>
|
<Styled.StyledLinkItUrl>
|
||||||
{JSON.stringify(text).replaceAll('"', "")}
|
{JSON.stringify(text).replaceAll('"', "")}
|
||||||
@ -82,13 +82,13 @@ const TextNode: React.FC<CustomNodeProps> = ({
|
|||||||
</Styled.StyledKey>
|
</Styled.StyledKey>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{data.isParent && data.childrenCount > 0 && !hideChildrenCount && (
|
{data.parent && data.childrenCount > 0 && !hideChildrenCount && (
|
||||||
<Styled.StyledChildrenCount>
|
<Styled.StyledChildrenCount>
|
||||||
({data.childrenCount})
|
({data.childrenCount})
|
||||||
</Styled.StyledChildrenCount>
|
</Styled.StyledChildrenCount>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{inViewport && data.isParent && hasCollapse && !hideCollapse && (
|
{inViewport && data.parent && hasCollapse && !hideCollapse && (
|
||||||
<StyledExpand onClick={handleExpand}>
|
<StyledExpand onClick={handleExpand}>
|
||||||
{isExpanded ? <MdLinkOff size={18} /> : <MdLink size={18} />}
|
{isExpanded ? <MdLinkOff size={18} /> : <MdLink size={18} />}
|
||||||
</StyledExpand>
|
</StyledExpand>
|
||||||
|
@ -2,9 +2,11 @@ import { LinkItUrl } from "react-linkify-it";
|
|||||||
import styled, { DefaultTheme } from "styled-components";
|
import styled, { DefaultTheme } from "styled-components";
|
||||||
|
|
||||||
function getTypeColor(value: string, theme: DefaultTheme) {
|
function getTypeColor(value: string, theme: DefaultTheme) {
|
||||||
if (!Number.isNaN(+value)) return "#FD0079";
|
if (!Number.isNaN(+value)) return theme.NODE_COLORS.INTEGER;
|
||||||
if (value === "true") return theme.TEXT_POSITIVE;
|
if (value === "true") return theme.NODE_COLORS.BOOL.TRUE;
|
||||||
if (value === "false") return theme.TEXT_DANGER;
|
if (value === "false") return theme.NODE_COLORS.BOOL.FALSE;
|
||||||
|
if (value === "null") return theme.NODE_COLORS.NULL;
|
||||||
|
return theme.NODE_COLORS.NODE_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StyledLinkItUrl = styled(LinkItUrl)`
|
export const StyledLinkItUrl = styled(LinkItUrl)`
|
||||||
@ -20,7 +22,7 @@ export const StyledForeignObject = styled.foreignObject<{
|
|||||||
text-align: ${({ isObject }) => !isObject && "center"};
|
text-align: ${({ isObject }) => !isObject && "center"};
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
color: ${({ theme }) => theme.TEXT_NORMAL};
|
color: ${({ theme }) => theme.NODE_COLORS.TEXT};
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
padding: ${({ isObject }) => isObject && "10px"};
|
padding: ${({ isObject }) => isObject && "10px"};
|
||||||
|
|
||||||
@ -51,15 +53,22 @@ export const StyledForeignObject = styled.foreignObject<{
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function getKeyColor(theme: DefaultTheme, parent: boolean, objectKey: boolean) {
|
function getKeyColor(
|
||||||
if (parent) return theme.NODE_KEY;
|
theme: DefaultTheme,
|
||||||
if (objectKey) return theme.OBJECT_KEY;
|
parent: "array" | "object" | false,
|
||||||
return theme.TEXT_POSITIVE;
|
objectKey: boolean
|
||||||
|
) {
|
||||||
|
if (parent) {
|
||||||
|
if (parent === "array") return theme.NODE_COLORS.PARENT_ARR;
|
||||||
|
return theme.NODE_COLORS.PARENT_OBJ;
|
||||||
|
}
|
||||||
|
if (objectKey) return theme.NODE_COLORS.NODE_KEY;
|
||||||
|
return theme.NODE_COLORS.TEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StyledKey = styled.span<{
|
export const StyledKey = styled.span<{
|
||||||
objectKey?: boolean;
|
objectKey?: boolean;
|
||||||
parent?: boolean;
|
parent?: "array" | "object" | false;
|
||||||
value?: string;
|
value?: string;
|
||||||
}>`
|
}>`
|
||||||
display: inline;
|
display: inline;
|
||||||
@ -90,7 +99,7 @@ export const StyledRow = styled.span.attrs<{
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const StyledChildrenCount = styled.span`
|
export const StyledChildrenCount = styled.span`
|
||||||
color: ${({ theme }) => theme.TEXT_POSITIVE};
|
color: ${({ theme }) => theme.NODE_COLORS.CHILD_COUNT};
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
margin-left: -15px;
|
margin-left: -15px;
|
||||||
`;
|
`;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import { DefaultTheme } from "styled-components";
|
|
||||||
|
|
||||||
const fixedColors = {
|
const fixedColors = {
|
||||||
CRIMSON: "#DC143C",
|
CRIMSON: "#DC143C",
|
||||||
BLURPLE: "#5865F2",
|
BLURPLE: "#5865F2",
|
||||||
@ -18,8 +16,44 @@ const fixedColors = {
|
|||||||
TEXT_DANGER: "#db662e",
|
TEXT_DANGER: "#db662e",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const darkTheme: DefaultTheme = {
|
const nodeColors = {
|
||||||
|
dark: {
|
||||||
|
NODE_COLORS: {
|
||||||
|
TEXT: "#35D073",
|
||||||
|
NODE_KEY: "#59b8ff",
|
||||||
|
NODE_VALUE: "#DCE5E7",
|
||||||
|
INTEGER: "#e8c479",
|
||||||
|
NULL: "#939598",
|
||||||
|
BOOL: {
|
||||||
|
FALSE: "#F85C50",
|
||||||
|
TRUE: "#00DC7D",
|
||||||
|
},
|
||||||
|
PARENT_ARR: "#FC9A40",
|
||||||
|
PARENT_OBJ: "#59b8ff",
|
||||||
|
CHILD_COUNT: "white",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
light: {
|
||||||
|
NODE_COLORS: {
|
||||||
|
TEXT: "#748700",
|
||||||
|
NODE_KEY: "#761CEA",
|
||||||
|
NODE_VALUE: "#535353",
|
||||||
|
INTEGER: "#A771FE",
|
||||||
|
NULL: "#afafaf",
|
||||||
|
BOOL: {
|
||||||
|
FALSE: "#FF0000",
|
||||||
|
TRUE: "#748700",
|
||||||
|
},
|
||||||
|
PARENT_ARR: "#FF6B00",
|
||||||
|
PARENT_OBJ: "#761CEA",
|
||||||
|
CHILD_COUNT: "#535353",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const darkTheme = {
|
||||||
...fixedColors,
|
...fixedColors,
|
||||||
|
...nodeColors.dark,
|
||||||
BLACK_SECONDARY: "#23272A",
|
BLACK_SECONDARY: "#23272A",
|
||||||
SILVER_DARK: "#4D4D4D",
|
SILVER_DARK: "#4D4D4D",
|
||||||
NODE_KEY: "#FAA81A",
|
NODE_KEY: "#FAA81A",
|
||||||
@ -37,10 +71,11 @@ export const darkTheme: DefaultTheme = {
|
|||||||
MODAL_BACKGROUND: "#36393E",
|
MODAL_BACKGROUND: "#36393E",
|
||||||
TEXT_NORMAL: "#dcddde",
|
TEXT_NORMAL: "#dcddde",
|
||||||
TEXT_POSITIVE: "hsl(139,calc(var(--saturation-factor, 1)*51.6%),52.2%)",
|
TEXT_POSITIVE: "hsl(139,calc(var(--saturation-factor, 1)*51.6%),52.2%)",
|
||||||
} as const;
|
};
|
||||||
|
|
||||||
export const lightTheme: DefaultTheme = {
|
export const lightTheme = {
|
||||||
...fixedColors,
|
...fixedColors,
|
||||||
|
...nodeColors.light,
|
||||||
BLACK_SECONDARY: "#F2F2F2",
|
BLACK_SECONDARY: "#F2F2F2",
|
||||||
SILVER_DARK: "#CCCCCC",
|
SILVER_DARK: "#CCCCCC",
|
||||||
NODE_KEY: "#DC3790",
|
NODE_KEY: "#DC3790",
|
||||||
@ -58,4 +93,11 @@ export const lightTheme: DefaultTheme = {
|
|||||||
MODAL_BACKGROUND: "#FFFFFF",
|
MODAL_BACKGROUND: "#FFFFFF",
|
||||||
TEXT_NORMAL: "#2e3338",
|
TEXT_NORMAL: "#2e3338",
|
||||||
TEXT_POSITIVE: "#008736",
|
TEXT_POSITIVE: "#008736",
|
||||||
} as const;
|
};
|
||||||
|
|
||||||
|
const themeDs = {
|
||||||
|
...lightTheme,
|
||||||
|
...darkTheme,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default themeDs;
|
||||||
|
@ -62,15 +62,14 @@ const HeroSection = () => {
|
|||||||
Help JSON Crack's Goals
|
Help JSON Crack's Goals
|
||||||
<FaHeart />
|
<FaHeart />
|
||||||
</Styles.StyledSponsorButton>
|
</Styles.StyledSponsorButton>
|
||||||
<Link
|
<Styles.StyledSponsorButton
|
||||||
href="https://marketplace.visualstudio.com/items?itemName=AykutSarac.jsoncrack-vscode"
|
href="https://marketplace.visualstudio.com/items?itemName=AykutSarac.jsoncrack-vscode"
|
||||||
passHref
|
link
|
||||||
|
isBlue
|
||||||
>
|
>
|
||||||
<Styles.StyledSponsorButton isBlue>
|
|
||||||
GET IT ON VS CODE
|
GET IT ON VS CODE
|
||||||
<SiVisualstudiocode />
|
<SiVisualstudiocode />
|
||||||
</Styles.StyledSponsorButton>
|
</Styles.StyledSponsorButton>
|
||||||
</Link>
|
|
||||||
<GoalsModal visible={isModalVisible} setVisible={setModalVisible} />
|
<GoalsModal visible={isModalVisible} setVisible={setModalVisible} />
|
||||||
</Styles.StyledButtonWrapper>
|
</Styles.StyledButtonWrapper>
|
||||||
</Styles.StyledHeroSection>
|
</Styles.StyledHeroSection>
|
||||||
@ -220,6 +219,13 @@ const EmbedSection = () => (
|
|||||||
navigate and understand even complex JSON data, making it a valuable tool for
|
navigate and understand even complex JSON data, making it a valuable tool for
|
||||||
anyone working with JSON.
|
anyone working with JSON.
|
||||||
</Styles.StyledMinorTitle>
|
</Styles.StyledMinorTitle>
|
||||||
|
<Styles.StyledButton
|
||||||
|
href="https://jsoncrack.com/embed"
|
||||||
|
status="SECONDARY"
|
||||||
|
link
|
||||||
|
>
|
||||||
|
LEARN TO EMBED
|
||||||
|
</Styles.StyledButton>
|
||||||
</Styles.StyledSectionArea>
|
</Styles.StyledSectionArea>
|
||||||
<div>
|
<div>
|
||||||
<Styles.StyledIframge
|
<Styles.StyledIframge
|
||||||
@ -227,9 +233,16 @@ const EmbedSection = () => (
|
|||||||
onLoad={e => {
|
onLoad={e => {
|
||||||
const frame = e.currentTarget.contentWindow;
|
const frame = e.currentTarget.contentWindow;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
frame?.postMessage({
|
frame?.postMessage(
|
||||||
|
{
|
||||||
json: defaultJson,
|
json: defaultJson,
|
||||||
});
|
options: {
|
||||||
|
theme: "dark",
|
||||||
|
direction: "DOWN"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"*"
|
||||||
|
);
|
||||||
}, 500);
|
}, 500);
|
||||||
}}
|
}}
|
||||||
></Styles.StyledIframge>
|
></Styles.StyledIframge>
|
||||||
|
@ -3,10 +3,11 @@ import dynamic from "next/dynamic";
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
import { baseURL } from "src/constants/data";
|
import { baseURL } from "src/constants/data";
|
||||||
|
import { darkTheme, lightTheme } from "src/constants/theme";
|
||||||
import { NodeModal } from "src/containers/Modals/NodeModal";
|
import { NodeModal } from "src/containers/Modals/NodeModal";
|
||||||
import useGraph from "src/store/useGraph";
|
import useGraph from "src/store/useGraph";
|
||||||
import { parser } from "src/utils/jsonParser";
|
import { parser } from "src/utils/jsonParser";
|
||||||
import styled from "styled-components";
|
import styled, { ThemeProvider } from "styled-components";
|
||||||
|
|
||||||
const Graph = dynamic<any>(() => import("src/components/Graph").then(c => c.Graph), {
|
const Graph = dynamic<any>(() => import("src/components/Graph").then(c => c.Graph), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
@ -62,11 +63,13 @@ const WidgetPage = () => {
|
|||||||
|
|
||||||
const [isModalVisible, setModalVisible] = React.useState(false);
|
const [isModalVisible, setModalVisible] = React.useState(false);
|
||||||
const [selectedNode, setSelectedNode] = React.useState<[string, string][]>([]);
|
const [selectedNode, setSelectedNode] = React.useState<[string, string][]>([]);
|
||||||
|
const [theme, setTheme] = React.useState("dark");
|
||||||
|
|
||||||
const collapsedNodes = useGraph(state => state.collapsedNodes);
|
const collapsedNodes = useGraph(state => state.collapsedNodes);
|
||||||
const collapsedEdges = useGraph(state => state.collapsedEdges);
|
const collapsedEdges = useGraph(state => state.collapsedEdges);
|
||||||
const loading = useGraph(state => state.loading);
|
const loading = useGraph(state => state.loading);
|
||||||
const setNodeEdges = useGraph(state => state.setNodeEdges);
|
const setNodeEdges = useGraph(state => state.setNodeEdges);
|
||||||
|
const setDirection = useGraph(state => state.setDirection);
|
||||||
|
|
||||||
const openModal = React.useCallback(() => setModalVisible(true), []);
|
const openModal = React.useCallback(() => setModalVisible(true), []);
|
||||||
|
|
||||||
@ -92,8 +95,19 @@ const WidgetPage = () => {
|
|||||||
const handler = (event: EmbedMessage) => {
|
const handler = (event: EmbedMessage) => {
|
||||||
try {
|
try {
|
||||||
if (!event.data?.json) return;
|
if (!event.data?.json) return;
|
||||||
|
|
||||||
const { nodes, edges } = parser(event.data.json);
|
const { nodes, edges } = parser(event.data.json);
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
direction: "RIGHT",
|
||||||
|
theme,
|
||||||
|
...event.data.options,
|
||||||
|
};
|
||||||
|
|
||||||
|
setDirection(options.direction);
|
||||||
|
if (options.theme === "light" || options.theme === "dark")
|
||||||
|
setTheme(options.theme);
|
||||||
|
|
||||||
setNodeEdges(nodes, edges);
|
setNodeEdges(nodes, edges);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@ -103,7 +117,7 @@ const WidgetPage = () => {
|
|||||||
|
|
||||||
window.addEventListener("message", handler);
|
window.addEventListener("message", handler);
|
||||||
return () => window.removeEventListener("message", handler);
|
return () => window.removeEventListener("message", handler);
|
||||||
}, [setNodeEdges]);
|
}, [setDirection, setNodeEdges, theme]);
|
||||||
|
|
||||||
if (query.json)
|
if (query.json)
|
||||||
return (
|
return (
|
||||||
@ -117,7 +131,7 @@ const WidgetPage = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<ThemeProvider theme={theme === "dark" ? darkTheme : lightTheme}>
|
||||||
<Graph openModal={openModal} setSelectedNode={setSelectedNode} isWidget />
|
<Graph openModal={openModal} setSelectedNode={setSelectedNode} isWidget />
|
||||||
<NodeModal
|
<NodeModal
|
||||||
selectedNode={selectedNode}
|
selectedNode={selectedNode}
|
||||||
@ -127,7 +141,7 @@ const WidgetPage = () => {
|
|||||||
<StyledAttribute href={`${baseURL}/editor`} target="_blank" rel="noreferrer">
|
<StyledAttribute href={`${baseURL}/editor`} target="_blank" rel="noreferrer">
|
||||||
jsoncrack.com
|
jsoncrack.com
|
||||||
</StyledAttribute>
|
</StyledAttribute>
|
||||||
</>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
38
src/typings/styled.d.ts
vendored
38
src/typings/styled.d.ts
vendored
@ -1,38 +1,8 @@
|
|||||||
|
import theme from "src/constants/theme";
|
||||||
import "styled-components";
|
import "styled-components";
|
||||||
|
|
||||||
declare module "styled-components" {
|
type CustomTheme = typeof theme;
|
||||||
export interface DefaultTheme {
|
|
||||||
BLURPLE: string;
|
|
||||||
FULL_WHITE: string;
|
|
||||||
BLACK: string;
|
|
||||||
BLACK_LIGHT: string;
|
|
||||||
BLACK_DARK: string;
|
|
||||||
BLACK_PRIMARY: string;
|
|
||||||
BLACK_SECONDARY: string;
|
|
||||||
CRIMSON: string;
|
|
||||||
DARK_SALMON: string;
|
|
||||||
DANGER: string;
|
|
||||||
LIGHTGREEN: string;
|
|
||||||
SEAGREEN: string;
|
|
||||||
ORANGE: string;
|
|
||||||
SILVER: string;
|
|
||||||
SILVER_DARK: string;
|
|
||||||
PRIMARY: string;
|
|
||||||
NODE_KEY: string;
|
|
||||||
OBJECT_KEY: string;
|
|
||||||
SIDEBAR_ICONS: string;
|
|
||||||
|
|
||||||
INTERACTIVE_NORMAL: string;
|
declare module "styled-components" {
|
||||||
INTERACTIVE_HOVER: string;
|
export interface DefaultTheme extends CustomTheme {}
|
||||||
INTERACTIVE_ACTIVE: string;
|
|
||||||
BACKGROUND_NODE: string;
|
|
||||||
BACKGROUND_TERTIARY: string;
|
|
||||||
BACKGROUND_SECONDARY: string;
|
|
||||||
BACKGROUND_PRIMARY: string;
|
|
||||||
BACKGROUND_MODIFIER_ACCENT: string;
|
|
||||||
MODAL_BACKGROUND: string;
|
|
||||||
TEXT_NORMAL: string;
|
|
||||||
TEXT_POSITIVE: string;
|
|
||||||
TEXT_DANGER: string;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,11 @@ export const getOutgoers = (
|
|||||||
const outgoerNodes: NodeData[] = [];
|
const outgoerNodes: NodeData[] = [];
|
||||||
const matchingNodes: string[] = [];
|
const matchingNodes: string[] = [];
|
||||||
|
|
||||||
|
if (parent.includes(nodeId)) {
|
||||||
|
const initialParentNode = nodes.find(n => n.id === nodeId);
|
||||||
|
if (initialParentNode) outgoerNodes.push(initialParentNode);
|
||||||
|
}
|
||||||
|
|
||||||
const runner = (nodeId: string) => {
|
const runner = (nodeId: string) => {
|
||||||
const outgoerIds = edges.filter(e => e.from === nodeId).map(e => e.to);
|
const outgoerIds = edges.filter(e => e.from === nodeId).map(e => e.to);
|
||||||
const nodeList = nodes.filter(n => {
|
const nodeList = nodes.filter(n => {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { parse } from "jsonc-parser";
|
import { Node, parseTree } from "jsonc-parser";
|
||||||
|
|
||||||
const calculateSize = (
|
const calculateSize = (
|
||||||
text: string | [string, string][],
|
text: string | [string, string][],
|
||||||
@ -32,114 +32,304 @@ const calculateSize = (
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const filterChild = ([_, v]) => {
|
export const parser = (jsonStr: string, isFolded = false) => {
|
||||||
const isNull = v === null;
|
try {
|
||||||
const isArray = Array.isArray(v) && v.length;
|
let json = parseTree(jsonStr);
|
||||||
const isObject = v instanceof Object;
|
let nodes: NodeData[] = [];
|
||||||
|
let edges: EdgeData[] = [];
|
||||||
|
|
||||||
return !isNull && (isArray || isObject);
|
const addNodes = (
|
||||||
};
|
text: any,
|
||||||
|
width: number,
|
||||||
const filterValues = ([k, v]) => {
|
height: number,
|
||||||
if (Array.isArray(v) || v instanceof Object) return false;
|
parent: "string" | "number" | "boolean" | "object" | "array" | "null" | false,
|
||||||
return true;
|
isEmpty?: boolean
|
||||||
};
|
) => {
|
||||||
|
let actualId = String(nodes.length + 1);
|
||||||
function generateChildren(object: Object, isFolded = false, nextId: () => string) {
|
nodes = [
|
||||||
if (!(object instanceof Object)) object = [object];
|
...nodes,
|
||||||
|
|
||||||
return Object.entries(object)
|
|
||||||
.filter(filterChild)
|
|
||||||
.flatMap(([key, v]) => {
|
|
||||||
const { width, height } = calculateSize(key, true, isFolded);
|
|
||||||
const children = extract(v, isFolded, nextId);
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
{
|
||||||
id: nextId(),
|
id: actualId,
|
||||||
text: key,
|
text: text,
|
||||||
children,
|
width: width,
|
||||||
width,
|
height: height,
|
||||||
height,
|
|
||||||
data: {
|
data: {
|
||||||
isParent: true,
|
parent: parent === "array" || parent === "object" ? parent : false,
|
||||||
childrenCount: children.length,
|
childrenCount: parent ? 1 : 0,
|
||||||
|
isEmpty: isEmpty,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
});
|
return actualId;
|
||||||
}
|
|
||||||
|
|
||||||
function generateNodeData(object: Object) {
|
|
||||||
if (object instanceof Object) {
|
|
||||||
const entries = Object.entries(object).filter(filterValues);
|
|
||||||
return entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
return String(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
const extract = (
|
|
||||||
os: string[] | object[] | null,
|
|
||||||
isFolded = false,
|
|
||||||
nextId = (
|
|
||||||
id => () =>
|
|
||||||
String(++id)
|
|
||||||
)(0)
|
|
||||||
) => {
|
|
||||||
if (!os) return [];
|
|
||||||
|
|
||||||
return [os].flat().map(o => {
|
|
||||||
const text = generateNodeData(o);
|
|
||||||
const { width, height } = calculateSize(text, false, isFolded);
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: nextId(),
|
|
||||||
text,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
children: generateChildren(o, isFolded, nextId),
|
|
||||||
data: {
|
|
||||||
isParent: false,
|
|
||||||
childrenCount: 0,
|
|
||||||
isEmpty: !text.length,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const flatten = (xs: { id: string; children: never[] }[]) =>
|
const addEdges = (from: string, to: string) => {
|
||||||
xs.flatMap(({ children, ...rest }) => [rest, ...flatten(children)]);
|
edges = [
|
||||||
|
...edges,
|
||||||
const relationships = (xs: { id: string; children: never[] }[]) => {
|
{
|
||||||
return xs.flatMap(({ id: from, children = [] }) => [
|
|
||||||
...children.map(({ id: to }) => ({
|
|
||||||
id: `e${from}-${to}`,
|
id: `e${from}-${to}`,
|
||||||
from,
|
from: from,
|
||||||
to,
|
to: to,
|
||||||
})),
|
},
|
||||||
...relationships(children),
|
];
|
||||||
]);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const parser = (jsonStr: string, isFolded = false) => {
|
let parentName: string = "";
|
||||||
try {
|
let bracketOpen: { id: string; type: string }[] = [];
|
||||||
let json = parse(jsonStr);
|
let objectsFromArray: number[] = [];
|
||||||
if (!Array.isArray(json)) json = [json];
|
let objectsFromArrayId = 0;
|
||||||
const nodes: NodeData[] = [];
|
let notHaveParent: string[] = [];
|
||||||
const edges: EdgeData[] = [];
|
let brothersNode: [string, string][] = [];
|
||||||
|
let brothersParentId: string | undefined = "";
|
||||||
|
let brotherKey: string = "";
|
||||||
|
let brothersNodeProps: {
|
||||||
|
id: string;
|
||||||
|
parentId: string | undefined;
|
||||||
|
objectsFromArrayId: number | undefined;
|
||||||
|
}[] = [];
|
||||||
|
|
||||||
const mappedElements = extract(json, isFolded);
|
const traverse = (
|
||||||
const res = [...flatten(mappedElements), ...relationships(mappedElements)];
|
objectToTraverse: Node,
|
||||||
|
parentType?: string,
|
||||||
|
myParentId?: string,
|
||||||
|
nextType?: string
|
||||||
|
) => {
|
||||||
|
let { type, children, value } = objectToTraverse;
|
||||||
|
|
||||||
res.forEach(data => {
|
if (!children) {
|
||||||
if (isNode(data)) {
|
if (value !== undefined) {
|
||||||
nodes.push(data);
|
if (
|
||||||
|
parentType === "property" &&
|
||||||
|
nextType !== "object" &&
|
||||||
|
nextType !== "array"
|
||||||
|
) {
|
||||||
|
brothersParentId = myParentId;
|
||||||
|
if (nextType === undefined) {
|
||||||
|
// add key and value to brothers node
|
||||||
|
brothersNode = [...brothersNode, [brotherKey, value]];
|
||||||
} else {
|
} else {
|
||||||
edges.push(data);
|
brotherKey = value;
|
||||||
|
}
|
||||||
|
} else if (parentType === "array") {
|
||||||
|
const { width, height } = calculateSize(String(value), false, isFolded);
|
||||||
|
const nodeFromArrayId = addNodes(String(value), width, height, false);
|
||||||
|
if (myParentId) {
|
||||||
|
addEdges(myParentId, nodeFromArrayId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nextType && parentType !== "array") {
|
||||||
|
if (nextType === "object" || nextType === "array") {
|
||||||
|
parentName = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (children) {
|
||||||
|
let parentId: string | undefined;
|
||||||
|
|
||||||
|
if (type !== "property" && parentName !== "") {
|
||||||
|
// add last brothers node and add parent node
|
||||||
|
|
||||||
|
if (brothersNode.length > 0) {
|
||||||
|
// add or concat brothers node of same parent
|
||||||
|
let findBrothersNode = brothersNodeProps.find(
|
||||||
|
e =>
|
||||||
|
e.parentId === brothersParentId &&
|
||||||
|
e.objectsFromArrayId ===
|
||||||
|
objectsFromArray[objectsFromArray.length - 1]
|
||||||
|
);
|
||||||
|
if (findBrothersNode) {
|
||||||
|
let ModifyNodes = [...nodes];
|
||||||
|
let findNode = nodes.findIndex(e => e.id === findBrothersNode?.id);
|
||||||
|
|
||||||
|
if (ModifyNodes[findNode]) {
|
||||||
|
ModifyNodes[findNode].text =
|
||||||
|
ModifyNodes[findNode].text.concat(brothersNode);
|
||||||
|
const { width, height } = calculateSize(
|
||||||
|
ModifyNodes[findNode].text,
|
||||||
|
false,
|
||||||
|
isFolded
|
||||||
|
);
|
||||||
|
ModifyNodes[findNode].width = width;
|
||||||
|
ModifyNodes[findNode].height = height;
|
||||||
|
nodes = [...ModifyNodes];
|
||||||
|
brothersNode = [];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const { width, height } = calculateSize(brothersNode, false, isFolded);
|
||||||
|
const brothersNodeId = addNodes(brothersNode, width, height, false);
|
||||||
|
brothersNode = [];
|
||||||
|
|
||||||
|
if (brothersParentId) {
|
||||||
|
addEdges(brothersParentId, brothersNodeId);
|
||||||
|
} else {
|
||||||
|
notHaveParent = [...notHaveParent, brothersNodeId];
|
||||||
|
}
|
||||||
|
|
||||||
|
brothersNodeProps = [
|
||||||
|
...brothersNodeProps,
|
||||||
|
{
|
||||||
|
id: brothersNodeId,
|
||||||
|
parentId: brothersParentId,
|
||||||
|
objectsFromArrayId: objectsFromArray[objectsFromArray.length - 1],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add parent node
|
||||||
|
const { width, height } = calculateSize(parentName, true, isFolded);
|
||||||
|
parentId = addNodes(parentName, width, height, type);
|
||||||
|
bracketOpen = [...bracketOpen, { id: parentId, type: type }];
|
||||||
|
parentName = "";
|
||||||
|
|
||||||
|
// add edges from parent node
|
||||||
|
let brothersProps = brothersNodeProps.filter(
|
||||||
|
e =>
|
||||||
|
e.parentId === myParentId &&
|
||||||
|
e.objectsFromArrayId === objectsFromArray[objectsFromArray.length - 1]
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
(brothersProps.length > 0 &&
|
||||||
|
bracketOpen[bracketOpen.length - 2] &&
|
||||||
|
bracketOpen[bracketOpen.length - 2].type !== "object") ||
|
||||||
|
(brothersProps.length > 0 && bracketOpen.length === 1)
|
||||||
|
) {
|
||||||
|
addEdges(brothersProps[brothersProps.length - 1].id, parentId);
|
||||||
|
} else if (myParentId) {
|
||||||
|
addEdges(myParentId, parentId);
|
||||||
|
} else {
|
||||||
|
notHaveParent = [...notHaveParent, parentId];
|
||||||
|
}
|
||||||
|
} else if (parentType === "array") {
|
||||||
|
objectsFromArray = [...objectsFromArray, objectsFromArrayId++];
|
||||||
|
}
|
||||||
|
children.forEach((branch, index, array) => {
|
||||||
|
if (array[index + 1]) {
|
||||||
|
traverse(
|
||||||
|
branch,
|
||||||
|
type,
|
||||||
|
bracketOpen[bracketOpen.length - 1]
|
||||||
|
? bracketOpen[bracketOpen.length - 1].id
|
||||||
|
: undefined,
|
||||||
|
array[index + 1].type
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
traverse(
|
||||||
|
branch,
|
||||||
|
type,
|
||||||
|
bracketOpen[bracketOpen.length - 1]
|
||||||
|
? bracketOpen[bracketOpen.length - 1].id
|
||||||
|
: undefined
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (type !== "property") {
|
||||||
|
// when children end
|
||||||
|
|
||||||
|
// add or concat brothers node when it is the last parent node
|
||||||
|
if (brothersNode.length > 0) {
|
||||||
|
let findBrothersNode = brothersNodeProps.find(
|
||||||
|
e =>
|
||||||
|
e.parentId === brothersParentId &&
|
||||||
|
e.objectsFromArrayId ===
|
||||||
|
objectsFromArray[objectsFromArray.length - 1]
|
||||||
|
);
|
||||||
|
if (findBrothersNode) {
|
||||||
|
let ModifyNodes = [...nodes];
|
||||||
|
let findNode = nodes.findIndex(e => e.id === findBrothersNode?.id);
|
||||||
|
|
||||||
|
if (ModifyNodes[findNode]) {
|
||||||
|
ModifyNodes[findNode].text =
|
||||||
|
ModifyNodes[findNode].text.concat(brothersNode);
|
||||||
|
const { width, height } = calculateSize(
|
||||||
|
ModifyNodes[findNode].text,
|
||||||
|
false,
|
||||||
|
isFolded
|
||||||
|
);
|
||||||
|
ModifyNodes[findNode].width = width;
|
||||||
|
ModifyNodes[findNode].height = height;
|
||||||
|
nodes = [...ModifyNodes];
|
||||||
|
brothersNode = [];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const { width, height } = calculateSize(brothersNode, false, isFolded);
|
||||||
|
const brothersNodeId = addNodes(brothersNode, width, height, false);
|
||||||
|
brothersNode = [];
|
||||||
|
|
||||||
|
if (brothersParentId) {
|
||||||
|
addEdges(brothersParentId, brothersNodeId);
|
||||||
|
} else {
|
||||||
|
notHaveParent = [...notHaveParent, brothersNodeId];
|
||||||
|
}
|
||||||
|
|
||||||
|
brothersNodeProps = [
|
||||||
|
...brothersNodeProps,
|
||||||
|
{
|
||||||
|
id: brothersNodeId,
|
||||||
|
parentId: brothersParentId,
|
||||||
|
objectsFromArrayId: objectsFromArray[objectsFromArray.length - 1],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// close brackets
|
||||||
|
if (parentType !== "array") {
|
||||||
|
if (bracketOpen.length > 0) {
|
||||||
|
let newBracketOpen = [...bracketOpen];
|
||||||
|
newBracketOpen.splice(newBracketOpen.length - 1);
|
||||||
|
bracketOpen = [...newBracketOpen];
|
||||||
|
}
|
||||||
|
} else if (parentType === "array") {
|
||||||
|
if (objectsFromArray.length > 0) {
|
||||||
|
let newobjectsFromArray = [...objectsFromArray];
|
||||||
|
newobjectsFromArray.splice(newobjectsFromArray.length - 1);
|
||||||
|
objectsFromArray = [...newobjectsFromArray];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentId) {
|
||||||
|
let myChildrens = edges.filter(e => e.from === parentId);
|
||||||
|
let myIndex = nodes.findIndex(e => e.id === parentId);
|
||||||
|
|
||||||
|
let ModifyNodes = [...nodes];
|
||||||
|
if (ModifyNodes[myIndex]) {
|
||||||
|
ModifyNodes[myIndex].data.childrenCount = myChildrens.length;
|
||||||
|
nodes = [...ModifyNodes];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (json) {
|
||||||
|
traverse(json);
|
||||||
|
|
||||||
|
if (notHaveParent.length > 1) {
|
||||||
|
if (json.type !== "array") {
|
||||||
|
const text = "";
|
||||||
|
const { width, height } = calculateSize(text, false, isFolded);
|
||||||
|
const emptyId = addNodes(text, width, height, false, true);
|
||||||
|
notHaveParent.forEach(children => {
|
||||||
|
addEdges(emptyId, children);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodes.length === 0) {
|
||||||
|
if (json.type === "array") {
|
||||||
|
const text = "[]";
|
||||||
|
const { width, height } = calculateSize(text, false, isFolded);
|
||||||
|
addNodes(text, width, height, false);
|
||||||
|
} else {
|
||||||
|
const text = "{}";
|
||||||
|
const { width, height } = calculateSize(text, false, isFolded);
|
||||||
|
addNodes(text, width, height, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return { nodes, edges };
|
return { nodes, edges };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@ -149,8 +339,3 @@ export const parser = (jsonStr: string, isFolded = false) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function isNode(element: NodeData | EdgeData) {
|
|
||||||
if ("text" in element) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user