Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: UI updates #367

Merged
merged 8 commits into from
Dec 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@
]
},
"extends": ["next/core-web-vitals", "prettier"],
"plugins": ["prettier", "unused-imports"]
"plugins": ["prettier", "unused-imports"],
"ignorePatterns": ["src/enums"]
}
10 changes: 5 additions & 5 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
node-version: [18.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/

steps:
Expand All @@ -18,7 +18,7 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm install
- run: npm run lint
- run: npm run build
cache: 'yarn'
- run: yarn install
- run: yarn lint
- run: yarn build
2 changes: 1 addition & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ node_modules/
out
public
*-lock.json
tsconfig.json
tsconfig.json
6 changes: 3 additions & 3 deletions LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -631,8 +631,8 @@ to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
JSON Crack
Copyright (C) 2023 Aykut Saraç

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand All @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:

<program> Copyright (C) <year> <name of author>
JSON Crack Copyright (C) 2023 Aykut Saraç
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
Expand Down
5 changes: 5 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ const config = {
compiler: {
styledComponents: true,
},
webpack: config => {
config.resolve.fallback = { fs: false };

return config;
},
};

const bundleAnalyzerConfig = withBundleAnalyzer(config);
Expand Down
38 changes: 20 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
"name": "json-crack",
"private": true,
"version": "v3.0.0",
"license": "GPL-3.0",
"author": "https://github.com/AykutSarac",
"homepage": "https://jsoncrack.com",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "tsc && eslint src && prettier --check src",
"lint": "tsc --project tsconfig.json && eslint src && prettier --check src",
"lint:fix": "eslint --fix src & prettier --write src",
"analyze": "ANALYZE=true npm run build"
},
Expand All @@ -25,52 +26,53 @@
"@supabase/auth-helpers-react": "^0.4.2",
"@supabase/supabase-js": "^2.36.0",
"@tanstack/react-query": "^4.35.3",
"allotment": "^1.19.2",
"allotment": "^1.19.3",
"axios": "^1.5.0",
"dayjs": "^1.11.10",
"html-to-image": "^1.11.11",
"jq-in-the-browser": "^0.7.2",
"json-2-csv": "^4.1.0",
"jq-web": "^0.5.1",
"json-2-csv": "^5.0.1",
"json-to-ts": "^1.7.0",
"jsonc-parser": "^3.2.0",
"jsonwebtoken": "^9.0.2",
"jxon": "^2.0.0-beta.5",
"lodash.debounce": "^4.0.8",
"lodash.get": "^4.4.2",
"lodash.set": "^4.3.2",
"maketypes": "^1.1.2",
"next": "13.4.12",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-ga4": "^2.1.0",
"react-hot-toast": "^2.4.1",
"react-icons": "^4.11.0",
"react-icons": "^4.12.0",
"react-json-tree": "^0.18.0",
"react-linkify-it": "^1.0.7",
"react-simple-typewriter": "^5.0.1",
"react-zoomable-ui": "^0.11.0",
"reaflow": "5.2.6",
"styled-components": "^6.0.8",
"reaflow": "5.2.8",
"styled-components": "^6.1.1",
"toml": "^3.0.0",
"use-long-press": "^3.1.5",
"zustand": "^4.4.0"
"zustand": "^4.4.7"
},
"devDependencies": {
"@next/bundle-analyzer": "^13.4.12",
"@testing-library/react": "^14.0.0",
"@trivago/prettier-plugin-sort-imports": "^4.2.0",
"@types/jsonwebtoken": "^9.0.3",
"@types/jxon": "^2.0.2",
"@types/lodash.debounce": "^4.0.7",
"@types/lodash.get": "^4.4.7",
"@types/lodash.set": "^4.3.7",
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/jsonwebtoken": "^9.0.5",
"@types/jxon": "^2.0.5",
"@types/lodash.debounce": "^4.0.9",
"@types/lodash.get": "^4.4.9",
"@types/lodash.set": "^4.3.9",
"@types/node": "^20.4.7",
"@types/react": "18.2.18",
"@types/react-color": "^3.0.6",
"@types/react-syntax-highlighter": "^15.5.7",
"eslint": "8.46.0",
"eslint": "8.56.0",
"eslint-config-next": "13.4.12",
"eslint-config-prettier": "^8.10.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-unused-imports": "^3.0.0",
"prettier": "^3.0.1",
"prettier": "^3.1.1",
"ts-node": "^10.9.1",
"typescript": "5.1.6"
}
Expand Down
16 changes: 8 additions & 8 deletions src/components/Graph/CustomNode/TextNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { MdLink, MdLinkOff } from "react-icons/md";
import { CustomNodeProps } from "src/components/Graph/CustomNode";
import useToggleHide from "src/hooks/useToggleHide";
import { isContentImage } from "src/lib/utils/graph/calculateNodeSize";
import useConfig from "src/store/useConfig";
import useGraph from "src/store/useGraph";
import useStored from "src/store/useStored";
import { TextRenderer } from "./TextRenderer";
import * as Styled from "./styles";

