🧡 Initial commit with Sveltekit.

This commit is contained in:
pheralb
2023-03-14 21:08:22 +00:00
parent 494d2b5e01
commit 41d98985b4
221 changed files with 191 additions and 1526 deletions
-47
View File
@@ -1,47 +0,0 @@
import { ErrorProps } from "@/interfaces/components";
import {
Button,
Center,
Heading,
HStack,
Text,
VStack,
} from "@chakra-ui/react";
import { ArrowClockwise, ArrowSquareOut, Warning } from "phosphor-react";
import { useRouter } from "next/router";
import CustomLink from "@/common/link";
const Error = (props: ErrorProps) => {
const router = useRouter();
const handleRefresh = () => {
router.reload();
};
return (
<Center>
<VStack>
<Warning size={90} />
<Heading fontSize="3xl">{props.title}</Heading>
<Text>{props.description}</Text>
<HStack>
<Button
variant="ghost"
borderWidth="1px"
leftIcon={<ArrowClockwise />}
onClick={handleRefresh}
>
Refresh
</Button>
<CustomLink href="https://github.com/pheralb/svgl/issues/new" external={true}>
<Button variant="ghost" rightIcon={<ArrowSquareOut />}>
Create issue
</Button>
</CustomLink>
</HStack>
</VStack>
</Center>
);
};
export default Error;
-16
View File
@@ -1,16 +0,0 @@
import { LoadingProps } from "@/interfaces/components";
import { Center, Spinner, Text, VStack } from "@chakra-ui/react";
import { LeapFrog } from "@uiball/loaders";
const Loading = (props: LoadingProps) => {
return (
<Center>
<VStack spacing={3} mt="3">
<LeapFrog size={32} speed={2.5} color="#4343E5" />
<Text>{props.text}</Text>
</VStack>
</Center>
);
};
export default Loading;
-125
View File
@@ -1,125 +0,0 @@
import { useEffect, useRef, useState } from "react";
import {
Input,
Text,
Image,
HStack,
Box,
Center,
Spinner,
} from "@chakra-ui/react";
import useDebounce from "@/hooks/useDebounce";
import { SearchProps, SVGCardProps } from "@/interfaces/components";
import CustomLink from "@/common/link";
import { getSvgByQuery } from "@/services";
import CustomIconBtn from "@/common/iconBtn";
import { Trash } from "phosphor-react";
import Tap from "@/animations/tap";
const Search = ({ availableFocus = false }: SearchProps) => {
const [search, setSearch] = useState("");
const [empty, setEmpty] = useState(false);
const [results, setResults] = useState<SVGCardProps[]>([]);
const debouncedSearch = useDebounce(search, 500);
const searchRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (debouncedSearch) {
fetch(getSvgByQuery + debouncedSearch).then((res) => {
if (res.ok) {
res.json().then((data) => {
setEmpty(data.length === 0);
setResults(data);
});
}
});
}
}, [debouncedSearch]);
useEffect(() => {
const isFocusAvailable = availableFocus && searchRef.current;
if (!isFocusAvailable) return;
const timeoutId = setTimeout(() => {
searchRef.current?.focus();
}, 100);
return () => clearTimeout(timeoutId);
}, [availableFocus]);
const handleFilter = (e: React.ChangeEvent<HTMLInputElement>) => {
setEmpty(false);
setSearch(e.target.value);
};
const handleClear = () => {
setSearch("");
setResults([]);
};
return (
<>
<Input
width="full"
variant="flushed"
size="lg"
placeholder="Search svgs..."
value={search}
onChange={handleFilter}
ref={searchRef}
/>
{search && !empty && results.length === 0 && (
<Box pt="4">
<Spinner />
</Box>
)}
{search && empty && <Box pt="3">No results found!</Box>}
{results && results.length > 0 && (
<>
<HStack
spacing={4}
mt={4}
overflowX="auto"
overflowY="hidden"
alignItems="start"
>
{results.map((item: SVGCardProps) => (
<Tap key={item.title}>
<CustomLink href={`/svg/${item.id}`}>
<Box
mb="2"
p="3"
shadow="sm"
borderWidth="1px"
borderRadius="5px"
width="100%"
>
<Center>
<Image
width="25px"
mb="2"
src={item.slug}
alt={item.title}
/>
</Center>
<Text>{item.title}</Text>
</Box>
</CustomLink>
</Tap>
))}
</HStack>
<Box p="3">
<CustomIconBtn
title="clear"
icon={<Trash size={16} />}
onClick={handleClear}
/>
</Box>
</>
)}
</>
);
};
export default Search;
-47
View File
@@ -1,47 +0,0 @@
import React from "react";
import { SVGCardProps } from "@/interfaces/components";
import {
Box,
Center,
Image,
Text,
useColorModeValue,
useDisclosure,
} from "@chakra-ui/react";
import Tap from "@/animations/tap";
import CustomLink from "@/common/link";
import { Smiley } from "phosphor-react";
const SVGCard = (props: SVGCardProps) => {
const bg = useColorModeValue("bg.light", "bg.dark");
const color = useColorModeValue("rgb(0,0,0, .1)", "rgb(255,255,255, .1)");
return (
<>
<Tap>
<CustomLink href={`/svg/${props.id}`}>
<Box
bg={bg}
p={4}
cursor="pointer"
borderRadius="10px"
borderWidth="1px"
mb="2"
_hover={{
border:`1px solid ${color}`,
transform: "scale(1.03)",
}}
transition="all 0.2s" >
<Center>
<Image height="40px" src={props.svg} alt={props.title} />
</Center>
<Text mt="3" fontWeight="light" textAlign="center">
{props.title}
</Text>
</Box>
</CustomLink>
</Tap>
</>
);
};
export default SVGCard;
-108
View File
@@ -1,108 +0,0 @@
import React from "react";
import {
Button,
Flex,
Heading,
HStack,
Icon,
Image,
Link,
} from "@chakra-ui/react";
import { ArrowSquareOut, Copy, DownloadSimple } from "phosphor-react";
import confetti from "canvas-confetti";
import download from "downloadjs";
import { toast } from "react-hot-toast";
import { ToastTheme } from "@/theme/toast";
import { SVGCardProps } from "@/interfaces/components";
// Download SVG =>
const downloadSvg = (url?: string) => {
confetti({
particleCount: 200,
startVelocity: 30,
spread: 300,
gravity: 1.2,
origin: { y: 0 },
});
download(url || "");
};
const MIMETYPE = 'text/plain';
// Return content of svg as blob =>
const getSvgContent = async (url: string | undefined, isSupported: boolean) => {
const response = await fetch(url || "");
const content = await response.text();
// It was necessary to use blob because in chrome there were issues with the copy to clipboard
const blob = new Blob([content], { type: MIMETYPE });
return isSupported ? blob : content;
}
// Copy to clipboard =>
const copyToClipboard = async (url?: string) => {
const data = {
[MIMETYPE]: getSvgContent(url, true)
};
try {
const clipboardItem = new ClipboardItem(data);
await navigator.clipboard.write([clipboardItem]);
} catch (error) {
// This section works as a fallback on Firefox
const content = await getSvgContent(url, false) as string;
await navigator.clipboard.writeText(content);
}
toast("Copied to clipboard", ToastTheme);
};
const SVGInfo = (props: SVGCardProps) => {
return (
<Flex
pt="7"
pb="7"
direction="column"
align="center"
justify="center"
borderWidth="1px"
borderRadius="10px"
>
<Image
src={props.slug}
alt={props.title}
fit="cover"
loading="lazy"
width="85px"
/>
<Heading mt={6} mb={6} fontSize="4xl">
{props.title}
</Heading>
<Flex direction={{ base: "column", md: "row" }}>
<Button
variant="ghost"
borderWidth="1px"
leftIcon={<Copy />}
onClick={() => copyToClipboard(props.slug)}
mb={{ base: "2", md: "0" }}
mr={{ base: "0", md: "3" }}
>
Copy to clipboard
</Button>
<Button
leftIcon={<DownloadSimple />}
variant="primary"
onClick={() => downloadSvg(props.slug)}
mb={{ base: "2", md: "0" }}
mr={{ base: "0", md: "3" }}
>
Download .svg
</Button>
<Link href={props.url} isExternal={true}>
{props.title} website <Icon as={ArrowSquareOut} mt="2" />
</Link>
</Flex>
</Flex>
);
};
export default SVGInfo;