Feature/site detail page (#99)

* site: pull data from "icons" dir

* site: display icons

* site: remove redundant code

* site: colour mode support

* site: header

* site: order imports

* site: search

* site: add toast when copying icon

* site: styling

* site: hero

* fix: disable theme toggle transitions

* feat: Use Yarn Workspaces

* refactor: Update site deploy scripts

* refactor: Remove dark mode for now

* feat: Add site title

* refactor: Fix warning and format

* feat: Add dark mode back 👀

* feat: Escape key to reset query

* Fix by aelfric

* Add Github link

* Fix #40

* Add site overlay

* sort categories

* Add detail page

* Add first categories

* add box

* move site to root directory

* fix merge issues

* Fix routing issues

* Fix icon overlay

* Add copy and download icon

* Fix style issues

* Add text

* update chakra UI

* remove import

* update dependecies

* add lucide react

* Fix bugs

* delete stats files

* update charkra version

Co-authored-by: John Letey <johnletey@gmail.com>
Co-authored-by: appmachine <appmachine@appmachines-iMac.local>
This commit is contained in:
Eric Fennis
2020-10-26 08:59:56 +01:00
committed by GitHub
parent 2c38fac9b1
commit 5c96b8d848
22 changed files with 1227 additions and 712 deletions

View File

@@ -1,5 +1,6 @@
import { CSSReset, ThemeProvider, ColorModeProvider } from '@chakra-ui/core';
import { CSSReset, ChakraProvider, ColorModeProvider } from '@chakra-ui/core';
import customTheme from '../lib/theme';
import '../assets/styling.css';
import Head from 'next/head';
const App = ({ Component, pageProps }) => {
@@ -8,12 +9,9 @@ const App = ({ Component, pageProps }) => {
<Head>
<title>Lucide</title>
</Head>
<ThemeProvider theme={customTheme}>
<ColorModeProvider>
<CSSReset />
<Component {...pageProps} />
</ColorModeProvider>
</ThemeProvider>
<ChakraProvider theme={customTheme}>
<Component {...pageProps} />
</ChakraProvider>
</>
);
};

View File

@@ -1,4 +1,5 @@
import Document, { Head, Html, Main, NextScript } from "next/document";
import { ColorModeScript } from "@chakra-ui/core"
class MyDocument extends Document {
render() {
@@ -6,7 +7,7 @@ class MyDocument extends Document {
<Html>
<Head>
<link
href="https://indestructibletype.com/fonts/Jost.css"
href="https://fonts.googleapis.com/css2?family=Mukta:wght@400;600;700&display=swap"
rel="stylesheet"
/>
</Head>
@@ -20,6 +21,7 @@ class MyDocument extends Document {
}
`}</style>
<body>
<ColorModeScript />
<Main />
<NextScript />
</body>

View File

@@ -0,0 +1,39 @@
import { useEffect } from 'react'
import { useRouter } from 'next/router'
import IconDetailOverlay from '../../components/IconDetailOverlay'
import { getAllData, getData } from '../../lib/icons';
import IconOverview from '../../components/IconOverview';
import Layout from '../../components/Layout';
import Header from '../../components/Header';
const IconPage = ({ icon, data }) => {
const router = useRouter()
return (
<Layout>
<IconDetailOverlay
icon={icon}
onClose={() => router.push('/')}
/>
<Header {...{data}}/>
<IconOverview {...{data}}/>
</Layout>
)
}
export default IconPage
export function getStaticProps({ params: { iconName } }) {
const data = getAllData();
const icon = getData(iconName);
return { props: { icon, data } }
}
export function getStaticPaths() {
return {
paths: getAllData().map(({name: iconName }) => ({
params: { iconName },
})),
fallback: false,
}
}

View File

@@ -1,148 +1,31 @@
import {
Button,
Flex,
Grid,
Link,
Icon,
Input,
InputGroup,
InputLeftElement,
Stack,
Text,
useToast,
} from '@chakra-ui/core';
import copy from 'copy-to-clipboard';
import download from 'downloadjs';
import JSZip from 'jszip';
import { useEffect, useRef, useState } from 'react';
import Layout from '../components/Layout';
import { getAllData } from '../lib/icons';
import useSearch from '../lib/search';
import { useRouter } from 'next/router';
import { useDebounce } from '../lib/useDebounce';
import Layout from "../components/Layout";
import { getAllData } from "../lib/icons";
function generateZip(icons) {
const zip = new JSZip();
Object.values(icons).forEach((icon) =>
// @ts-ignore
zip.file(`${icon.name}.svg`, icon.src)
);
return zip.generateAsync({ type: 'blob' });
}
import IconOverview from "../components/IconOverview";
import IconDetailOverlay from "../components/IconDetailOverlay";
import { useRouter } from "next/router";
import Header from "../components/Header";
const IndexPage = ({ data }) => {
const router = useRouter();
const { query } = router.query;
const [queryText, setQueryText] = useState(query || '');
const toast = useToast();
const debouncedQuery = useDebounce(queryText, 1000);
const results = useSearch(data, queryText);
useEffect(() => {
setQueryText(query);
}, [query]);
useEffect(() => {
router.push({
pathname: '/',
query: { query: debouncedQuery },
});
}, [debouncedQuery]);
const inputElement = useRef(null);
function handleKeyDown(event) {
if (event.key === '/' && inputElement.current !== document.activeElement) {
event.preventDefault();
inputElement.current.focus();
}
}
useEffect(() => {
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, []);
const getIcon = (iconName) => data.find(({name}) => name === iconName) || {};
return (
<Layout>
<Flex direction="column" align="center" justify="center">
<Text fontSize="3xl" as="b" mb="4">
Simply beautiful open source icons, community-sourced
</Text>
<Text fontSize="lg" as="p" textAlign="center" mb="8">
An open-source icon library, a fork of Feather Icons. <br/>We're expanding the icon set as much as possible while keeping it nice-looking - <Link href="https://github.com/lucide-icons/lucide" isExternal>join us</Link>!
</Text>
<Stack isInline marginTop={3} marginBottom={10}>
<Button
onClick={async () => {
const zip = await generateZip(data);
download(zip, 'feather.zip');
}}
>
Download all
</Button>
</Stack>
</Flex>
<InputGroup position="sticky" top={2} zIndex={1}>
<InputLeftElement children={<Icon name="search" />} />
<Input
ref={inputElement}
placeholder={`Search ${Object.keys(data).length} icons (Press "/" to focus)`}
value={queryText}
onChange={(event) => setQueryText(event.target.value)}
marginBottom={5}
/>
</InputGroup>
{results.length > 0 ? (
<Grid templateColumns={`repeat(auto-fill, minmax(160px, 1fr))`} gap={5}>
{results.map((icon) => {
// @ts-ignore
const actualIcon = icon.item ? icon.item : icon;
return (
<Button
variant="ghost"
borderWidth="1px"
rounded="lg"
padding={16}
onClick={(event) => {
if (event.shiftKey) {
copy(actualIcon.src);
toast({
title: 'Copied!',
description: `Icon "${actualIcon.name}" copied to clipboard.`,
status: 'success',
duration: 1500,
});
} else {
download(actualIcon.src, `${actualIcon.name}.svg`, 'image/svg+xml');
}
}}
key={actualIcon.name}
alignItems="center"
>
<Flex direction="column" align="center" justify="center">
<div dangerouslySetInnerHTML={{ __html: actualIcon.src }} />
<Text marginTop={5}>{actualIcon.name}</Text>
</Flex>
</Button>
);
})}
</Grid>
) : (
<Text
fontSize="2xl"
fontWeight="bold"
textAlign="center"
style={{ wordBreak: 'break-word' }}
>
No results found for "{query}"
</Text>
)}
<IconDetailOverlay
isOpen={!!router.query.iconName}
icon={getIcon(router.query.iconName)}
onClose={() => router.push('/')}
/>
<Header {...{data}}/>
<IconOverview {...{data}}/>
</Layout>
);
};
export async function getStaticProps() {
let data = getAllData();
return {
props: {
data,