mirror of
https://github.com/AykutSarac/jsoncrack.com.git
synced 2025-01-12 19:02:53 +08:00
feat: allow upload file by dropping on screen
This commit is contained in:
parent
3874575a34
commit
3299a61d83
60
src/containers/Editor/FullscreenDropzone.tsx
Normal file
60
src/containers/Editor/FullscreenDropzone.tsx
Normal file
@ -0,0 +1,60 @@
|
||||
import React from "react";
|
||||
import { Group, Text } from "@mantine/core";
|
||||
import { Dropzone } from "@mantine/dropzone";
|
||||
import toast from "react-hot-toast";
|
||||
import { VscCircleSlash, VscFiles } from "react-icons/vsc";
|
||||
import { FileFormat } from "src/enums/file.enum";
|
||||
import useFile from "src/store/useFile";
|
||||
|
||||
export const FullscreenDropzone = () => {
|
||||
const setContents = useFile(state => state.setContents);
|
||||
|
||||
return (
|
||||
<Dropzone.FullScreen
|
||||
maxSize={300 * 1024}
|
||||
maxFiles={1}
|
||||
accept={[
|
||||
"application/json",
|
||||
"application/x-yaml",
|
||||
"text/csv",
|
||||
"application/xml",
|
||||
"application/toml",
|
||||
]}
|
||||
onReject={files => toast.error(`Unable to load file ${files[0].file.name}`)}
|
||||
onDrop={async e => {
|
||||
const fileContent = await e[0].text();
|
||||
let fileExtension = e[0].name.split(".").pop() as FileFormat | undefined;
|
||||
if (!fileExtension) fileExtension = FileFormat.JSON;
|
||||
setContents({ contents: fileContent, format: fileExtension, hasChanges: false });
|
||||
}}
|
||||
>
|
||||
<Group
|
||||
justify="center"
|
||||
ta="center"
|
||||
align="center"
|
||||
gap="xl"
|
||||
h="100vh"
|
||||
style={{ pointerEvents: "none" }}
|
||||
>
|
||||
<Dropzone.Accept>
|
||||
<VscFiles size={100} />
|
||||
<Text fz="h1" fw={500} mt="lg">
|
||||
Upload to JSON Crack
|
||||
</Text>
|
||||
<Text fz="lg" c="dimmed" mt="sm">
|
||||
(Max file size: 4 MB)
|
||||
</Text>
|
||||
</Dropzone.Accept>
|
||||
<Dropzone.Reject>
|
||||
<VscCircleSlash size={100} />
|
||||
<Text fz="h1" fw={500} mt="lg">
|
||||
Invalid file
|
||||
</Text>
|
||||
<Text fz="lg" c="dimmed" mt="sm">
|
||||
Allowed formats are JSON, YAML, CSV, XML, TOML
|
||||
</Text>
|
||||
</Dropzone.Reject>
|
||||
</Group>
|
||||
</Dropzone.FullScreen>
|
||||
);
|
||||
};
|
@ -4,6 +4,7 @@ import styled from "styled-components";
|
||||
import { Allotment } from "allotment";
|
||||
import "allotment/dist/style.css";
|
||||
import useGraph from "src/modules/GraphView/stores/useGraph";
|
||||
import { FullscreenDropzone } from "./FullscreenDropzone";
|
||||
|
||||
export const StyledEditor = styled(Allotment)`
|
||||
position: relative !important;
|
||||
@ -28,18 +29,21 @@ export const Editor = () => {
|
||||
const fullscreen = useGraph(state => state.fullscreen);
|
||||
|
||||
return (
|
||||
<StyledEditor proportionalLayout={false}>
|
||||
<Allotment.Pane
|
||||
preferredSize={450}
|
||||
minSize={fullscreen ? 0 : 300}
|
||||
maxSize={800}
|
||||
visible={!fullscreen}
|
||||
>
|
||||
<TextEditor />
|
||||
</Allotment.Pane>
|
||||
<Allotment.Pane minSize={0}>
|
||||
<LiveEditor />
|
||||
</Allotment.Pane>
|
||||
</StyledEditor>
|
||||
<>
|
||||
<StyledEditor proportionalLayout={false}>
|
||||
<Allotment.Pane
|
||||
preferredSize={450}
|
||||
minSize={fullscreen ? 0 : 300}
|
||||
maxSize={800}
|
||||
visible={!fullscreen}
|
||||
>
|
||||
<TextEditor />
|
||||
</Allotment.Pane>
|
||||
<Allotment.Pane minSize={0}>
|
||||
<LiveEditor />
|
||||
</Allotment.Pane>
|
||||
</StyledEditor>
|
||||
<FullscreenDropzone />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -104,6 +104,17 @@ export const Navbar = () => {
|
||||
>
|
||||
Sign in
|
||||
</Button>
|
||||
<Button
|
||||
component="a"
|
||||
color="brightBlue"
|
||||
href="/editor"
|
||||
radius="md"
|
||||
visibleFrom="sm"
|
||||
size="md"
|
||||
fw={600}
|
||||
>
|
||||
Start for free
|
||||
</Button>
|
||||
</Right>
|
||||
</StyledNavbar>
|
||||
</StyledNavbarWrapper>
|
||||
|
@ -3,6 +3,7 @@ import dynamic from "next/dynamic";
|
||||
import Head from "next/head";
|
||||
import { useRouter } from "next/router";
|
||||
import { useMantineColorScheme } from "@mantine/core";
|
||||
import "@mantine/dropzone/styles.css";
|
||||
import styled, { ThemeProvider } from "styled-components";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { metaDescription } from "src/constants/landing";
|
||||
|
@ -14,6 +14,7 @@ type SetContents = {
|
||||
contents?: string;
|
||||
hasChanges?: boolean;
|
||||
skipUpdate?: boolean;
|
||||
format?: FileFormat;
|
||||
};
|
||||
|
||||
type Query = string | string[] | undefined;
|
||||
@ -96,9 +97,14 @@ const useFile = create<FileStates & JsonActions>()((set, get) => ({
|
||||
console.warn("The content was unable to be converted, so it was cleared instead.");
|
||||
}
|
||||
},
|
||||
setContents: async ({ contents, hasChanges = true, skipUpdate = false }) => {
|
||||
setContents: async ({ contents, hasChanges = true, skipUpdate = false, format }) => {
|
||||
try {
|
||||
set({ ...(contents && { contents }), error: null, hasChanges });
|
||||
set({
|
||||
...(contents && { contents }),
|
||||
error: null,
|
||||
hasChanges,
|
||||
format: format ?? get().format,
|
||||
});
|
||||
|
||||
const isFetchURL = window.location.href.includes("?");
|
||||
const json = await contentToJson(get().contents, get().format);
|
||||
|
Loading…
x
Reference in New Issue
Block a user