mirror of
https://github.com/lucide-icons/lucide.git
synced 2026-11-13 13:48:26 +00:00
Fix site build (#796)
* Some fixes site * A lot of fixes in the site * move file
This commit is contained in:
+5
-1
@@ -17,5 +17,9 @@ module.exports = {
|
||||
trailingComma: 'all'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./site/tsconfig.json', './packages/*/tsconfig.json'],
|
||||
},
|
||||
};
|
||||
|
||||
Generated
+1807
-507
File diff suppressed because it is too large
Load Diff
+4
-11
@@ -1,21 +1,14 @@
|
||||
/* eslint-disable no-undef */
|
||||
/* eslint-disable */
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint', 'import', 'prettier'],
|
||||
plugins: ['@typescript-eslint', 'import'],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:@next/next/recommended',
|
||||
'prettier',
|
||||
],
|
||||
rules: {
|
||||
'prettier/prettier': [
|
||||
'error',
|
||||
{
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
},
|
||||
],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable */
|
||||
module.exports = {
|
||||
testPathIgnorePatterns: ['<rootDir>/.next/', '<rootDir>/node_modules/'],
|
||||
setupFilesAfterEnv: ['<rootDir>/setupTests.js'],
|
||||
|
||||
+3
-7
@@ -1,15 +1,11 @@
|
||||
/* eslint-disable no-undef */
|
||||
/* eslint-disable */
|
||||
module.exports = {
|
||||
webpack(config) {
|
||||
config.module.rules.push({
|
||||
test: /\.svg$/,
|
||||
issuer: {
|
||||
and: [/\.(js|ts)x?$/],
|
||||
},
|
||||
|
||||
use: ['@svgr/webpack'],
|
||||
use: ["@svgr/webpack"]
|
||||
});
|
||||
|
||||
return config;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
+15
-12
@@ -10,7 +10,9 @@
|
||||
"export": "next export -o build",
|
||||
"deploy": "pnpm build && pnpm export",
|
||||
"lint": "eslint .",
|
||||
"test": "jest"
|
||||
"lint:fix": "eslint --fix .",
|
||||
"test": "jest",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@chakra-ui/react": "1.8.8",
|
||||
@@ -19,7 +21,7 @@
|
||||
"@mdx-js/loader": "^1.6.22",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"@next/mdx": "^11.0.0",
|
||||
"@svgr/webpack": "^6.2.1",
|
||||
"@svgr/webpack": "^6.3.1",
|
||||
"downloadjs": "^1.4.7",
|
||||
"framer-motion": "^4",
|
||||
"fuse.js": "^6.5.3",
|
||||
@@ -37,23 +39,24 @@
|
||||
"react-svg-loader": "^3.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/eslint-plugin-next": "^11.1.0",
|
||||
"@testing-library/dom": "^7.24.4",
|
||||
"@testing-library/jest-dom": "^5.11.4",
|
||||
"@testing-library/react": "^11.0.4",
|
||||
"@testing-library/react-hooks": "^3.4.2",
|
||||
"react-test-renderer": "17.0.2",
|
||||
"@next/eslint-plugin-next": "^12.2.5",
|
||||
"@testing-library/dom": "^7.31.2",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^11.2.7",
|
||||
"@testing-library/react-hooks": "^8.0.1",
|
||||
"@types/jest": "^28.1.7",
|
||||
"@types/node": "^14.0.11",
|
||||
"@types/react": "^16.9.35",
|
||||
"@types/react-dom": "^16.9.8",
|
||||
"@typescript-eslint/eslint-plugin": "^4.29.1",
|
||||
"@typescript-eslint/parser": "^4.29.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.34.0",
|
||||
"@typescript-eslint/parser": "^5.34.0",
|
||||
"babel-jest": "^26.5.2",
|
||||
"babel-loader": "^8.1.0",
|
||||
"cheerio": "^1.0.0-rc.3",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint": "^8.22.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"jest": "^26.5.2",
|
||||
"prettier": "^2.3.2",
|
||||
"react-test-renderer": "17.0.2",
|
||||
"typescript": "^4.3.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ const calculateLinesToHighlight = (meta: string) => {
|
||||
};
|
||||
};
|
||||
|
||||
interface HighlightProps {
|
||||
interface HighlightProps extends BoxProps {
|
||||
code: string;
|
||||
language: Language;
|
||||
metastring?: string;
|
||||
|
||||
@@ -21,7 +21,7 @@ function ColorPicker({ hsv, hsl, onChange, value: color }: ColorPickerProps) {
|
||||
}, [color]);
|
||||
|
||||
const handleChange = (e) => {
|
||||
let value = e.target.value;
|
||||
const value = e.target.value;
|
||||
setValue(value);
|
||||
onChange(value, e);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Box, ButtonProps, Button, useClipboard } from '@chakra-ui/react';
|
||||
import { Button, useClipboard } from '@chakra-ui/react';
|
||||
|
||||
const CopyButton = ({ copyText, buttonText = 'copy', ...props }) => {
|
||||
const { hasCopied, onCopy } = useClipboard(copyText);
|
||||
|
||||
@@ -21,11 +21,11 @@ const DEFAULT_STYLE = {
|
||||
|
||||
export const IconStyleContext = createContext<ICustomIconStyle>({
|
||||
color: 'currentColor',
|
||||
setColor: (s: string) => null,
|
||||
setColor: () => null,
|
||||
strokeWidth: 2,
|
||||
setStroke: (n: number) => null,
|
||||
setStroke: () => null,
|
||||
size: 24,
|
||||
setSize: (n: number) => null,
|
||||
setSize: () => null,
|
||||
resetStyle: () => null,
|
||||
iconsRef: { current: {} },
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button, Flex, Link, WrapItem, Text, Wrap, Heading, Icon } from '@chakra-ui/react';
|
||||
import { Button, Flex, Link, WrapItem, Text, Wrap, Heading } from '@chakra-ui/react';
|
||||
import download from 'downloadjs';
|
||||
import JSZip from 'jszip';
|
||||
import { Download, Github } from 'lucide-react';
|
||||
@@ -17,7 +17,7 @@ import { useState } from 'react';
|
||||
import { useCustomizeIconContext } from './CustomizeIconContext';
|
||||
import { IconEntity } from '../types';
|
||||
|
||||
type IconContent = [icon: string, src:string]
|
||||
type IconContent = [icon: string, src:string];
|
||||
|
||||
async function generateZip(icons: IconContent[]) {
|
||||
const zip = new JSZip();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createContext, useState, useContext, useMemo, useCallback, useEffect } from 'react';
|
||||
import { createContext, useState, useContext, useMemo, useCallback } from 'react';
|
||||
|
||||
interface HeadingTypes {
|
||||
anchor: string;
|
||||
@@ -8,7 +8,7 @@ interface HeadingTypes {
|
||||
|
||||
const HeadingNavigationContext = createContext({
|
||||
headings: [],
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
|
||||
addHeading: (heading: HeadingTypes) => {},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { useHeadingNavigationContext } from './HeadingNavigationProvider';
|
||||
|
||||
const HeadingTreeMenu = () => {
|
||||
const { headings } = useHeadingNavigationContext();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const headingElements = useMemo(
|
||||
() =>
|
||||
headings.map(heading => {
|
||||
|
||||
@@ -1,39 +1,50 @@
|
||||
import { Box, Text, IconButton, useColorMode, Flex, Slide, ButtonGroup, Button, useToast, Heading, Avatar, AvatarGroup, Link, Tooltip, useMediaQuery, useDisclosure } from "@chakra-ui/react";
|
||||
import theme from "../lib/theme";
|
||||
import download from 'downloadjs';
|
||||
import copy from "copy-to-clipboard";
|
||||
import { X as Close } from 'lucide-react';
|
||||
import {useContext, useEffect, useRef} from "react";
|
||||
import {IconStyleContext} from "./CustomizeIconContext";
|
||||
import {IconWrapper} from "./IconWrapper";
|
||||
import ModifiedTooltip from "./ModifiedTooltip";
|
||||
import { IconData } from "../lib/icons";
|
||||
|
||||
type IconDownload = {
|
||||
src: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
const IconDetailOverlay = ({ open = true, close, icon }) => {
|
||||
interface IconDetailOverlayProps {
|
||||
open: boolean
|
||||
close: () => void
|
||||
icon?: IconData
|
||||
}
|
||||
|
||||
const IconDetailOverlay = ({ open = true, close, icon }: IconDetailOverlayProps) => {
|
||||
const toast = useToast();
|
||||
const { colorMode } = useColorMode();
|
||||
const { tags = [], name } = icon;
|
||||
const {color, strokeWidth, size} = useContext(IconStyleContext);
|
||||
const iconRef = useRef<SVGSVGElement>(null);
|
||||
const [isMobile] = useMediaQuery("(max-width: 560px)")
|
||||
const { isOpen, onOpen, onClose } = useDisclosure()
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
close();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if(open) {
|
||||
onOpen()
|
||||
}
|
||||
}, [open])
|
||||
|
||||
const iconStyling = (isLight) => ({
|
||||
if(icon == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
const { tags = [], name = '' } = icon;
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
close();
|
||||
};
|
||||
|
||||
const iconStyling = {
|
||||
height: "25vw",
|
||||
width: "25vw",
|
||||
minHeight: "160px",
|
||||
@@ -41,12 +52,15 @@ const IconDetailOverlay = ({ open = true, close, icon }) => {
|
||||
maxHeight: "240px",
|
||||
maxWidth: "240px",
|
||||
color: color,
|
||||
});
|
||||
};
|
||||
|
||||
const downloadIcon = ({src, name} : IconDownload) => download(src, `${name}.svg`, 'image/svg+xml');
|
||||
const downloadIcon = ({src, name = ''} : IconDownload) => download(src, `${name}.svg`, 'image/svg+xml');
|
||||
|
||||
const copyIcon = async ({src, name} : IconDownload) => {
|
||||
const trimmedSrc = src.replace(/(\r\n|\n|\r|\s\s)/gm, "")
|
||||
|
||||
await navigator.clipboard.writeText(trimmedSrc)
|
||||
|
||||
const copyIcon = ({src, name} : IconDownload) => {
|
||||
copy(src);
|
||||
toast({
|
||||
title: "Copied!",
|
||||
description: `Icon "${name}" copied to clipboard.`,
|
||||
@@ -95,7 +109,6 @@ const IconDetailOverlay = ({ open = true, close, icon }) => {
|
||||
w="full"
|
||||
px={8}
|
||||
>
|
||||
|
||||
<Box
|
||||
borderWidth="1px"
|
||||
rounded="lg"
|
||||
@@ -135,7 +148,7 @@ const IconDetailOverlay = ({ open = true, close, icon }) => {
|
||||
padding={0}
|
||||
>
|
||||
<div
|
||||
style={iconStyling(colorMode == "light")}
|
||||
style={iconStyling}
|
||||
className="icon-large"
|
||||
>
|
||||
<IconWrapper
|
||||
@@ -216,8 +229,8 @@ const IconDetailOverlay = ({ open = true, close, icon }) => {
|
||||
</Heading>
|
||||
<AvatarGroup size="md">
|
||||
{ icon.contributors.map((commit, index) => (
|
||||
<Link href={`https://github.com/${commit.author}`} isExternal key={`${index}_${commit.sha}`}>
|
||||
<Tooltip label={commit.author} key={commit.sha}>
|
||||
<Link href={`https://github.com/${commit.author}`} isExternal key={`${index}_${commit.commit}`}>
|
||||
<Tooltip label={commit.author} key={commit.commit}>
|
||||
<Avatar name={commit.author} src={`https://github.com/${commit.author}.png?size=88`} />
|
||||
</Tooltip>
|
||||
</Link>
|
||||
|
||||
@@ -1,22 +1,42 @@
|
||||
import { Button, ButtonProps, Flex, Text, useToast } from '@chakra-ui/react';
|
||||
import download from 'downloadjs';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import { memo } from 'react';
|
||||
import { Contributor } from '../lib/fetchAllContributors';
|
||||
import { useCustomizeIconContext } from './CustomizeIconContext';
|
||||
import { IconWrapper } from './IconWrapper';
|
||||
|
||||
interface IconListItemProps {
|
||||
name: string;
|
||||
content: string;
|
||||
contributors: any[];
|
||||
contributors: Contributor[]
|
||||
src: string;
|
||||
onClick?: ButtonProps['onClick'];
|
||||
onClick?: ButtonProps['onClick']
|
||||
}
|
||||
|
||||
const IconListItem = ({ name, content, onClick, src: svg }: IconListItemProps) => {
|
||||
const toast = useToast();
|
||||
const { color, size, strokeWidth, iconsRef } = useCustomizeIconContext();
|
||||
|
||||
const handleClick:ButtonProps['onClick'] = async (event) => {
|
||||
const src = (iconsRef.current[name].outerHTML ?? svg).replace(/(\r\n|\n|\r|(>\s\s<))/gm, "")
|
||||
if (event.shiftKey) {
|
||||
await navigator.clipboard.writeText(src)
|
||||
|
||||
toast({
|
||||
title: 'Copied!',
|
||||
description: `Icon "${name}" copied to clipboard.`,
|
||||
status: 'success',
|
||||
duration: 1500,
|
||||
});
|
||||
}
|
||||
if (event.altKey) {
|
||||
download(src, `${name}.svg`, 'image/svg+xml');
|
||||
}
|
||||
if (onClick) {
|
||||
onClick(event);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -26,24 +46,7 @@ const IconListItem = ({ name, content, onClick, src: svg }: IconListItemProps) =
|
||||
height={32}
|
||||
position="relative"
|
||||
whiteSpace="normal"
|
||||
onClick={event => {
|
||||
const src = iconsRef.current[name].outerHTML ?? svg
|
||||
if (event.shiftKey) {
|
||||
copy(src);
|
||||
toast({
|
||||
title: 'Copied!',
|
||||
description: `Icon "${name}" copied to clipboard.`,
|
||||
status: 'success',
|
||||
duration: 1500,
|
||||
});
|
||||
}
|
||||
if (event.altKey) {
|
||||
download(src, `${name}.\svg`, 'image/svg+xml');
|
||||
}
|
||||
if (onClick) {
|
||||
onClick(event);
|
||||
}
|
||||
}}
|
||||
onClick={handleClick}
|
||||
key={name}
|
||||
alignItems="center"
|
||||
>
|
||||
|
||||
@@ -121,6 +121,7 @@ const Layout = ({ aside, children }: LayoutProps) => {
|
||||
<Divider mb={6} mt={12} />
|
||||
<p style={{ alignSelf: 'center' }}>
|
||||
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src="/vercel.svg" alt="Powered by Vercel" width="200" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Icon, Link, Text } from '@chakra-ui/react';
|
||||
//@ts-ignore
|
||||
import LogoImage from '../../public/logo.svg';
|
||||
import NextLink from 'next/link';
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
import { useDisclosure } from '@chakra-ui/react';
|
||||
import { createContext, useState, useContext, useMemo, useCallback, useEffect } from 'react';
|
||||
import { createContext, useContext, useMemo } from 'react';
|
||||
|
||||
const MobileNavigationContext = createContext({
|
||||
isOpen: false,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Box, Tooltip, useColorMode } from "@chakra-ui/react";
|
||||
import theme from '../lib/theme';
|
||||
|
||||
const ModifiedTooltip = ({}) => {
|
||||
const ModifiedTooltip = () => {
|
||||
const { colorMode } = useColorMode();
|
||||
|
||||
return (
|
||||
|
||||
@@ -5,17 +5,31 @@ import {
|
||||
Heading,
|
||||
Text,
|
||||
useColorMode,
|
||||
Divider,
|
||||
ButtonGroup,
|
||||
Stack,
|
||||
} from '@chakra-ui/react';
|
||||
import Image from 'next/image';
|
||||
import { Code, FileText } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import { MDXRemote } from 'next-mdx-remote';
|
||||
import mdxComponents from '../lib/mdxComponents';
|
||||
|
||||
const Package = ({ name, description, image, shields, source, documentation }) => {
|
||||
interface Shield {
|
||||
alt: string
|
||||
src: string
|
||||
href: string
|
||||
}
|
||||
export interface PackageItem {
|
||||
name: string
|
||||
description: string
|
||||
image: string
|
||||
shields: Shield[]
|
||||
source: string
|
||||
documentation: string
|
||||
order: number
|
||||
private?: boolean
|
||||
flutter?: object
|
||||
}
|
||||
|
||||
|
||||
const Package = ({ name, description, image, shields, source, documentation }: PackageItem) => {
|
||||
const { colorMode } = useColorMode();
|
||||
|
||||
return (
|
||||
@@ -71,6 +85,7 @@ const Package = ({ name, description, image, shields, source, documentation }) =
|
||||
{shields.map(({ alt, src, href }, index) => (
|
||||
<Link href={href} passHref>
|
||||
<a target="_blank">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img {...{ alt, src }} key={index} />
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
@@ -2,6 +2,11 @@ import crypto from 'crypto';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
export interface Contributor {
|
||||
author: string
|
||||
commit: string
|
||||
}
|
||||
|
||||
const IGNORE_COMMIT_MESSAGES = ['fork', 'optimize'];
|
||||
|
||||
function getContentHashOfFile(path) {
|
||||
@@ -14,8 +19,7 @@ function getContentHashOfFile(path) {
|
||||
});
|
||||
}
|
||||
|
||||
const fetchCommitsOfIcon = (name) =>
|
||||
new Promise(async (resolve, reject) => {
|
||||
const fetchCommitsOfIcon = async (name) =>{
|
||||
try {
|
||||
const headers = new Headers();
|
||||
const username = 'ericfennis';
|
||||
@@ -35,22 +39,22 @@ const fetchCommitsOfIcon = (name) =>
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
resolve({
|
||||
return {
|
||||
name,
|
||||
commits: data,
|
||||
});
|
||||
};
|
||||
} catch (error) {
|
||||
|
||||
reject(error);
|
||||
throw new Error(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const filterCommits = (commits) =>
|
||||
commits.filter(({ commit }) =>
|
||||
!IGNORE_COMMIT_MESSAGES.some(ignoreItem =>
|
||||
commit.message.toLowerCase().includes(ignoreItem),
|
||||
))
|
||||
.map(({ sha, author, commit }) => ({
|
||||
.map(({ sha, author }) => ({
|
||||
author: author && author.login ? author.login : null,
|
||||
commit: sha,
|
||||
}))
|
||||
@@ -86,7 +90,7 @@ async function writeIconCache(icon, content) {
|
||||
fs.writeFileSync(iconCachePath, JSON.stringify(content), 'utf-8');
|
||||
}
|
||||
|
||||
export async function getContributors(icon) {
|
||||
export async function getContributors(icon): Promise<Contributor[]> {
|
||||
try {
|
||||
let iconCommits
|
||||
const iconCache = await checkIconCache(icon);
|
||||
@@ -94,7 +98,7 @@ export async function getContributors(icon) {
|
||||
if (iconCache) {
|
||||
iconCommits = iconCache
|
||||
} else {
|
||||
const { commits } : any = await fetchCommitsOfIcon(icon);
|
||||
const { commits } = await fetchCommitsOfIcon(icon);
|
||||
|
||||
writeIconCache(icon, commits)
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { promises as fs, constants } from 'fs';
|
||||
import path from 'path';
|
||||
import yaml from 'js-yaml'
|
||||
import { PackageItem } from '../components/Package';
|
||||
|
||||
const fileExist = (filePath) => fs.access(filePath, constants.F_OK).then(() => true).catch(() => false)
|
||||
|
||||
const fetchPackages = async () => {
|
||||
const fetchPackages = async (): Promise<PackageItem[]> => {
|
||||
const docsDir = path.resolve(process.cwd(), '../packages');
|
||||
const fileNames = await (await fs.readdir(docsDir)).map(filename => ({filename, directory: docsDir}))
|
||||
|
||||
@@ -31,8 +32,6 @@ const fetchPackages = async () => {
|
||||
return null
|
||||
}))
|
||||
|
||||
|
||||
|
||||
return packageJsons
|
||||
}
|
||||
|
||||
|
||||
+18
-9
@@ -1,8 +1,8 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import cheerio from 'cheerio';
|
||||
import { parseSync, stringify } from 'svgson';
|
||||
import tags from '../../../tags.json';
|
||||
import { getContributors } from "./fetchAllContributors";
|
||||
import { Contributor, getContributors } from "./fetchAllContributors";
|
||||
|
||||
const directory = path.join(process.cwd(), "../icons");
|
||||
|
||||
@@ -14,12 +14,13 @@ export function getAllNames() {
|
||||
});
|
||||
}
|
||||
|
||||
export async function getData(name:string) {
|
||||
export async function getData(name: string) {
|
||||
const fullPath = path.join(directory, `${name}.svg`);
|
||||
const fileContents = fs.readFileSync(fullPath, "utf8");
|
||||
const fileContent = fs.readFileSync(fullPath, "utf8");
|
||||
|
||||
const $ = cheerio.load(fileContents);
|
||||
const content = $("svg").html();
|
||||
const svgNodes = parseSync(fileContent);
|
||||
|
||||
const svgContent = svgNodes.children.map((node) => stringify(node)).join('');
|
||||
|
||||
const contributors = await getContributors(name);
|
||||
|
||||
@@ -27,12 +28,20 @@ export async function getData(name:string) {
|
||||
name,
|
||||
tags: tags[name] || [],
|
||||
contributors,
|
||||
src: fileContents,
|
||||
content: content
|
||||
src: fileContent,
|
||||
content: svgContent
|
||||
};
|
||||
}
|
||||
|
||||
export async function getAllData() {
|
||||
export interface IconData {
|
||||
name: string
|
||||
tags: string[]
|
||||
contributors: Contributor[]
|
||||
src: string
|
||||
content: string
|
||||
}
|
||||
|
||||
export async function getAllData(): Promise<IconData[]> {
|
||||
const names = getAllNames();
|
||||
|
||||
return Promise.all(names.map((name) => getData(name)));
|
||||
|
||||
@@ -10,6 +10,7 @@ export const useKeyBindings = (
|
||||
const [keyBindings] = useState(initialKeyBindings);
|
||||
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line no-undef
|
||||
document.addEventListener(
|
||||
eventListener,
|
||||
(event) => {
|
||||
@@ -28,6 +29,7 @@ export const useKeyBindings = (
|
||||
|
||||
return () =>
|
||||
Object.keys(keyBindings).forEach((keyBinding) =>
|
||||
// eslint-disable-next-line no-undef
|
||||
document.removeEventListener(eventListener, keyBindings[keyBinding])
|
||||
);
|
||||
}, []);
|
||||
|
||||
@@ -52,8 +52,7 @@ const components = {
|
||||
|
||||
return (
|
||||
<CodeBlock
|
||||
//@ts-ignore
|
||||
my={6}
|
||||
marginY={6}
|
||||
code={code}
|
||||
language={language}
|
||||
/>
|
||||
@@ -78,7 +77,7 @@ const components = {
|
||||
/>
|
||||
),
|
||||
inlineCode: InlineCode,
|
||||
hr: (props) => <Divider my={4}/>,
|
||||
hr: () => <Divider my={4} />,
|
||||
a: ({children, href, ...rest}) => {
|
||||
let link = href
|
||||
const isExternal = link.startsWith('http')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Fuse from 'fuse.js';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
const useSearch = <T>(query = '', collection: T[], keys: Fuse.FuseOptionKey[]) => {
|
||||
const useSearch = <T>(query = '', collection: T[], keys: Fuse.FuseOptionKey<T>[] = []) => {
|
||||
const index = useMemo(() => {
|
||||
return new Fuse(collection, {
|
||||
threshold: 0.2,
|
||||
|
||||
@@ -17,15 +17,6 @@ class MyDocument extends Document {
|
||||
<meta name="theme-color" content="#F56565" />
|
||||
<meta name="google-site-verification" content="pr2dEIF-6zFdjXlDxutqEokeinrQNLx5qAjeVCqASDY" />
|
||||
</Head>
|
||||
<style jsx global>{`
|
||||
* {
|
||||
-webkit-transition: none !important;
|
||||
-moz-transition: none !important;
|
||||
-o-transition: none !important;
|
||||
-ms-transition: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
`}</style>
|
||||
<body>
|
||||
<ColorModeScript />
|
||||
<Main />
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import Layout from '../../components/Layout';
|
||||
import Header from '../../components/Header';
|
||||
import Head from 'next/head';
|
||||
import fetchAllDocuments from '../../lib/fetchAllDocuments';
|
||||
|
||||
export { default } from '.';
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Box, Button, Text } from '@chakra-ui/react';
|
||||
import { ArrowRight } from 'lucide-react'
|
||||
import Link from 'next/link';
|
||||
|
||||
const DocPage = ({ doc, data, content }) => {
|
||||
const DocPage = ({ doc, data }) => {
|
||||
if (!data || !doc) return null
|
||||
|
||||
const nextPage = data.nextPage || []
|
||||
@@ -69,7 +69,7 @@ const DocPage = ({ doc, data, content }) => {
|
||||
|
||||
export default DocPage
|
||||
|
||||
export async function getStaticProps({ params }) {
|
||||
export async function getStaticProps() {
|
||||
const allDocs = await fetchAllDocuments();
|
||||
const doc = allDocs.find(({filename = ''}) => filename === 'index.md');
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ const IconPage = ({ icon, data }) => {
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<IconDetailOverlay key={currentIcon.name} icon={currentIcon} close={onClose} />
|
||||
<IconDetailOverlay key={currentIcon.name} icon={currentIcon} close={onClose} open />
|
||||
<Header {...{ data }} />
|
||||
<IconOverview {...{ data }} />
|
||||
</Layout>
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
import Layout from '../components/Layout';
|
||||
import { getAllData } from '../lib/icons';
|
||||
import { getAllData, IconData } from '../lib/icons';
|
||||
|
||||
import IconOverview from '../components/IconOverview';
|
||||
import IconDetailOverlay from '../components/IconDetailOverlay';
|
||||
import { useRouter } from 'next/router';
|
||||
import Header from '../components/Header';
|
||||
import MobileMenu from '../components/MobileMenu';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { GetStaticPropsResult, NextPage } from 'next';
|
||||
|
||||
const IndexPage = ({ data }) => {
|
||||
interface HomePageProps {
|
||||
data: IconData[]
|
||||
}
|
||||
|
||||
const HomePage: NextPage<HomePageProps> = ({ data }) => {
|
||||
const router = useRouter();
|
||||
const getIcon = iconName => data.find(({ name }) => name === iconName) || {};
|
||||
const getIcon = iconName => data.find(({ name }) => name === iconName);
|
||||
|
||||
const currentIcon = useMemo(() => {
|
||||
return getIcon(router.query.iconName)
|
||||
@@ -30,7 +35,7 @@ const IndexPage = ({ data }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export async function getStaticProps() {
|
||||
export async function getStaticProps(): Promise<GetStaticPropsResult<HomePageProps>> {
|
||||
const data = await getAllData();
|
||||
|
||||
return {
|
||||
@@ -40,4 +45,4 @@ export async function getStaticProps() {
|
||||
};
|
||||
}
|
||||
|
||||
export default IndexPage;
|
||||
export default HomePage;
|
||||
|
||||
@@ -5,9 +5,9 @@ import MobileMenu from '../components/MobileMenu';
|
||||
import { Box, Heading, Text } from '@chakra-ui/react';
|
||||
import { promises as fs } from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import { GetStaticProps, NextPage } from 'next';
|
||||
import { GetStaticProps } from 'next';
|
||||
|
||||
const LicensePage = ({ licenseText }) => {
|
||||
const LicensePage = ({ licenseText }: { licenseText: string[] }): JSX.Element => {
|
||||
return (
|
||||
<Box>
|
||||
<MobileMenu />
|
||||
@@ -43,7 +43,7 @@ const LicensePage = ({ licenseText }) => {
|
||||
|
||||
export default LicensePage;
|
||||
|
||||
export const getStaticProps: GetStaticProps = async ({ params }) => {
|
||||
export const getStaticProps: GetStaticProps = async () => {
|
||||
const doc: string = await fs.readFile(resolve('../LICENSE'), 'utf-8');
|
||||
|
||||
const licenseText = doc
|
||||
|
||||
@@ -2,12 +2,17 @@ import Layout from '../../components/Layout';
|
||||
import HeadingNavigationProvider from '../../components/HeadingNavigationProvider';
|
||||
import MobileMenu from '../../components/MobileMenu';
|
||||
import { Stack } from '@chakra-ui/react';
|
||||
import Package from '../../components/Package';
|
||||
import packagesData from '../../lib/packageData';
|
||||
import Package, { PackageItem } from '../../components/Package';
|
||||
import packagesData from '../../data/packageData';
|
||||
import { Heading } from '@chakra-ui/react';
|
||||
import fetchPackages from '../../lib/fetchPackages';
|
||||
import { GetStaticPropsResult } from 'next';
|
||||
|
||||
const PackagesPage = ({ packages }) => {
|
||||
interface PackagesPageProps {
|
||||
packages: PackageItem[]
|
||||
}
|
||||
|
||||
const PackagesPage = ({ packages }: PackagesPageProps): JSX.Element => {
|
||||
return (
|
||||
<HeadingNavigationProvider>
|
||||
<MobileMenu />
|
||||
@@ -25,11 +30,11 @@ const PackagesPage = ({ packages }) => {
|
||||
|
||||
export default PackagesPage;
|
||||
|
||||
export async function getStaticProps({ params }) {
|
||||
const packages = (await fetchPackages())
|
||||
export async function getStaticProps(): Promise<GetStaticPropsResult<PackagesPageProps>> {
|
||||
const packages: PackageItem[] = (await fetchPackages())
|
||||
.filter(Boolean)
|
||||
.filter(packageObj => !packageObj.private && packageObj.name in packagesData)
|
||||
.map(({ name, description, flutter = false }) => {
|
||||
.map(({ name, description, flutter }) => {
|
||||
const packageDirectory = flutter ? 'lucide-flutter' : name;
|
||||
|
||||
return {
|
||||
@@ -43,5 +48,8 @@ export async function getStaticProps({ params }) {
|
||||
})
|
||||
.sort((a, b) => a.order - b.order);
|
||||
|
||||
console.log(packages);
|
||||
|
||||
|
||||
return { props: { packages } };
|
||||
}
|
||||
|
||||
@@ -2,15 +2,20 @@ import { getAllData } from '../src/lib/icons';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import useSearch from '../src/lib/useSearch';
|
||||
|
||||
const keys = [
|
||||
{ name: 'name', weight: 2 },
|
||||
{ name: 'tags', weight: 1 },
|
||||
]
|
||||
|
||||
describe('Icon Overview', () => {
|
||||
it('can search filter icons', async () => {
|
||||
let allData = getAllData();
|
||||
const allData = await getAllData();
|
||||
|
||||
const { result: result1, waitForNextUpdate: wait1 } = renderHook(() => useSearch(allData, ''));
|
||||
const { result: result1 } = renderHook(() => useSearch('', allData, keys));
|
||||
expect(result1.current).toHaveLength(allData.length);
|
||||
|
||||
const { result: result2, waitForNextUpdate: wait2 } = renderHook(() =>
|
||||
useSearch(allData, 'airplay')
|
||||
useSearch(allData, 'airplay', keys)
|
||||
);
|
||||
await wait2();
|
||||
expect(result2.current).toHaveLength(2);
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { act, fireEvent, screen } from '@testing-library/react';
|
||||
import { screen, render } from '@testing-library/react';
|
||||
import Index from '../src/pages/index';
|
||||
import React from 'react';
|
||||
import { render } from './test-utils';
|
||||
import { getAllData } from '../src/lib/icons';
|
||||
|
||||
import App from '../src/pages/_app';
|
||||
|
||||
describe('App', () => {
|
||||
it('renders without crashing', () => {
|
||||
let allData = getAllData();
|
||||
it('renders without crashing', async () => {
|
||||
const allData = await getAllData();
|
||||
render(<App Component={Index} pageProps={{ data: allData }} />);
|
||||
expect(
|
||||
screen.getByText('Simply beautiful open source icons, community-sourced')
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import React from 'react';
|
||||
import { render as defaultRender } from '@testing-library/react';
|
||||
import { RouterContext } from 'next/dist/next-server/lib/router-context';
|
||||
import { NextRouter } from 'next/router';
|
||||
|
||||
export * from '@testing-library/react';
|
||||
|
||||
// --------------------------------------------------
|
||||
// Override the default test render with our own
|
||||
//
|
||||
// You can override the router mock like this:
|
||||
//
|
||||
// const { baseElement } = render(<MyComponent />, {
|
||||
// router: { pathname: '/my-custom-pathname' },
|
||||
// });
|
||||
// --------------------------------------------------
|
||||
type DefaultParams = Parameters<typeof defaultRender>;
|
||||
type RenderUI = DefaultParams[0];
|
||||
type RenderOptions = DefaultParams[1] & { router?: Partial<NextRouter> };
|
||||
|
||||
export function render(ui: RenderUI, { wrapper, router, ...options }: RenderOptions = {}) {
|
||||
if (!wrapper) {
|
||||
wrapper = ({ children }) => (
|
||||
<RouterContext.Provider value={{ ...mockRouter, ...router }}>
|
||||
{children}
|
||||
</RouterContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
return defaultRender(ui, { wrapper, ...options });
|
||||
}
|
||||
|
||||
const mockRouter: NextRouter = {
|
||||
basePath: '',
|
||||
pathname: '/',
|
||||
route: '/',
|
||||
asPath: '/',
|
||||
query: {},
|
||||
push: jest.fn(),
|
||||
replace: jest.fn(),
|
||||
reload: jest.fn(),
|
||||
back: jest.fn(),
|
||||
prefetch: jest.fn(),
|
||||
beforePopState: jest.fn(),
|
||||
events: {
|
||||
on: jest.fn(),
|
||||
off: jest.fn(),
|
||||
emit: jest.fn(),
|
||||
},
|
||||
isFallback: false,
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user