From 1b8d6c48fcddb586ca76ac129ebcb62b59955f1f Mon Sep 17 00:00:00 2001 From: rektdeckard Date: Thu, 2 Feb 2023 22:15:00 -0700 Subject: [PATCH] chore(build): move to react 18 + vite --- .gitignore | 1 + bin/fetch.js | 93 ----------- public/index.html => index.html | 11 +- package.json | 50 +++--- src/components/App/App.test.tsx | 9 -- src/components/App/index.ts | 1 + src/components/Banner/Banner.tsx | 1 - src/components/Banner/index.ts | 1 + src/components/ColorInput/ColorInput.tsx | 11 +- src/components/ColorInput/index.ts | 1 + .../ErrorBoundary/ErrorBoundary.tsx | 9 +- src/components/ErrorBoundary/index.ts | 1 + src/components/Footer/Footer.tsx | 18 +-- src/components/Footer/index.ts | 1 + src/components/Header/Header.tsx | 78 ++++------ src/components/Header/index.ts | 1 + src/components/IconGrid/DetailsPanel.tsx | 17 +- src/components/IconGrid/IconGrid.tsx | 13 +- src/components/IconGrid/IconGridItem.tsx | 14 +- src/components/IconGrid/TagCloud.tsx | 6 +- src/components/IconGrid/index.ts | 1 + src/components/Links/Links.tsx | 5 +- src/components/Links/index.ts | 1 + src/components/Notice/Notice.tsx | 15 +- src/components/Notice/index.ts | 1 + src/components/SearchInput/SearchInput.tsx | 14 +- src/components/SearchInput/index.ts | 1 + .../SettingsActions/SettingsActions.tsx | 15 +- src/components/SettingsActions/index.ts | 1 + src/components/SizeInput/SizeInput.tsx | 4 +- src/components/SizeInput/index.ts | 1 + src/components/StyleInput/StyleInput.tsx | 7 +- src/components/StyleInput/index.ts | 1 + src/components/Toolbar/Toolbar.tsx | 10 +- src/components/Toolbar/index.ts | 1 + src/index.tsx | 22 ++- src/lib/icons.ts | 4 +- src/lib/index.ts | 7 +- src/react-app-env.d.ts | 1 - src/serviceWorker.ts | 146 ------------------ src/setupTests.ts | 5 - src/state/selectors.ts | 4 +- src/utils/index.ts | 2 +- src/vite-env.d.ts | 1 + tsconfig.json | 26 ++-- vite.config.ts | 15 ++ 46 files changed, 198 insertions(+), 450 deletions(-) delete mode 100644 bin/fetch.js rename public/index.html => index.html (92%) delete mode 100644 src/components/App/App.test.tsx create mode 100644 src/components/App/index.ts create mode 100644 src/components/Banner/index.ts create mode 100644 src/components/ColorInput/index.ts create mode 100644 src/components/ErrorBoundary/index.ts create mode 100644 src/components/Footer/index.ts create mode 100644 src/components/Header/index.ts create mode 100644 src/components/IconGrid/index.ts create mode 100644 src/components/Links/index.ts create mode 100644 src/components/Notice/index.ts create mode 100644 src/components/SearchInput/index.ts create mode 100644 src/components/SettingsActions/index.ts create mode 100644 src/components/SizeInput/index.ts create mode 100644 src/components/StyleInput/index.ts create mode 100644 src/components/Toolbar/index.ts delete mode 100644 src/react-app-env.d.ts delete mode 100644 src/serviceWorker.ts delete mode 100644 src/setupTests.ts create mode 100644 src/vite-env.d.ts create mode 100644 vite.config.ts diff --git a/.gitignore b/.gitignore index 7d3cec2..9866ef4 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ # production /build +/dist # misc .DS_Store diff --git a/bin/fetch.js b/bin/fetch.js deleted file mode 100644 index bb31925..0000000 --- a/bin/fetch.js +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/env node -"use strict"; - -const fs = require("fs/promises"); -const path = require("path"); -const axios = require("axios"); -const chalk = require("chalk"); -const { Command } = require("commander"); -const { version } = require("../package.json"); - -const ICON_API_URL = "https://api.phosphoricons.com"; - -async function main() { - const program = new Command(); - program - .version(version) - .option( - "-r --release ", - "Fetch icons from Phosphor API and compile to internal data structure" - ) - .option("-p --published", "Published status of icons") - .option("-P, --no-published", "Published status of icons") - .option("-q --query ", "Fulltext search term") - .option("-n --name ", "Exact icon namee match"); - - program.parse(process.argv); - const params = new URLSearchParams(Object.entries(program.opts())).toString(); - - try { - const res = await axios.get(`${ICON_API_URL}?${params}`); - if (res.data) { - let fileString = `\ -import * as Icons from "phosphor-react"; -import { IconEntry, IconCategory } from "."; - -export const icons: ReadonlyArray = [ -`; - - res.data.icons.forEach((icon) => { - let categories = "["; - icon.searchCategories?.forEach((c) => { - categories += `IconCategory.${c.toUpperCase()},`; - }); - categories += "]"; - - fileString += `\ - { - name: "${icon.name}", - categories: ${categories}, - tags: ${JSON.stringify(["*new*", ...icon.tags])}, - Icon: Icons.${icon.name - .split("-") - .map((substr) => substr.replace(/^\w/, (c) => c.toUpperCase())) - .join("")}, - }, -`; - console.log(`${chalk.inverse.green(" DONE ")} ${icon.name}`); - }); - - fileString += ` -]; - -if (process.env.NODE_ENV === "development") { - console.log(\`\${icons.length} icons\`); -} - -export const iconCount = (icons.length * 6) - .toString() - .replace(/\B(?=(\d{3})+(?!\d))/g, ","); - -`; - - try { - await fs.writeFile( - path.join(__dirname, "../src/lib/new.ts"), - fileString - ); - console.log( - `${chalk.green(" DONE ")} ${res.data.icons.length} icons ingested` - ); - } catch (e) { - console.error(`${chalk.inverse.red(" FAIL ")} Could not write file`); - } - } else { - console.error(`${chalk.inverse.red(" FAIL ")} No data`); - } - } catch (e) { - console.error(e); - process.exit(-1); - } -} - -main(); diff --git a/public/index.html b/index.html similarity index 92% rename from public/index.html rename to index.html index 7fd8915..e67608f 100644 --- a/public/index.html +++ b/index.html @@ -3,7 +3,7 @@ Phosphor Icons - + - + - +
+ diff --git a/package.json b/package.json index 74bc167..1fecf57 100644 --- a/package.json +++ b/package.json @@ -21,46 +21,40 @@ "repository": "github:phosphor-icons/phosphor-home", "private": true, "scripts": { - "analyze": "source-map-explorer 'build/static/js/*.js'", - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject", - "fetch": "node ./bin/fetch.js", + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", "format": "prettier --write \"./src/**/*.{js,jsx,ts,tsx,json,vue}\"" }, "dependencies": { - "@phosphor-icons/core": "^1.4.5", + "@phosphor-icons/core": "^1.4.7", "file-saver": "^2.0.2", - "framer-motion": "^3.10.0", + "framer-motion": "^9.0.1", "fuse.js": "^6.4.1", - "phosphor-react": "^1.4.0", - "react": "^17.0.1", - "react-dom": "^17.0.1", + "phosphor-react": "^1.4.1", + "prop-types": "^15.8.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", "react-dropdown-select": "^4.4.2", "react-ga": "^3.1.2", "react-hotkeys-hook": "^3.2.1", - "react-scripts": "3.4.1", - "react-use": "^15.3.2", - "recoil": "^0.5.2", - "svg2png-converter": "^1.0.0", + "react-use": "^17.4.0", + "recoil": "^0.7.6", + "svg2png-converter": "^1.0.2", + "tinycolor": "^0.0.1", "tinycolor2": "^1.4.2" }, "devDependencies": { - "@testing-library/jest-dom": "^4.2.4", - "@testing-library/react": "^9.3.2", - "@testing-library/user-event": "^7.1.2", - "@types/file-saver": "^2.0.1", - "@types/jest": "^24.0.0", - "@types/node": "^12.0.0", - "@types/react": "^16.9.46", - "@types/react-dom": "^16.9.8", + "@types/file-saver": "^2.0.5", + "@types/node": "^18.11.18", + "@types/react": "^18.0.27", + "@types/react-dom": "^18.0.10", "@types/react-virtualized": "^9.21.10", - "@types/tinycolor2": "^1.4.2", - "axios": "^0.24.0", - "chalk": "^4", - "commander": "^8.3.0", - "typescript": "^3.9.6" + "@types/tinycolor2": "^1.4.3", + "@vitejs/plugin-react": "^3.1.0", + "typescript": "^4.9.5", + "vite": "^4.1.1", + "vite-plugin-svgr": "^2.4.0" }, "eslintConfig": { "extends": "react-app" diff --git a/src/components/App/App.test.tsx b/src/components/App/App.test.tsx deleted file mode 100644 index 352d7b8..0000000 --- a/src/components/App/App.test.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from "react"; -import { render } from "@testing-library/react"; -import App from "./App"; - -test("renders learn react link", () => { - const { getByText } = render(); - const linkElement = getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/src/components/App/index.ts b/src/components/App/index.ts new file mode 100644 index 0000000..8ce017e --- /dev/null +++ b/src/components/App/index.ts @@ -0,0 +1 @@ +export { default } from "./App"; diff --git a/src/components/Banner/Banner.tsx b/src/components/Banner/Banner.tsx index af87e19..66e2628 100644 --- a/src/components/Banner/Banner.tsx +++ b/src/components/Banner/Banner.tsx @@ -1,4 +1,3 @@ -import React from "react"; import { Medal } from "phosphor-react"; import ReactGA from "react-ga"; diff --git a/src/components/Banner/index.ts b/src/components/Banner/index.ts new file mode 100644 index 0000000..907b6e0 --- /dev/null +++ b/src/components/Banner/index.ts @@ -0,0 +1 @@ +export { default } from "./Banner"; diff --git a/src/components/ColorInput/ColorInput.tsx b/src/components/ColorInput/ColorInput.tsx index b98df2a..f091dbb 100644 --- a/src/components/ColorInput/ColorInput.tsx +++ b/src/components/ColorInput/ColorInput.tsx @@ -1,14 +1,15 @@ -import React, { useCallback } from "react"; +import { useCallback } from "react"; import { useRecoilState, useRecoilValue } from "recoil"; -import { iconColorAtom } from "../../state/atoms"; -import { isDarkThemeSelector } from "../../state/selectors"; -import useThrottled from "../../hooks/useThrottled"; +import { iconColorAtom } from "@/state/atoms"; +import { isDarkThemeSelector } from "@/state/selectors"; +import useThrottled from "@/hooks/useThrottled"; + import "./ColorInput.css"; type ColorInputProps = {}; -const ColorInput: React.FC = () => { +const ColorInput = (_: ColorInputProps) => { const [color, setColor] = useRecoilState(iconColorAtom); const isDark = useRecoilValue(isDarkThemeSelector); diff --git a/src/components/ColorInput/index.ts b/src/components/ColorInput/index.ts new file mode 100644 index 0000000..ea57087 --- /dev/null +++ b/src/components/ColorInput/index.ts @@ -0,0 +1 @@ +export { default } from "./ColorInput"; diff --git a/src/components/ErrorBoundary/ErrorBoundary.tsx b/src/components/ErrorBoundary/ErrorBoundary.tsx index 40bdf9b..4922981 100644 --- a/src/components/ErrorBoundary/ErrorBoundary.tsx +++ b/src/components/ErrorBoundary/ErrorBoundary.tsx @@ -1,14 +1,15 @@ -import React, { ErrorInfo } from "react"; +import { Component, ErrorInfo, ReactNode } from "react"; interface ErrorBoundaryProps { - fallback?: JSX.Element | React.ReactNode; + fallback?: JSX.Element | ReactNode; + children?: JSX.Element | ReactNode; } interface ErrorBoundaryState { errorMessage?: string; } -export default class ErrorBoundary extends React.Component< +export default class ErrorBoundary extends Component< ErrorBoundaryProps, ErrorBoundaryState > { @@ -26,7 +27,7 @@ export default class ErrorBoundary extends React.Component< console.info(info); } - render(): JSX.Element | React.ReactNode { + render(): JSX.Element | ReactNode { if (this.state.errorMessage) { return this.props.fallback ??

{this.state.errorMessage}

; } diff --git a/src/components/ErrorBoundary/index.ts b/src/components/ErrorBoundary/index.ts new file mode 100644 index 0000000..3406bab --- /dev/null +++ b/src/components/ErrorBoundary/index.ts @@ -0,0 +1 @@ +export { default } from "./ErrorBoundary"; diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx index 3112c44..89fe7b1 100644 --- a/src/components/Footer/Footer.tsx +++ b/src/components/Footer/Footer.tsx @@ -1,15 +1,15 @@ -import React from "react"; import { Coffee, Heart } from "phosphor-react"; -import uArrowUpLeft from "../../assets/u-arrow-up-left.svg"; -import markerGreen from "../../assets/marker-green.svg"; -import postIt from "../../assets/footer-mobile.svg"; -import Links from "../Links/Links"; +import Links from "@/components/Links/Links"; + +import { ReactComponent as UArrowUpLeft } from "@/assets/u-arrow-up-left.svg"; +import { ReactComponent as MarkerGreen } from "@/assets/marker-green.svg"; +import { ReactComponent as PostIt } from "@/assets/footer-mobile.svg"; import "./Footer.css"; type FooterProps = {}; -const Footer: React.FC = () => { +const Footer = (_: FooterProps) => { return (
@@ -23,7 +23,7 @@ const Footer: React.FC = () => { ?.scrollIntoView({ behavior: "smooth", block: "start" }); }} > - +
@@ -108,11 +108,11 @@ const Footer: React.FC = () => { {" "} by Mikhail Sharanda.

- +
- +
diff --git a/src/components/Footer/index.ts b/src/components/Footer/index.ts new file mode 100644 index 0000000..3738288 --- /dev/null +++ b/src/components/Footer/index.ts @@ -0,0 +1 @@ +export { default } from "./Footer"; diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index fd2cce6..a1ecd72 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -1,22 +1,22 @@ -import React from "react"; import { ArrowCircleUpRight, ArrowCircleDown } from "phosphor-react"; -import markerPurple from "../../assets/marker-purple.svg"; -import paperclips from "../../assets/paperclips-header-mobile.svg"; -import paperclipsThree from "../../assets/paperclips-header.svg"; -import tablet from "../../assets/tablet.svg"; -import tabletSpec from "../../assets/tablet-spec.svg"; -import billiardBall from "../../assets/billiard-ball.svg"; -import billiardBallSpec from "../../assets/billiard-ball-spec.svg"; -import warning from "../../assets/warning.svg"; -import warningSpec from "../../assets/warning-spec.svg"; -import cuttingMat from "../../assets/cutting-mat.svg"; -import cuttingMatSpec from "../../assets/cutting-mat-spec.svg"; -import receipt from "../../assets/receipt.svg"; -import receiptSpec from "../../assets/receipt-spec.svg"; -import calculator from "../../assets/calculator.svg"; -import calculatorSpec from "../../assets/calculator-spec.svg"; -import Links from "../Links/Links"; +import { ReactComponent as MarkerPurple } from "@/assets/marker-purple.svg"; +import { ReactComponent as PaperClips } from "@/assets/paperclips-header-mobile.svg"; +import { ReactComponent as PaperClipsThree } from "@/assets/paperclips-header.svg"; +import { ReactComponent as Tablet } from "@/assets/tablet.svg"; +import { ReactComponent as TabletSpec } from "@/assets/tablet-spec.svg"; +import { ReactComponent as BilliardBall } from "@/assets/billiard-ball.svg"; +import { ReactComponent as BilliardBallSpec } from "@/assets/billiard-ball-spec.svg"; +import { ReactComponent as Warning } from "@/assets/warning.svg"; +import { ReactComponent as WarningSpec } from "@/assets/warning-spec.svg"; +import { ReactComponent as CuttingMat } from "@/assets/cutting-mat.svg"; +import { ReactComponent as CuttingMatSpec } from "@/assets/cutting-mat-spec.svg"; +import { ReactComponent as Receipt } from "@/assets/receipt.svg"; +import { ReactComponent as ReceiptSpec } from "@/assets/receipt-spec.svg"; +import { ReactComponent as Calculator } from "@/assets/calculator.svg"; +import { ReactComponent as CalculatorSpec } from "@/assets/calculator-spec.svg"; + +import Links from "@/components/Links"; import "./Header.css"; type HeaderProps = {}; @@ -33,25 +33,21 @@ const handleScrollToIcons = () => .getElementById("toolbar") ?.scrollIntoView({ behavior: "smooth", block: "start" }); -const Header: React.FC = () => { +const Header = (_: HeaderProps) => { return (
- - - - - - - + + + + + + + - - + +

@@ -73,20 +69,12 @@ const Header: React.FC = () => {

- - - - - - + + + + + +
diff --git a/src/components/Header/index.ts b/src/components/Header/index.ts new file mode 100644 index 0000000..2764567 --- /dev/null +++ b/src/components/Header/index.ts @@ -0,0 +1 @@ +export { default } from "./Header"; diff --git a/src/components/IconGrid/DetailsPanel.tsx b/src/components/IconGrid/DetailsPanel.tsx index 8d2c88a..642e7de 100644 --- a/src/components/IconGrid/DetailsPanel.tsx +++ b/src/components/IconGrid/DetailsPanel.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useEffect } from "react"; +import React, { useRef, useEffect, CSSProperties } from "react"; import { useRecoilValue, useSetRecoilState } from "recoil"; import { useHotkeys } from "react-hotkeys-hook"; import { motion } from "framer-motion"; @@ -12,11 +12,12 @@ import { iconSizeAtom, iconColorAtom, iconPreviewOpenAtom, -} from "../../state/atoms"; -import useTransientState from "../../hooks/useTransientState"; +} from "@/state/atoms"; +import useTransientState from "@/hooks/useTransientState"; +import { IconEntry, SnippetType } from "@/lib"; +import { getCodeSnippets, supportsWeight } from "@/utils"; + import TagCloud from "./TagCloud"; -import { IconEntry, SnippetType } from "../../lib"; -import { getCodeSnippets, supportsWeight } from "../../utils"; const panelVariants = { open: { @@ -58,7 +59,7 @@ const renderedSnippets = [ SnippetType.FLUTTER, ]; -const DetailsPanel: React.FC = (props) => { +const DetailsPanel = (props: InfoPanelProps) => { const { index, spans, isDark, entry } = props; const { name, Icon, categories, tags } = entry; const weight = useRecoilValue(iconWeightAtom); @@ -78,10 +79,10 @@ const DetailsPanel: React.FC = (props) => { [name] ); - const buttonBarStyle: React.CSSProperties = { + const buttonBarStyle: CSSProperties = { color: isDark ? "white" : buttonColor, }; - const snippetButtonStyle: React.CSSProperties = + const snippetButtonStyle: CSSProperties = weight === "duotone" ? { color: disabledColor, userSelect: "none" } : { color: buttonColor }; diff --git a/src/components/IconGrid/IconGrid.tsx b/src/components/IconGrid/IconGrid.tsx index 806dcad..9b9f420 100644 --- a/src/components/IconGrid/IconGrid.tsx +++ b/src/components/IconGrid/IconGrid.tsx @@ -1,17 +1,18 @@ -import React, { useRef, useEffect } from "react"; +import { useRef, useEffect } from "react"; import { useRecoilValue } from "recoil"; import { motion, useAnimation } from "framer-motion"; import { IconContext } from "phosphor-react"; -import { iconWeightAtom, iconSizeAtom, iconColorAtom } from "../../state/atoms"; +import { iconWeightAtom, iconSizeAtom, iconColorAtom } from "@/state/atoms"; import { filteredQueryResultsSelector, isDarkThemeSelector, -} from "../../state/selectors"; -import useGridSpans from "../../hooks/useGridSpans"; +} from "@/state/selectors"; +import useGridSpans from "@/hooks/useGridSpans"; +import Notice from "@/components/Notice"; + import IconGridItem from "./IconGridItem"; import TagCloud from "./TagCloud"; -import Notice from "../Notice/Notice"; import "./IconGrid.css"; const defaultSearchTags = [ @@ -26,7 +27,7 @@ const defaultSearchTags = [ type IconGridProps = {}; -const IconGrid: React.FC = () => { +const IconGrid = (_: IconGridProps) => { const weight = useRecoilValue(iconWeightAtom); const size = useRecoilValue(iconSizeAtom); const color = useRecoilValue(iconColorAtom); diff --git a/src/components/IconGrid/IconGridItem.tsx b/src/components/IconGrid/IconGridItem.tsx index 9ef19f3..c82a367 100644 --- a/src/components/IconGrid/IconGridItem.tsx +++ b/src/components/IconGrid/IconGridItem.tsx @@ -1,15 +1,11 @@ -import React, { - useRef, - useLayoutEffect, - useEffect, - MutableRefObject, -} from "react"; +import { useRef, useLayoutEffect, useEffect, MutableRefObject } from "react"; import { useRecoilState } from "recoil"; import { motion, AnimatePresence } from "framer-motion"; -import { iconPreviewOpenAtom } from "../../state/atoms"; +import { IconEntry } from "@/lib"; +import { iconPreviewOpenAtom } from "@/state/atoms"; + import DetailsPanel from "./DetailsPanel"; -import { IconEntry } from "../../lib"; interface IconGridItemProps { index: number; @@ -31,7 +27,7 @@ const itemVariants = { }), }; -const IconGridItem: React.FC = (props) => { +const IconGridItem = (props: IconGridItemProps) => { const { index, originOffset, entry } = props; const { name, Icon } = entry; const [open, setOpen] = useRecoilState(iconPreviewOpenAtom); diff --git a/src/components/IconGrid/TagCloud.tsx b/src/components/IconGrid/TagCloud.tsx index 85121f8..e2ab621 100644 --- a/src/components/IconGrid/TagCloud.tsx +++ b/src/components/IconGrid/TagCloud.tsx @@ -1,7 +1,7 @@ -import React, { useCallback } from "react"; +import { useCallback } from "react"; import { useSetRecoilState } from "recoil"; -import { searchQueryAtom } from "../../state/atoms"; +import { searchQueryAtom } from "@/state/atoms"; import "./TagCloud.css"; interface TagCloudProps { @@ -10,7 +10,7 @@ interface TagCloudProps { isDark: boolean; } -const TagCloud: React.FC = ({ name, tags, isDark }) => { +const TagCloud = ({ name, tags, isDark }: TagCloudProps) => { const setQuery = useSetRecoilState(searchQueryAtom); const handleTagClick = useCallback( (tag: string) => { diff --git a/src/components/IconGrid/index.ts b/src/components/IconGrid/index.ts new file mode 100644 index 0000000..4078c88 --- /dev/null +++ b/src/components/IconGrid/index.ts @@ -0,0 +1 @@ +export { default } from "./IconGrid"; diff --git a/src/components/Links/Links.tsx b/src/components/Links/Links.tsx index 8c8b41e..c4d458b 100644 --- a/src/components/Links/Links.tsx +++ b/src/components/Links/Links.tsx @@ -1,14 +1,13 @@ -import React from "react"; import { OutboundLink } from "react-ga"; import { ArrowElbowDownRight } from "phosphor-react"; -import { iconCount } from "../../lib/icons"; +import { iconCount } from "@/lib/icons"; import "./Links.css"; interface LinksProps {} -const Links: React.FC = () => { +const Links = (_: LinksProps) => { return (
diff --git a/src/components/Links/index.ts b/src/components/Links/index.ts new file mode 100644 index 0000000..4ca5192 --- /dev/null +++ b/src/components/Links/index.ts @@ -0,0 +1 @@ +export { default } from "./Links"; diff --git a/src/components/Notice/Notice.tsx b/src/components/Notice/Notice.tsx index d54078b..43f5082 100644 --- a/src/components/Notice/Notice.tsx +++ b/src/components/Notice/Notice.tsx @@ -1,21 +1,18 @@ -import React from "react"; +import { ReactNode } from "react"; import { motion } from "framer-motion"; import { useRecoilValue } from "recoil"; - -import { isDarkThemeSelector } from "../../state/selectors"; -import { searchQueryAtom } from "../../state/atoms"; import { HourglassMedium, Question, SmileyXEyes } from "phosphor-react"; +import { isDarkThemeSelector } from "@/state/selectors"; +import { searchQueryAtom } from "@/state/atoms"; + interface NoticeProps { message?: string; type?: "wait" | "help" | "warn" | "none"; + children?: ReactNode; } -const Notice: React.FC = ({ - message, - type = "warn", - children, -}) => { +const Notice = ({ message, type = "warn", children }: NoticeProps) => { const isDark = useRecoilValue(isDarkThemeSelector); const query = useRecoilValue(searchQueryAtom); diff --git a/src/components/Notice/index.ts b/src/components/Notice/index.ts new file mode 100644 index 0000000..d9ef138 --- /dev/null +++ b/src/components/Notice/index.ts @@ -0,0 +1 @@ +export { default } from "./Notice"; diff --git a/src/components/SearchInput/SearchInput.tsx b/src/components/SearchInput/SearchInput.tsx index 25ec8d6..e886447 100644 --- a/src/components/SearchInput/SearchInput.tsx +++ b/src/components/SearchInput/SearchInput.tsx @@ -1,11 +1,17 @@ -import React, { useState, useEffect, useRef, MutableRefObject } from "react"; +import { + useState, + useEffect, + useRef, + MutableRefObject, + ReactNode, +} from "react"; import { useRecoilState } from "recoil"; import { useDebounce } from "react-use"; import { useHotkeys } from "react-hotkeys-hook"; import { Command, MagnifyingGlass, X, HourglassHigh } from "phosphor-react"; import ReactGA from "react-ga"; -import { searchQueryAtom } from "../../state/atoms"; +import { searchQueryAtom } from "@/state/atoms"; import "./SearchInput.css"; const apple = /iPhone|iPod|iPad|Macintosh|MacIntel|MacPPC/i; @@ -16,7 +22,7 @@ const isMobile = mobile.test(window.navigator.userAgent); type SearchInputProps = {}; -const SearchInput: React.FC = () => { +const SearchInput = (_: SearchInputProps) => { const [value, setValue] = useState(""); const [query, setQuery] = useRecoilState(searchQueryAtom); const inputRef = @@ -94,7 +100,7 @@ const SearchInput: React.FC = () => { ); }; -const Keys: React.FC<{}> = ({ children }) => ( +const Keys = ({ children }: { children?: ReactNode }) => (
{children}
); diff --git a/src/components/SearchInput/index.ts b/src/components/SearchInput/index.ts new file mode 100644 index 0000000..1a2fa40 --- /dev/null +++ b/src/components/SearchInput/index.ts @@ -0,0 +1 @@ +export { default } from "./SearchInput"; diff --git a/src/components/SettingsActions/SettingsActions.tsx b/src/components/SettingsActions/SettingsActions.tsx index f7ef981..ea9b34e 100644 --- a/src/components/SettingsActions/SettingsActions.tsx +++ b/src/components/SettingsActions/SettingsActions.tsx @@ -1,12 +1,13 @@ -import React from "react"; -import { ArrowCounterClockwise, CheckCircle, Link } from "phosphor-react"; import { useRecoilValue, useResetRecoilState } from "recoil"; -import { iconWeightAtom, iconSizeAtom, iconColorAtom } from "../../state/atoms"; -import "./SettingsActions.css"; -import useTransientState from "../../hooks/useTransientState"; -import { resetSettingsSelector } from "../../state/selectors"; +import { ArrowCounterClockwise, CheckCircle, Link } from "phosphor-react"; -const SettingsActions: React.FC = () => { +import { iconWeightAtom, iconSizeAtom, iconColorAtom } from "@/state/atoms"; +import useTransientState from "@/hooks/useTransientState"; +import { resetSettingsSelector } from "@/state/selectors"; + +import "./SettingsActions.css"; + +const SettingsActions = () => { const weight = useRecoilValue(iconWeightAtom); const size = useRecoilValue(iconSizeAtom); const color = useRecoilValue(iconColorAtom); diff --git a/src/components/SettingsActions/index.ts b/src/components/SettingsActions/index.ts new file mode 100644 index 0000000..1e0e12e --- /dev/null +++ b/src/components/SettingsActions/index.ts @@ -0,0 +1 @@ +export { default } from "./SettingsActions"; diff --git a/src/components/SizeInput/SizeInput.tsx b/src/components/SizeInput/SizeInput.tsx index d8e37be..071077f 100644 --- a/src/components/SizeInput/SizeInput.tsx +++ b/src/components/SizeInput/SizeInput.tsx @@ -1,7 +1,7 @@ import React, { useCallback } from "react"; import { useRecoilState } from "recoil"; -import { iconSizeAtom } from "../../state/atoms"; +import { iconSizeAtom } from "@/state/atoms"; import "./SizeInput.css"; type SizeInputProps = {}; @@ -14,7 +14,7 @@ const handleBlur = (event: React.UIEvent) => { event.currentTarget.blur(); }; -const SizeInput: React.FC = () => { +const SizeInput = (_: SizeInputProps) => { const [size, setSize] = useRecoilState(iconSizeAtom); const handleSizeChange = useCallback( diff --git a/src/components/SizeInput/index.ts b/src/components/SizeInput/index.ts new file mode 100644 index 0000000..eb5f6d1 --- /dev/null +++ b/src/components/SizeInput/index.ts @@ -0,0 +1 @@ +export { default } from "./SizeInput"; diff --git a/src/components/StyleInput/StyleInput.tsx b/src/components/StyleInput/StyleInput.tsx index cd52a5c..f2843ff 100644 --- a/src/components/StyleInput/StyleInput.tsx +++ b/src/components/StyleInput/StyleInput.tsx @@ -1,10 +1,11 @@ -import React, { useMemo } from "react"; +import { useMemo } from "react"; import { useRecoilState } from "recoil"; import Select from "react-dropdown-select"; import { PencilLine } from "phosphor-react"; import { IconStyle } from "@phosphor-icons/core"; -import { iconWeightAtom } from "../../state/atoms"; +import { iconWeightAtom } from "@/state/atoms"; + import "./StyleInput.css"; type WeightOption = { key: string; value: IconStyle; icon: JSX.Element }; @@ -44,7 +45,7 @@ const options: WeightOption[] = [ type StyleInputProps = {}; -const StyleInput: React.FC = () => { +const StyleInput = (_: StyleInputProps) => { const [style, setStyle] = useRecoilState(iconWeightAtom); const currentStyle = useMemo( diff --git a/src/components/StyleInput/index.ts b/src/components/StyleInput/index.ts new file mode 100644 index 0000000..4f7b186 --- /dev/null +++ b/src/components/StyleInput/index.ts @@ -0,0 +1 @@ +export { default } from "./StyleInput"; diff --git a/src/components/Toolbar/Toolbar.tsx b/src/components/Toolbar/Toolbar.tsx index ac3c17f..417b33c 100644 --- a/src/components/Toolbar/Toolbar.tsx +++ b/src/components/Toolbar/Toolbar.tsx @@ -1,11 +1,11 @@ import React from "react"; +import StyleInput from "@/components/StyleInput"; +import SearchInput from "@/components/SearchInput"; +import SizeInput from "@/components/SizeInput"; +import ColorInput from "@/components/ColorInput"; +import SettingsActions from "@/components/SettingsActions"; import "./Toolbar.css"; -import StyleInput from "../StyleInput/StyleInput"; -import SearchInput from "../SearchInput/SearchInput"; -import SizeInput from "../SizeInput/SizeInput"; -import ColorInput from "../ColorInput/ColorInput"; -import SettingsActions from "../SettingsActions/SettingsActions"; type ToolbarProps = {}; diff --git a/src/components/Toolbar/index.ts b/src/components/Toolbar/index.ts new file mode 100644 index 0000000..cec4f38 --- /dev/null +++ b/src/components/Toolbar/index.ts @@ -0,0 +1 @@ +export { default } from "./Toolbar"; diff --git a/src/index.tsx b/src/index.tsx index 9a0e59d..dbab96d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,27 +1,23 @@ -import React from "react"; -import ReactDOM from "react-dom"; +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; import { RecoilRoot } from "recoil"; -import * as serviceWorker from "./serviceWorker"; -import App from "./components/App/App"; +import App from "./components/App"; import ReactGA from "react-ga"; ReactGA.initialize("UA-179205759-1", { titleCase: false }); ReactGA.pageview(window.location.pathname); -ReactDOM.render( - +const container = document.getElementById("root"); +const root = createRoot(container!); + +root.render( + - , - document.getElementById("root") + ); -// If you want your app to work offline and load faster, you can change -// unregister() to register() below. Note this comes with some pitfalls. -// Learn more about service workers: https://bit.ly/CRA-PWA -serviceWorker.unregister(); - console.log(` %c sphorphosphor %co%cspho diff --git a/src/lib/icons.ts b/src/lib/icons.ts index 92639b3..ec1d758 100644 --- a/src/lib/icons.ts +++ b/src/lib/icons.ts @@ -4,9 +4,7 @@ import { icons as iconData } from "@phosphor-icons/core"; import { IconEntry } from "."; export const icons: ReadonlyArray = iconData.map((entry) => ({ - name: entry.name, - categories: entry.categories, - tags: entry.tags, + ...entry, Icon: Icons[entry.pascal_name as keyof typeof Icons] as Icons.Icon, })); diff --git a/src/lib/index.ts b/src/lib/index.ts index fece71e..86e3b58 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1,10 +1,7 @@ import { Icon } from "phosphor-react"; -import { IconCategory } from "@phosphor-icons/core"; +import { IconEntry as CoreEntry } from "@phosphor-icons/core"; -export interface IconEntry { - name: string; - categories: IconCategory[]; - tags: string[]; +export interface IconEntry extends CoreEntry { Icon: Icon; } diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts deleted file mode 100644 index 6431bc5..0000000 --- a/src/react-app-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/src/serviceWorker.ts b/src/serviceWorker.ts deleted file mode 100644 index e7b8199..0000000 --- a/src/serviceWorker.ts +++ /dev/null @@ -1,146 +0,0 @@ -// This optional code is used to register a service worker. -// register() is not called by default. - -// This lets the app load faster on subsequent visits in production, and gives -// it offline capabilities. However, it also means that developers (and users) -// will only see deployed updates on subsequent visits to a page, after all the -// existing tabs open on the page have been closed, since previously cached -// resources are updated in the background. - -// To learn more about the benefits of this model and instructions on how to -// opt-in, read https://bit.ly/CRA-PWA - -const isLocalhost = Boolean( - window.location.hostname === "localhost" || - // [::1] is the IPv6 localhost address. - window.location.hostname === "[::1]" || - // 127.0.0.0/8 are considered localhost for IPv4. - window.location.hostname.match( - /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ - ) -); - -type Config = { - onSuccess?: (registration: ServiceWorkerRegistration) => void; - onUpdate?: (registration: ServiceWorkerRegistration) => void; -}; - -export function register(config?: Config) { - if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) { - // The URL constructor is available in all browsers that support SW. - const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); - if (publicUrl.origin !== window.location.origin) { - // Our service worker won't work if PUBLIC_URL is on a different origin - // from what our page is served on. This might happen if a CDN is used to - // serve assets; see https://github.com/facebook/create-react-app/issues/2374 - return; - } - - window.addEventListener("load", () => { - const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; - - if (isLocalhost) { - // This is running on localhost. Let's check if a service worker still exists or not. - checkValidServiceWorker(swUrl, config); - - // Add some additional logging to localhost, pointing developers to the - // service worker/PWA documentation. - navigator.serviceWorker.ready.then(() => { - console.log( - "This web app is being served cache-first by a service " + - "worker. To learn more, visit https://bit.ly/CRA-PWA" - ); - }); - } else { - // Is not localhost. Just register service worker - registerValidSW(swUrl, config); - } - }); - } -} - -function registerValidSW(swUrl: string, config?: Config) { - navigator.serviceWorker - .register(swUrl) - .then((registration) => { - registration.onupdatefound = () => { - const installingWorker = registration.installing; - if (installingWorker == null) { - return; - } - installingWorker.onstatechange = () => { - if (installingWorker.state === "installed") { - if (navigator.serviceWorker.controller) { - // At this point, the updated precached content has been fetched, - // but the previous service worker will still serve the older - // content until all client tabs are closed. - console.log( - "New content is available and will be used when all " + - "tabs for this page are closed. See https://bit.ly/CRA-PWA." - ); - - // Execute callback - if (config && config.onUpdate) { - config.onUpdate(registration); - } - } else { - // At this point, everything has been precached. - // It's the perfect time to display a - // "Content is cached for offline use." message. - console.log("Content is cached for offline use."); - - // Execute callback - if (config && config.onSuccess) { - config.onSuccess(registration); - } - } - } - }; - }; - }) - .catch((error) => { - console.error("Error during service worker registration:", error); - }); -} - -function checkValidServiceWorker(swUrl: string, config?: Config) { - // Check if the service worker can be found. If it can't reload the page. - fetch(swUrl, { - headers: { "Service-Worker": "script" }, - }) - .then((response) => { - // Ensure service worker exists, and that we really are getting a JS file. - const contentType = response.headers.get("content-type"); - if ( - response.status === 404 || - (contentType != null && contentType.indexOf("javascript") === -1) - ) { - // No service worker found. Probably a different app. Reload the page. - navigator.serviceWorker.ready.then((registration) => { - registration.unregister().then(() => { - window.location.reload(); - }); - }); - } else { - // Service worker found. Proceed as normal. - registerValidSW(swUrl, config); - } - }) - .catch(() => { - console.log( - "No internet connection found. App is running in offline mode." - ); - }); -} - -export function unregister() { - if ("serviceWorker" in navigator) { - navigator.serviceWorker.ready - .then((registration) => { - registration.unregister(); - }) - .catch((error) => { - console.error(error.message); - }); - } -} diff --git a/src/setupTests.ts b/src/setupTests.ts deleted file mode 100644 index 5fdf001..0000000 --- a/src/setupTests.ts +++ /dev/null @@ -1,5 +0,0 @@ -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import "@testing-library/jest-dom/extend-expect"; diff --git a/src/state/selectors.ts b/src/state/selectors.ts index 157aa2a..7f43e8b 100644 --- a/src/state/selectors.ts +++ b/src/state/selectors.ts @@ -9,8 +9,8 @@ import { iconSizeAtom, iconColorAtom, } from "./atoms"; -import { IconEntry } from "../lib"; -import { icons } from "../lib/icons"; +import { IconEntry } from "@/lib"; +import { icons } from "@/lib/icons"; const fuse = new Fuse(icons, { keys: [{ name: "name", weight: 4 }, "tags", "categories"], diff --git a/src/utils/index.ts b/src/utils/index.ts index 60b3979..87d1497 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,6 +1,6 @@ import { IconStyle } from "@phosphor-icons/core"; -import { SnippetType } from "../lib"; +import { SnippetType } from "@/lib"; export function getCodeSnippets({ name, diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..d816124 --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/tsconfig.json b/tsconfig.json index cf0f8ac..4cf3ce0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,23 +1,22 @@ { "compilerOptions": { - "target": "es5", - "lib": [ - "es6", - "dom", - "dom.iterable", - "esnext" - ], + "baseUrl": "./", + "paths": { + "@/*": ["./src/*"] + }, + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "target": "ESNext", "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "strict": true, "forceConsistentCasingInFileNames": true, - "module": "esnext", + "module": "ESNext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "react", + "jsx": "react-jsx", "sourceMap": true, "declaration": true, "noUnusedLocals": true, @@ -26,11 +25,6 @@ "noFallthroughCasesInSwitch": true, "noEmit": true }, - "include": [ - "src" - ], - "exclude": [ - "node_modules", - "build" - ] + "include": ["src"], + "exclude": ["node_modules", "build"] } diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..f224d2b --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,15 @@ +import path from "path"; +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import svgr from 'vite-plugin-svgr' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react(), svgr()], + resolve: { + alias: { + "~": path.resolve(__dirname, "./public"), + "@": path.resolve(__dirname, "./src"), + }, + }, +});