Expand Down Expand Up @@ -52,13 +52,13 @@ const Node: React.FC<CustomNodeProps> = ({ node, x, y, hasCollapse = false }) =>
data: { isParent, childrenCount, type },
} = node;
const { validateHiddenNodes } = useToggleHide();
const hideCollapse = useStored(state => state.hideCollapse);
const showChildrenCount = useStored(state => state.childrenCount);
const imagePreview = useStored(state => state.imagePreview);
const collapseButtonVisible = useConfig(state => state.collapseButtonVisible);
const childrenCountVisible = useConfig(state => state.childrenCountVisible);
const imagePreviewEnabled = useConfig(state => state.imagePreviewEnabled);
const expandNodes = useGraph(state => state.expandNodes);
const collapseNodes = useGraph(state => state.collapseNodes);
const isExpanded = useGraph(state => state.collapsedParents.includes(id));
const isImage = imagePreview && isContentImage(text as string);
const isImage = imagePreviewEnabled && isContentImage(text as string);
const value = JSON.stringify(text).replaceAll('"', "");

const handleExpand = (e: React.MouseEvent<HTMLButtonElement>) => {
Expand All @@ -80,16 +80,16 @@ const Node: React.FC<CustomNodeProps> = ({ node, x, y, hasCollapse = false }) =>
data-x={x}
data-y={y}
data-key={JSON.stringify(text)}
$hasCollapse={isParent && hideCollapse}
$hasCollapse={isParent && collapseButtonVisible}
>
<Styled.StyledKey $value={value} $parent={isParent} $type={type}>
<TextRenderer>{value}</TextRenderer>
</Styled.StyledKey>
{isParent && childrenCount > 0 && showChildrenCount && (
{isParent && childrenCount > 0 && childrenCountVisible && (
<Styled.StyledChildrenCount>({childrenCount})</Styled.StyledChildrenCount>
)}

{isParent && hasCollapse && hideCollapse && (
{isParent && hasCollapse && collapseButtonVisible && (
<StyledExpand aria-label="Expand" onClick={handleExpand}>
{isExpanded ? <MdLinkOff size={18} /> : <MdLink size={18} />}
</StyledExpand>
Expand Down
8 changes: 4 additions & 4 deletions src/components/Graph/CustomNode/TextRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ const isURL =
/(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/gi;

export const TextRenderer: React.FC<{ children: string }> = ({ children }) => {
if (isURL.test(children.replaceAll('"', ""))) {
if (isURL.test(children?.replaceAll('"', ""))) {
return <Styled.StyledLinkItUrl>{children}</Styled.StyledLinkItUrl>;
}

if (isColorFormat(children.replaceAll('"', ""))) {
if (isColorFormat(children?.replaceAll('"', ""))) {
return (
<StyledRow>
<ColorSwatch radius={4} h={12} w={12} color={children.replaceAll('"', "")} />
{children.replaceAll('"', "")}
<ColorSwatch radius={4} h={12} w={12} mr={8} color={children?.replaceAll('"', "")} />
{children?.replaceAll('"', "")}
</StyledRow>
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Graph/CustomNode/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import dynamic from "next/dynamic";
import { NodeProps } from "reaflow";
import useGraph from "src/store/useGraph";
import useModal from "src/store/useModal";
import { NodeData } from "src/types/models";
import { NodeData } from "src/types/graph";
import { ObjectNode } from "./ObjectNode";
import { TextNode } from "./TextNode";

Expand Down
58 changes: 38 additions & 20 deletions src/components/Graph/index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import React from "react";
import dynamic from "next/dynamic";
import styled from "styled-components";
import { toast } from "react-hot-toast";
import { Space } from "react-zoomable-ui";
import { ElkRoot } from "reaflow/dist/layout/useLayout";
import { useLongPress } from "use-long-press";
import { CustomNode } from "src/components/Graph/CustomNode";
import { ViewMode } from "src/enums/viewMode.enum";
import useToggleHide from "src/hooks/useToggleHide";
import { Loading } from "src/layout/Loading";
import useConfig from "src/store/useConfig";
import useGraph from "src/store/useGraph";
import useStored from "src/store/useStored";
import useUser from "src/store/useUser";
import { NodeData } from "src/types/models";
import { NodeData } from "src/types/graph";
import { CustomEdge } from "./CustomEdge";
import { ErrorView } from "./ErrorView";
import { PremiumView } from "./PremiumView";
Expand All @@ -23,7 +25,7 @@ interface GraphProps {
isWidget?: boolean;
}

const StyledEditorWrapper = styled.div<{ $widget: boolean }>`
const StyledEditorWrapper = styled.div<{ $widget: boolean; $showRulers: boolean }>`
position: absolute;
width: 100%;
height: ${({ $widget }) => ($widget ? "calc(100vh - 36px)" : "calc(100vh - 63px)")};
Expand All @@ -33,20 +35,24 @@ const StyledEditorWrapper = styled.div<{ $widget: boolean }>`
--line-color-2: ${({ theme }) => theme.GRID_COLOR_SECONDARY};

background-color: var(--bg-color);
background-image: linear-gradient(var(--line-color-1) 1.5px, transparent 1.5px),
linear-gradient(90deg, var(--line-color-1) 1.5px, transparent 1.5px),
linear-gradient(var(--line-color-2) 1px, transparent 1px),
linear-gradient(90deg, var(--line-color-2) 1px, transparent 1px);
background-position:
-1.5px -1.5px,
-1.5px -1.5px,
-1px -1px,
-1px -1px;
background-size:
100px 100px,
100px 100px,
20px 20px,
20px 20px;
${({ $showRulers }) =>
$showRulers &&
`
background-image: linear-gradient(var(--line-color-1) 1.5px, transparent 1.5px),
linear-gradient(90deg, var(--line-color-1) 1.5px, transparent 1.5px),
linear-gradient(var(--line-color-2) 1px, transparent 1px),
linear-gradient(90deg, var(--line-color-2) 1px, transparent 1px);
background-position:
-1.5px -1.5px,
-1.5px -1.5px,
-1px -1px,
-1px -1px;
background-size:
100px 100px,
100px 100px,
20px 20px,
20px 20px;
`};

:active {
cursor: move;
Expand Down Expand Up @@ -76,7 +82,8 @@ const layoutOptions = {
};

const PREMIUM_LIMIT = 200;
const ERROR_LIMIT = 3_000;
const ERROR_LIMIT_TREE = 5_000;
const ERROR_LIMIT = 10_000;

const GraphCanvas = ({ isWidget }: GraphProps) => {
const { validateHiddenNodes } = useToggleHide();
Expand Down Expand Up @@ -138,6 +145,7 @@ const GraphCanvas = ({ isWidget }: GraphProps) => {

function getViewType(nodes: NodeData[]) {
if (nodes.length > ERROR_LIMIT) return "error";
if (nodes.length > ERROR_LIMIT_TREE) return "tree";
if (nodes.length > PREMIUM_LIMIT) return "premium";
return "graph";
}
Expand All @@ -147,7 +155,9 @@ export const Graph = ({ isWidget = false }: GraphProps) => {
const loading = useGraph(state => state.loading);
const isPremium = useUser(state => state.premium);
const viewType = useGraph(state => getViewType(state.nodes));
const gesturesEnabled = useStored(state => state.gesturesEnabled);
const gesturesEnabled = useConfig(state => state.gesturesEnabled);
const rulersEnabled = useConfig(state => state.rulersEnabled);
const setViewMode = useConfig(state => state.setViewMode);

const callback = React.useCallback(() => {
const canvas = document.querySelector(".jsoncrack-canvas") as HTMLDivElement | null;
Expand All @@ -166,7 +176,14 @@ export const Graph = ({ isWidget = false }: GraphProps) => {
if ("activeElement" in document) (document.activeElement as HTMLElement)?.blur();
}, []);

if (viewType === "error") return <ErrorView />;
if (viewType === "error") {
return <ErrorView />;
}

if (viewType === "tree") {
setViewMode(ViewMode.Tree);
toast("This document is too large to display as a graph. Switching to tree view.");
}

if (viewType === "premium" && !isWidget) {
if (!isPremium) return <PremiumView />;
Expand All @@ -180,6 +197,7 @@ export const Graph = ({ isWidget = false }: GraphProps) => {
onContextMenu={e => e.preventDefault()}
onClick={blurOnClick}
key={String(gesturesEnabled)}
$showRulers={rulersEnabled}
{...bindLongPress()}
>
<Space
Expand Down
4 changes: 2 additions & 2 deletions src/components/MonacoEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import React from "react";
import styled from "styled-components";
import Editor, { loader, useMonaco } from "@monaco-editor/react";
import { Loading } from "src/layout/Loading";
import useConfig from "src/store/useConfig";
import useFile from "src/store/useFile";
import useStored from "src/store/useStored";

loader.config({
paths: {
Expand Down Expand Up @@ -33,7 +33,7 @@ export const MonacoEditor = () => {
const setError = useFile(state => state.setError);
const jsonSchema = useFile(state => state.jsonSchema);
const getHasChanges = useFile(state => state.getHasChanges);
const theme = useStored(state => (state.lightmode ? "light" : "vs-dark"));
const theme = useConfig(state => (state.darkmodeEnabled ? "vs-dark" : "light"));
const fileType = useFile(state => state.format);

React.useEffect(() => {
Expand Down
Loading