mirror of
https://github.com/ershisan99/DevToysWeb.git
synced 2025-12-17 20:49:24 +00:00
feat: add all tools page mock
This commit is contained in:
@@ -5,7 +5,7 @@ A web clone of [DevToys](https://github.com/veler/DevToys)
|
|||||||
## Todo
|
## Todo
|
||||||
|
|
||||||
- [x] Add site layout
|
- [x] Add site layout
|
||||||
- [ ] Add all tools page mock
|
- [x] Add all tools page mock
|
||||||
- [ ] Implement tools
|
- [ ] Implement tools
|
||||||
- [ ] Converters
|
- [ ] Converters
|
||||||
- [ ] Encoders / Decoders
|
- [ ] Encoders / Decoders
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
"@mui/icons-material": "^5.5.1",
|
"@mui/icons-material": "^5.5.1",
|
||||||
"@mui/material": "^5.5.1",
|
"@mui/material": "^5.5.1",
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
|
"fuse.js": "^6.5.3",
|
||||||
"next": "12.1.0",
|
"next": "12.1.0",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
|
|||||||
25
src/components/common/Main.tsx
Normal file
25
src/components/common/Main.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { Box, css, Stack, Theme, Typography } from "@mui/material";
|
||||||
|
import { memo, PropsWithChildren } from "react";
|
||||||
|
|
||||||
|
type Props = PropsWithChildren<{
|
||||||
|
title: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
const mainTitle = (theme: Theme) => css`
|
||||||
|
font-size: ${theme.typography.fontSize * 3}px;
|
||||||
|
font-weight: 400;
|
||||||
|
margin-bottom: ${theme.spacing(2)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledComponent = ({ children, title }: Props) => (
|
||||||
|
<Box component="main" padding={6}>
|
||||||
|
<Typography variant="h1" css={mainTitle}>
|
||||||
|
{title}
|
||||||
|
</Typography>
|
||||||
|
<Stack spacing={2}>{children}</Stack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Component = memo(StyledComponent);
|
||||||
|
|
||||||
|
export default Component;
|
||||||
3
src/components/common/index.ts
Normal file
3
src/components/common/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import Main from "./Main";
|
||||||
|
|
||||||
|
export { Main };
|
||||||
96
src/components/pages/home/Card.tsx
Normal file
96
src/components/pages/home/Card.tsx
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Card,
|
||||||
|
CardActionArea,
|
||||||
|
CardContent,
|
||||||
|
CardMedia,
|
||||||
|
css,
|
||||||
|
Theme,
|
||||||
|
Tooltip,
|
||||||
|
Typography,
|
||||||
|
} from "@mui/material";
|
||||||
|
import NextLink, { LinkProps } from "next/link";
|
||||||
|
import { memo, ReactNode } from "react";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
icon: ReactNode;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
} & Pick<LinkProps, "href">;
|
||||||
|
|
||||||
|
const cardStyle = css`
|
||||||
|
text-align: left;
|
||||||
|
width: 160px;
|
||||||
|
height: 300px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const cardActionArea = css`
|
||||||
|
height: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const cardMedia = (theme: Theme) => css`
|
||||||
|
text-align: center;
|
||||||
|
padding: ${theme.spacing(4)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const cardMediaIconWrapper = (theme: Theme) => css`
|
||||||
|
padding: ${theme.spacing(2)};
|
||||||
|
height: 96px;
|
||||||
|
> * {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const cardContent = css`
|
||||||
|
padding-top: 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const cardTitle = css`
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const cardDescription = (theme: Theme) => css`
|
||||||
|
font-size: 12px;
|
||||||
|
color: ${theme.palette.grey[600]};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledComponent = ({ icon, title, description, href, disabled }: Props) => {
|
||||||
|
const card = (
|
||||||
|
<Card css={cardStyle}>
|
||||||
|
<NextLink {...{ href }} passHref>
|
||||||
|
<CardActionArea
|
||||||
|
{...{ disabled }}
|
||||||
|
css={cardActionArea}
|
||||||
|
style={{ opacity: disabled ? 0.5 : 1.0 }}
|
||||||
|
>
|
||||||
|
<CardMedia css={cardMedia}>
|
||||||
|
<Box css={cardMediaIconWrapper}>{icon}</Box>
|
||||||
|
</CardMedia>
|
||||||
|
<CardContent css={cardContent}>
|
||||||
|
<Typography gutterBottom variant="h3" css={cardTitle}>
|
||||||
|
{title}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" css={cardDescription}>
|
||||||
|
{description}
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</CardActionArea>
|
||||||
|
</NextLink>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
|
||||||
|
return disabled ? (
|
||||||
|
<Tooltip title="coming soon!" placement="top" arrow>
|
||||||
|
{card}
|
||||||
|
</Tooltip>
|
||||||
|
) : (
|
||||||
|
card
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Component = memo(StyledComponent);
|
||||||
|
|
||||||
|
export default Component;
|
||||||
158
src/components/pages/home/Content.tsx
Normal file
158
src/components/pages/home/Content.tsx
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
import CodeIcon from "@mui/icons-material/Code";
|
||||||
|
import DataObjectIcon from "@mui/icons-material/DataObject";
|
||||||
|
import DragHandleIcon from "@mui/icons-material/DragHandle";
|
||||||
|
import FilterIcon from "@mui/icons-material/Filter";
|
||||||
|
import FingerprintIcon from "@mui/icons-material/Fingerprint";
|
||||||
|
import KeyIcon from "@mui/icons-material/Key";
|
||||||
|
import LinkIcon from "@mui/icons-material/Link";
|
||||||
|
import NumbersIcon from "@mui/icons-material/Numbers";
|
||||||
|
import TextIncreaseIcon from "@mui/icons-material/TextIncrease";
|
||||||
|
import TransformIcon from "@mui/icons-material/Transform";
|
||||||
|
import { Grid } from "@mui/material";
|
||||||
|
import Fuse from "fuse.js";
|
||||||
|
import { memo } from "react";
|
||||||
|
import { selector, useRecoilValue } from "recoil";
|
||||||
|
|
||||||
|
import { Main } from "@/components/common";
|
||||||
|
import { searchTextState } from "@/components/layout/states";
|
||||||
|
import { pagesPath } from "@/libs/$path";
|
||||||
|
|
||||||
|
import Card from "./Card";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
filteredTools: typeof tools;
|
||||||
|
};
|
||||||
|
|
||||||
|
const tools = [
|
||||||
|
{
|
||||||
|
icon: <TransformIcon />,
|
||||||
|
title: "Json <> Yaml Converter",
|
||||||
|
description: "Convert Json data to Yaml and vice versa",
|
||||||
|
keywords: "json yaml converter",
|
||||||
|
href: pagesPath.$url(),
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <NumbersIcon />,
|
||||||
|
title: "Number Base Converter",
|
||||||
|
description: "Convert numbers from one base to another",
|
||||||
|
keywords: "number base converter",
|
||||||
|
href: pagesPath.$url(),
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <CodeIcon />,
|
||||||
|
title: "HTML Encoder / Decoder",
|
||||||
|
description:
|
||||||
|
"Encode or decode all the applicable characters to their corresponding HTML entities",
|
||||||
|
keywords: "html encoder escaper decocder unescaper",
|
||||||
|
href: pagesPath.$url(),
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <LinkIcon />,
|
||||||
|
title: "URL Encoder / Decoder",
|
||||||
|
description:
|
||||||
|
"Encode or decode all the applicable characters to their corresponding URL entities",
|
||||||
|
keywords: "url encoder escaper decocder unescaper",
|
||||||
|
href: pagesPath.$url(),
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <DragHandleIcon />,
|
||||||
|
title: "Base 64 Encoder / Decoder",
|
||||||
|
description: "Encode and decode Base64 data",
|
||||||
|
keywords: "base64 encoder decocder",
|
||||||
|
href: pagesPath.$url(),
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <KeyIcon />,
|
||||||
|
title: "JWT Decoder",
|
||||||
|
description: "Decode a JWT header, payload and signature",
|
||||||
|
keywords: "jwt json web token decocder",
|
||||||
|
href: pagesPath.$url(),
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <DataObjectIcon />,
|
||||||
|
title: "JSON Formatter",
|
||||||
|
description: "Indent or minify JSON data",
|
||||||
|
keywords: "json formatter",
|
||||||
|
href: pagesPath.$url(),
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <FingerprintIcon />,
|
||||||
|
title: "Hash Generator",
|
||||||
|
description: "Calculate MD5, SHA1, SHA256 and SHA512 hash from text data",
|
||||||
|
keywords: "hash generator md5 sha1 sha256 sha512",
|
||||||
|
href: pagesPath.$url(),
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <NumbersIcon />,
|
||||||
|
title: "UUID Generator",
|
||||||
|
description: "Generate UUIDs version 1 and 4",
|
||||||
|
keywords: "guid uuid1 uuid4 generator",
|
||||||
|
href: pagesPath.$url(),
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <TextIncreaseIcon />,
|
||||||
|
title: "Regex Tester",
|
||||||
|
description: "Validate and test regular expressions",
|
||||||
|
keywords: "regular expression regex validator tester",
|
||||||
|
href: pagesPath.$url(),
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <FilterIcon />,
|
||||||
|
title: "PNG / JPEG Compressor",
|
||||||
|
description: "Lossless PNG and JPEG optimizer",
|
||||||
|
keywords: "png jpeg compressor optimizer image",
|
||||||
|
href: pagesPath.$url(),
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const StyledComponent = ({ filteredTools }: Props) => (
|
||||||
|
<Main title="All tools">
|
||||||
|
<Grid container rowSpacing={4} columnSpacing={2}>
|
||||||
|
{filteredTools.map(({ icon, title, description, href, disabled }) => (
|
||||||
|
<Grid key={title} item>
|
||||||
|
<Card {...{ icon, title, description, href, disabled }} />
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</Main>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Component = memo(StyledComponent);
|
||||||
|
|
||||||
|
const filteredToolsState = selector({
|
||||||
|
key: "filteredToolsState",
|
||||||
|
get: ({ get }) => {
|
||||||
|
const searchText = get(searchTextState).trim();
|
||||||
|
|
||||||
|
if (searchText === "") {
|
||||||
|
return { filteredTools: tools };
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchWords = searchText.split(" ").map(word => ({ keywords: word }));
|
||||||
|
|
||||||
|
const fuse = new Fuse(tools, { keys: ["keywords"], threshold: 0.5 });
|
||||||
|
const result = fuse.search({ $and: searchWords });
|
||||||
|
const filteredTools = result.map(({ item }) => item);
|
||||||
|
|
||||||
|
return { filteredTools };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const Container = () => {
|
||||||
|
const { filteredTools } = useRecoilValue(filteredToolsState);
|
||||||
|
|
||||||
|
return <Component {...{ filteredTools }} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Container;
|
||||||
3
src/components/pages/home/index.ts
Normal file
3
src/components/pages/home/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import Content from "./Content";
|
||||||
|
|
||||||
|
export { Content };
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import type { NextPage } from "next";
|
import type { NextPage } from "next";
|
||||||
|
|
||||||
const Page: NextPage = () => <p>hello</p>;
|
import { Content } from "@/components/pages/home";
|
||||||
|
|
||||||
|
const Page: NextPage = Content;
|
||||||
|
|
||||||
export default Page;
|
export default Page;
|
||||||
|
|||||||
@@ -1287,6 +1287,11 @@ functional-red-black-tree@^1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
|
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
|
||||||
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
|
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
|
||||||
|
|
||||||
|
fuse.js@^6.5.3:
|
||||||
|
version "6.5.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-6.5.3.tgz#7446c0acbc4ab0ab36fa602e97499bdb69452b93"
|
||||||
|
integrity sha512-sA5etGE7yD/pOqivZRBvUBd/NaL2sjAu6QuSaFoe1H2BrJSkH/T/UXAJ8CdXdw7DvY3Hs8CXKYkDWX7RiP5KOg==
|
||||||
|
|
||||||
get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1:
|
get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6"
|
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6"
|
||||||
|
|||||||
Reference in New Issue
Block a user