16 Commits

Author SHA1 Message Date
rektdeckard
4d7f5ea100 chore(lib): Bump version to v1.4.0 2021-12-23 03:55:37 -05:00
rektdeckard
3cdcdd4e0d chore(grid): add some types 2021-12-22 01:25:58 -05:00
rektdeckard
c090531800 feat(icons): update icons and tags to 1.4 2021-12-22 01:25:31 -05:00
rektdeckard
887617e523 feat(bin): update fetch script to take CLI args 2021-12-12 00:27:14 -05:00
rektdeckard
e242bcc660 feat(bin): add icon db fetch script 2021-12-05 22:58:42 -05:00
rektdeckard
22b69c3ae6 chore(links): ignore pointer events on link underlines 2021-12-03 19:58:04 -05:00
rektdeckard
e4b99d2ca9 chore(app): better ux for settings actions 2021-11-29 21:14:17 -05:00
rektdeckard
14d8c9d0e7 feat(app): add persistence of settings across sessions 2021-11-26 22:03:54 -05:00
rektdeckard
cdcf38466e feat(ui): add callouts for new and updated icons 2021-11-26 22:03:01 -05:00
rektdeckard
f256109ba4 chore(deps): upgrade recoil 2021-11-26 22:01:20 -05:00
Tobias Fried
bcff9fecb3 chore(readme): typo 2021-10-20 20:21:48 -04:00
Tobias Fried
a218b632ba chore(readme): add community projects 2021-10-20 20:17:13 -04:00
rektdeckard
56dd2ba514 feat(external): Add Sketch plugin link 2021-07-09 23:03:32 -04:00
rektdeckard
f5089e1c60 chore(readme): Update icon count 2021-07-08 02:48:43 -04:00
rektdeckard
77d93e4038 chore(readme): Revisions for v1.3.1 2021-07-08 02:09:33 -04:00
rektdeckard
b6e2ae7da5 feat(deps): Update to phosphor-react@1.3.1 and add icon entries 2021-07-05 22:46:47 -04:00
51 changed files with 2776 additions and 1836 deletions

View File

@@ -4,7 +4,7 @@
Phosphor is a flexible icon family for interfaces, diagrams, presentations — whatever, really. Phosphor is a flexible icon family for interfaces, diagrams, presentations — whatever, really.
- 772 icons and counting - 1047 icons and counting
- 6 weights: **Thin**, **Light**, **Regular**, **Bold**, **Fill**, and **Duotone** - 6 weights: **Thin**, **Light**, **Regular**, **Bold**, **Fill**, and **Duotone**
- Designed at 16 x 16px to read well small and scale up big - Designed at 16 x 16px to read well small and scale up big
- Raw stroke information retained to fine-tune the style - Raw stroke information retained to fine-tune the style
@@ -88,7 +88,7 @@ ReactDOM.render(<App />, document.getElementById("root"));
> **Note:** Due to possible namespace collisions with built-in HTML elements, compononent names in the Vue library are prefixed with `Ph`, but otherwise follow the same naming conventions. Both Pascal and kebab-case conventions can be used in templates. > **Note:** Due to possible namespace collisions with built-in HTML elements, compononent names in the Vue library are prefixed with `Ph`, but otherwise follow the same naming conventions. Both Pascal and kebab-case conventions can be used in templates.
## Related Projects ## Our Related Projects
- [phosphor-react](https://github.com/phosphor-icons/phosphor-react) ▲ Phosphor icon component library for React - [phosphor-react](https://github.com/phosphor-icons/phosphor-react) ▲ Phosphor icon component library for React
- [phosphor-vue](https://github.com/phosphor-icons/phosphor-vue) ▲ Phosphor icon component library for Vue - [phosphor-vue](https://github.com/phosphor-icons/phosphor-vue) ▲ Phosphor icon component library for Vue
@@ -96,6 +96,16 @@ ReactDOM.render(<App />, document.getElementById("root"));
- [phosphor-flutter](https://github.com/phosphor-icons/phosphor-flutter) ▲ Phosphor IconData library for Flutter - [phosphor-flutter](https://github.com/phosphor-icons/phosphor-flutter) ▲ Phosphor IconData library for Flutter
- [phosphor-webcomponents](https://github.com/phosphor-icons/phosphor-webcomponents) ▲ Phosphor icons as Web Components - [phosphor-webcomponents](https://github.com/phosphor-icons/phosphor-webcomponents) ▲ Phosphor icons as Web Components
- [phosphor-figma](https://github.com/phosphor-icons/phosphor-figma) ▲ Phosphor icons Figma plugin - [phosphor-figma](https://github.com/phosphor-icons/phosphor-figma) ▲ Phosphor icons Figma plugin
- [phosphor-sketch](https://github.com/phosphor-icons/phosphor-sketch) ▲ Phosphor icons Sketch plugin
## Community Projects
- [phosphor-react-native](https://github.com/duongdev/phosphor-react-native) ▲ Phosphor icon component library for React Native
- [phosphor-svelte](https://github.com/haruaki07/phosphor-svelte) ▲ Phosphor icons for Svelte apps
- [phosphor-r](https://github.com/dreamRs/phosphoricons) ▲ Phosphor icon wrapper for R documents and applications
- [blade-phosphor-icons](https://github.com/codeat3/blade-phosphor-icons) ▲ Phosphor icons in your Laravel Blade views
If you've made a port of Phosphor and you want to see it here, just open a PR [here](https://github.com/phosphor-icons/phosphor-home)!
## License ## License

93
bin/fetch.js Normal file
View File

@@ -0,0 +1,93 @@
#!/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 <version>",
"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 <text>", "Fulltext search term")
.option("-n --name <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<IconEntry> = [
`;
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();

View File

@@ -1,6 +1,6 @@
{ {
"name": "phosphor-home", "name": "phosphor-home",
"version": "1.2.1", "version": "1.4.0",
"license": "MIT", "license": "MIT",
"homepage": "https://phosphoricons.com", "homepage": "https://phosphoricons.com",
"author": { "author": {
@@ -20,14 +20,20 @@
], ],
"repository": "github:phosphor-icons/phosphor-home", "repository": "github:phosphor-icons/phosphor-home",
"private": true, "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",
"format": "prettier --write \"./src/**/*.{js,jsx,ts,tsx,json,vue}\""
},
"dependencies": { "dependencies": {
"@types/braintree-web-drop-in": "^1.22.3",
"axios": "^0.21.1",
"braintree-web-drop-in": "^1.30.1",
"file-saver": "^2.0.2", "file-saver": "^2.0.2",
"framer-motion": "^3.10.0", "framer-motion": "^3.10.0",
"fuse.js": "^6.4.1", "fuse.js": "^6.4.1",
"phosphor-react": "^1.2.1", "phosphor-react": "^1.4.0",
"react": "^17.0.1", "react": "^17.0.1",
"react-dom": "^17.0.1", "react-dom": "^17.0.1",
"react-dropdown-select": "^4.4.2", "react-dropdown-select": "^4.4.2",
@@ -35,19 +41,25 @@
"react-hotkeys-hook": "^3.2.1", "react-hotkeys-hook": "^3.2.1",
"react-scripts": "3.4.1", "react-scripts": "3.4.1",
"react-use": "^15.3.2", "react-use": "^15.3.2",
"recoil": "^0.1.3", "recoil": "^0.5.2",
"svg2png-converter": "^1.0.0", "svg2png-converter": "^1.0.0",
"tinycolor2": "^1.4.2" "tinycolor2": "^1.4.2"
}, },
"scripts": { "devDependencies": {
"analyze": "source-map-explorer 'build/static/js/*.js'", "@testing-library/jest-dom": "^4.2.4",
"start": "react-scripts start", "@testing-library/react": "^9.3.2",
"build": "react-scripts build", "@testing-library/user-event": "^7.1.2",
"test": "react-scripts test", "@types/file-saver": "^2.0.1",
"eject": "react-scripts eject", "@types/jest": "^24.0.0",
"predeploy": "npm run build", "@types/node": "^12.0.0",
"deploy": "gh-pages -d build", "@types/react": "^16.9.46",
"format": "prettier --write \"./src/**/*.{js,jsx,ts,tsx,json,vue}\"" "@types/react-dom": "^16.9.8",
"@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"
}, },
"eslintConfig": { "eslintConfig": {
"extends": "react-app" "extends": "react-app"
@@ -63,19 +75,5 @@
"last 1 firefox version", "last 1 firefox version",
"last 1 safari version" "last 1 safari version"
] ]
},
"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/react-list": "^0.8.5",
"@types/react-virtualized": "^9.21.10",
"@types/tinycolor2": "^1.4.2",
"typescript": "^3.9.6"
} }
} }

View File

@@ -18,6 +18,13 @@
"sizes": "512x512" "sizes": "512x512"
} }
], ],
"permissions": [
"http://*/*",
"https://*/*",
"clipboardRead",
"clipboardWrite",
"storage"
],
"start_url": ".", "start_url": ".",
"display": "standalone", "display": "standalone",
"theme_color": "#35313D", "theme_color": "#35313D",

View File

@@ -49,7 +49,6 @@ button {
justify-content: flex-start; justify-content: flex-start;
} }
input.main-button,
button.main-button { button.main-button {
height: 64px; height: 64px;
padding: 0 48px 0 40px; padding: 0 48px 0 40px;
@@ -73,29 +72,19 @@ button.main-button {
margin: 0 24px 24px 0; margin: 0 24px 24px 0;
} }
input.main-button:active,
button.main-button:active { button.main-button:active {
transform: translate(4px, 4px); transform: translate(4px, 4px);
box-shadow: 0 0 0 0 black; box-shadow: 0 0 0 0 black;
} }
input.main-button:focus,
button.main-button:focus { button.main-button:focus {
outline: none; outline: none;
} }
input.main-button:disabled,
button.main-button:disabled {
border: 2px solid gray;
box-shadow: 4px 4px 0 0 gray;
cursor: not-allowed;
}
/* button.main-button:not(:last-child) { /* button.main-button:not(:last-child) {
margin: 0 24px 24px 0; margin: 0 24px 24px 0;
} */ } */
input.main-button,
button.main-button svg { button.main-button svg {
margin-right: 12px; margin-right: 12px;
} }
@@ -120,60 +109,22 @@ a.main-link:after {
width: 100%; width: 100%;
border-bottom: 1px solid black; border-bottom: 1px solid black;
transition: 0.2s; transition: 0.2s;
pointer-events: none;
} }
a.main-link:hover:after { a.main-link:hover:after {
width: 0%; width: 0%;
} }
button.text-button { .badge.new {
display: flex; color: #ff6e60;
align-items: center;
padding: 8px;
background-color: transparent;
font-size: 16px;
cursor: pointer;
border-radius: 8px;
} }
button.text-button svg { .badge.updated {
margin: 8px; color: #397fff;
} }
.radio-group { .badge {
display: flex; font-size: 24px;
flex-flow: row wrap; line-height: 0.5em;
justify-content: space-around;
row-gap: 8px;
}
.radio-button input[type="radio"] {
display: none;
}
.radio-button label {
display: inline-block;
padding: 8px;
min-width: 50px;
background-color: white;
border-radius: 8px;
border: 2px solid black;
font-weight: bold;
cursor: pointer;
text-align: center;
user-select: none;
}
.radio-button label:hover {
background-color: #E0E0E0;
}
.radio-button input[type="radio"]:checked + label {
color: white;
background-color: black;
}
.radio-button label input {
margin-left: 8px;
} }

View File

@@ -1,23 +1,21 @@
import React, { Suspense } from "react"; import React, { Suspense } from "react";
import Header from "components/Header";
import Modal from "components/Modal";
import Toolbar from "components/Toolbar";
import IconGrid from "components/IconGrid";
import Footer from "components/Footer";
import ErrorBoundary from "components/ErrorBoundary";
import Notice from "components/Notice";
import useIconParameters from "hooks/useIconParameters";
import "./App.css"; import "./App.css";
import Header from "../Header/Header";
import Toolbar from "../Toolbar/Toolbar";
import IconGrid from "../IconGrid/IconGrid";
import Footer from "../Footer/Footer";
import ErrorBoundary from "../ErrorBoundary/ErrorBoundary";
import Notice from "../Notice/Notice";
import useIconParameters from "../../hooks/useIconParameters";
import usePersistSettings from "../../hooks/usePersistSettings";
const errorFallback = <Notice message="Search error" />; const errorFallback = <Notice message="Search error" />;
const paymentFallback = <Notice message="Could not connect to payments" />;
const waitingFallback = <Notice type="none" message="" />; const waitingFallback = <Notice type="none" message="" />;
const App: React.FC<any> = () => { const App: React.FC<any> = () => {
useIconParameters(); useIconParameters();
usePersistSettings();
return ( return (
<React.StrictMode> <React.StrictMode>
@@ -30,9 +28,6 @@ const App: React.FC<any> = () => {
</Suspense> </Suspense>
</ErrorBoundary> </ErrorBoundary>
</main> </main>
<Suspense fallback={paymentFallback}>
<Modal />
</Suspense>
<Footer /> <Footer />
</React.StrictMode> </React.StrictMode>
); );

View File

@@ -1,2 +0,0 @@
import App from "./App";
export default App;

View File

@@ -1,10 +1,9 @@
import React, { useCallback } from "react"; import React, { useCallback } from "react";
import { useRecoilState, useRecoilValue } from "recoil"; import { useRecoilState, useRecoilValue } from "recoil";
import { iconColorAtom } from "state/atoms"; import { iconColorAtom } from "../../state/atoms";
import { isDarkThemeSelector } from "state/selectors"; import { isDarkThemeSelector } from "../../state/selectors";
import useThrottled from "hooks/useThrottled"; import useThrottled from "../../hooks/useThrottled";
import "./ColorInput.css"; import "./ColorInput.css";
type ColorInputProps = {}; type ColorInputProps = {};

View File

@@ -1,2 +0,0 @@
import ColorInput from "./ColorInput";
export default ColorInput;

View File

@@ -1,2 +0,0 @@
import ErrorBoundary from "./ErrorBoundary";
export default ErrorBoundary;

View File

@@ -1,12 +1,10 @@
import React from "react"; import React from "react";
import { Coffee, Heart } from "phosphor-react"; import { Coffee, Heart } from "phosphor-react";
import uArrowUpLeft from "assets/u-arrow-up-left.svg"; import uArrowUpLeft from "../../assets/u-arrow-up-left.svg";
import markerGreen from "assets/marker-green.svg"; import markerGreen from "../../assets/marker-green.svg";
import postIt from "assets/footer-mobile.svg"; import postIt from "../../assets/footer-mobile.svg";
import Links from "../Links/Links";
import Links from "components/Links";
import "./Footer.css"; import "./Footer.css";
type FooterProps = {}; type FooterProps = {};
@@ -36,8 +34,8 @@ const Footer: React.FC<FooterProps> = () => {
a little quirky, too. a little quirky, too.
</p> </p>
<p> <p>
We're thankful for the tools we've benefited from and this is our We're thankful for the tools we've benefited from and
contribution towards a collaborative community. this is our contribution towards a collaborative community.
</p> </p>
<p> <p>
Phosphor is free and open-source, licensed under{" "} Phosphor is free and open-source, licensed under{" "}

View File

@@ -1,2 +0,0 @@
import Footer from "./Footer";
export default Footer;

View File

@@ -1,24 +1,22 @@
import React from "react"; import React from "react";
import { ArrowCircleUpRight, ArrowCircleDown } from "phosphor-react"; import { ArrowCircleUpRight, ArrowCircleDown } from "phosphor-react";
import markerPurple from "assets/marker-purple.svg"; import markerPurple from "../../assets/marker-purple.svg";
import paperclips from "assets/paperclips-header-mobile.svg"; import paperclips from "../../assets/paperclips-header-mobile.svg";
import paperclipsThree from "assets/paperclips-header.svg"; import paperclipsThree from "../../assets/paperclips-header.svg";
import tablet from "assets/tablet.svg"; import tablet from "../../assets/tablet.svg";
import tabletSpec from "assets/tablet-spec.svg"; import tabletSpec from "../../assets/tablet-spec.svg";
import billiardBall from "assets/billiard-ball.svg"; import billiardBall from "../../assets/billiard-ball.svg";
import billiardBallSpec from "assets/billiard-ball-spec.svg"; import billiardBallSpec from "../../assets/billiard-ball-spec.svg";
import warning from "assets/warning.svg"; import warning from "../../assets/warning.svg";
import warningSpec from "assets/warning-spec.svg"; import warningSpec from "../../assets/warning-spec.svg";
import cuttingMat from "assets/cutting-mat.svg"; import cuttingMat from "../../assets/cutting-mat.svg";
import cuttingMatSpec from "assets/cutting-mat-spec.svg"; import cuttingMatSpec from "../../assets/cutting-mat-spec.svg";
import receipt from "assets/receipt.svg"; import receipt from "../../assets/receipt.svg";
import receiptSpec from "assets/receipt-spec.svg"; import receiptSpec from "../../assets/receipt-spec.svg";
import calculator from "assets/calculator.svg"; import calculator from "../../assets/calculator.svg";
import calculatorSpec from "assets/calculator-spec.svg"; import calculatorSpec from "../../assets/calculator-spec.svg";
import Links from "../Links/Links";
import Links from "components/Links";
import "./Header.css"; import "./Header.css";
type HeaderProps = {}; type HeaderProps = {};

View File

@@ -1,2 +0,0 @@
import Header from "./Header";
export default Header;

View File

@@ -3,19 +3,18 @@ import { useRecoilValue, useSetRecoilState } from "recoil";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { Svg2Png } from "svg2png-converter"; import { Svg2Png } from "svg2png-converter";
import { saveAs } from "file-saver"; import { saveAs } from "file-saver";
import ReactGA from "react-ga";
import { Copy, X, CheckCircle, Download } from "phosphor-react"; import { Copy, X, CheckCircle, Download } from "phosphor-react";
import ReactGA from "react-ga";
import { import {
iconWeightAtom, iconWeightAtom,
iconSizeAtom, iconSizeAtom,
iconColorAtom, iconColorAtom,
iconPreviewOpenAtom, iconPreviewOpenAtom,
} from "state/atoms"; } from "../../state/atoms";
import { IconEntry } from "lib"; import useTransientState from "../../hooks/useTransientState";
import useTransientState from "hooks/useTransientState";
import TagCloud from "./TagCloud"; import TagCloud from "./TagCloud";
import { IconEntry } from "../../lib";
const panelVariants = { const panelVariants = {
open: { open: {

View File

@@ -3,21 +3,20 @@ import { useRecoilValue } from "recoil";
import { motion, useAnimation } from "framer-motion"; import { motion, useAnimation } from "framer-motion";
import { IconContext } from "phosphor-react"; import { IconContext } from "phosphor-react";
import { iconWeightAtom, iconSizeAtom, iconColorAtom } from "state/atoms"; import { iconWeightAtom, iconSizeAtom, iconColorAtom } from "../../state/atoms";
import { import {
filteredQueryResultsSelector, filteredQueryResultsSelector,
isDarkThemeSelector, isDarkThemeSelector,
} from "state/selectors"; } from "../../state/selectors";
import useGridSpans from "hooks/useGridSpans"; import useGridSpans from "../../hooks/useGridSpans";
import IconGridItem from "./IconGridItem"; import IconGridItem from "./IconGridItem";
import TagCloud from "./TagCloud"; import TagCloud from "./TagCloud";
import Notice from "components/Notice"; import Notice from "../Notice/Notice";
import "./IconGrid.css"; import "./IconGrid.css";
const defaultSearchTags = [ const defaultSearchTags = [
"*new*", "*new*",
"*updated*",
"communication", "communication",
"editor", "editor",
"emoji", "emoji",

View File

@@ -7,9 +7,9 @@ import React, {
import { useRecoilState } from "recoil"; import { useRecoilState } from "recoil";
import { motion, AnimatePresence } from "framer-motion"; import { motion, AnimatePresence } from "framer-motion";
import { IconEntry } from "lib"; import { iconPreviewOpenAtom } from "../../state/atoms";
import { iconPreviewOpenAtom } from "state/atoms";
import DetailsPanel from "./DetailsPanel"; import DetailsPanel from "./DetailsPanel";
import { IconEntry } from "../../lib";
interface IconGridItemProps { interface IconGridItemProps {
index: number; index: number;
@@ -25,7 +25,7 @@ const delayPerPixel = 0.0004;
const itemVariants = { const itemVariants = {
hidden: { opacity: 0 }, hidden: { opacity: 0 },
visible: (delayRef: any) => ({ visible: (delayRef: MutableRefObject<number>) => ({
opacity: 1, opacity: 1,
transition: { delay: delayRef.current }, transition: { delay: delayRef.current },
}), }),
@@ -36,6 +36,8 @@ const IconGridItem: React.FC<IconGridItemProps> = (props) => {
const { name, Icon } = entry; const { name, Icon } = entry;
const [open, setOpen] = useRecoilState(iconPreviewOpenAtom); const [open, setOpen] = useRecoilState(iconPreviewOpenAtom);
const isOpen = open === name; const isOpen = open === name;
const isNew = entry.tags.includes("*new*");
const isUpdated = entry.tags.includes("*updated*");
const delayRef = useRef<number>(0); const delayRef = useRef<number>(0);
const offset = useRef({ top: 0, left: 0 }); const offset = useRef({ top: 0, left: 0 });
const ref = useRef<any>(); const ref = useRef<any>();
@@ -84,7 +86,11 @@ const IconGridItem: React.FC<IconGridItemProps> = (props) => {
onClick={handleOpen} onClick={handleOpen}
> >
<Icon /> <Icon />
<p>{name}</p> <p>
{name}
{isNew && <span className="badge new"></span>}
{isUpdated && <span className="badge updated"></span>}
</p>
</motion.div> </motion.div>
<AnimatePresence initial={false}> <AnimatePresence initial={false}>
{isOpen && <DetailsPanel {...props} />} {isOpen && <DetailsPanel {...props} />}

View File

@@ -1,8 +1,7 @@
import React, { useCallback } from "react"; import React, { useCallback } from "react";
import { useSetRecoilState } from "recoil"; import { useSetRecoilState } from "recoil";
import { searchQueryAtom } from "state/atoms"; import { searchQueryAtom } from "../../state/atoms";
import "./TagCloud.css"; import "./TagCloud.css";
interface TagCloudProps { interface TagCloudProps {
@@ -30,6 +29,8 @@ const TagCloud: React.FC<TagCloudProps> = ({ name, tags, isDark }) => {
onClick={() => void handleTagClick(tag)} onClick={() => void handleTagClick(tag)}
> >
<code className={`${isDark ? "dark" : ""}`}>{tag}</code> <code className={`${isDark ? "dark" : ""}`}>{tag}</code>
{tag === "*new*" && <span className="badge new"></span>}
{tag === "*updated*" && <span className="badge updated"></span>}
</button> </button>
))} ))}
</div> </div>

View File

@@ -1,2 +0,0 @@
import IconGrid from "./IconGrid";
export default IconGrid;

View File

@@ -20,14 +20,13 @@
margin-right: 12px; margin-right: 12px;
} }
.nav-link { a.nav-link {
text-decoration: none; text-decoration: none;
position: relative; position: relative;
color: black; color: black;
cursor: pointer;
} }
.nav-link:after { a.nav-link:after {
content: ""; content: "";
position: absolute; position: absolute;
bottom: -2px; bottom: -2px;
@@ -37,6 +36,6 @@
transition: 0.2s; transition: 0.2s;
} }
.nav-link:hover:after { a.nav-link:hover:after {
width: 100%; width: 100%;
} }

View File

@@ -1,22 +1,14 @@
import React from "react"; import React from "react";
import { OutboundLink } from "react-ga"; import { OutboundLink } from "react-ga";
import { useSetRecoilState } from "recoil";
import { ArrowElbowDownRight } from "phosphor-react"; import { ArrowElbowDownRight } from "phosphor-react";
import { iconCount } from "lib/icons"; import { iconCount } from "../../lib/icons";
import { modalSelector } from "state/selectors";
import DonationModal from "components/Modal/DonationModal";
import "./Links.css"; import "./Links.css";
interface LinksProps {} interface LinksProps {}
const Links: React.FC<LinksProps> = () => { const Links: React.FC<LinksProps> = () => {
const openModal = useSetRecoilState(modalSelector);
const openDonationModal = () => openModal({ type: DonationModal });
const delayedOpenDonationModal = () => setTimeout(openDonationModal, 2000);
return ( return (
<div className="links"> <div className="links">
<div> <div>
@@ -27,7 +19,6 @@ const Links: React.FC<LinksProps> = () => {
eventLabel="Download all" eventLabel="Download all"
download download
type="application/zip" type="application/zip"
onClick={delayedOpenDonationModal}
> >
Download all ({iconCount}) Download all ({iconCount})
</OutboundLink> </OutboundLink>
@@ -54,25 +45,41 @@ const Links: React.FC<LinksProps> = () => {
</div> </div>
<div> <div>
<ArrowElbowDownRight size={24} /> <ArrowElbowDownRight size={24} />
<a <OutboundLink
className="nav-link" className="nav-link"
href="https://github.com/phosphor-icons/phosphor-home/issues" to="https://phosphoricons.com/assets/phosphor-icons.sketchplugin.zip"
eventLabel="Download sketch plugin"
download
type="application/zip"
> >
Request an icon Sketch plugin
</a> </OutboundLink>
</div> </div>
<div> <div>
<ArrowElbowDownRight size={24} /> <ArrowElbowDownRight size={24} />
<span className="nav-link" onClick={openDonationModal}> <span>
Donate <a className="nav-link" href="https://paypal.me/minoraxis">
Donate on PayPal
</a>
{" / "}
<a className="nav-link" href="https://patreon.com/phosphoricons">
Patreon
</a>
</span> </span>
</div> </div>
{/* <div>
<ArrowElbowDownRight size={24} />
<a className="nav-link" href="https://paypal.me/minoraxis">
Donate on PayPal
</a>
</div>
<div> <div>
<ArrowElbowDownRight size={24} /> <ArrowElbowDownRight size={24} />
<a className="nav-link" href="https://patreon.com/phosphoricons"> <a className="nav-link" href="https://patreon.com/phosphoricons">
Support us on Patreon Support us on Patreon
</a> </a>
</div> </div>
*/}
<div> <div>
<ArrowElbowDownRight size={24} /> <ArrowElbowDownRight size={24} />
<a <a
@@ -82,6 +89,15 @@ const Links: React.FC<LinksProps> = () => {
GitHub GitHub
</a> </a>
</div> </div>
<div>
<ArrowElbowDownRight size={24} />
<a
className="nav-link"
href="https://github.com/phosphor-icons/phosphor-home/issues"
>
Request an icon
</a>
</div>
</div> </div>
); );
}; };

View File

@@ -1,2 +0,0 @@
import Links from "./Links";
export default Links;

View File

@@ -1,91 +0,0 @@
import React, { useState } from "react";
import { ModalInstance } from "../Modal";
import DonationStepMethod from "./DonationStepMethod";
import DonationStepDropin from "./DonationStepDropin";
import DonationStepThanks from "./DonationStepThanks";
const routes = [DonationStepMethod, DonationStepDropin, DonationStepThanks];
export enum DonationType {
FIVE_DOLLARS = 5,
TEN_DOLLARS = 10,
TWENTY_DOLLARS = 20,
FIFTY_DOLLARS = 50,
ONE_HUNDRED_DOLLARS = 100,
CUSTOM = -1,
}
interface RouteProps {
donationType: DonationType | undefined;
setDonationType: React.Dispatch<
React.SetStateAction<DonationType | undefined>
>;
donationAmount: number;
setDonationAmount: React.Dispatch<React.SetStateAction<number>>;
}
export interface StepProps {
previousStep: () => void;
nextStep: () => void;
close: () => void;
routeProps: RouteProps;
}
interface StepperProps {
routes: Array<React.FC<StepProps>>;
routeProps: RouteProps;
close: () => void;
}
const Stepper: React.FC<StepperProps> = ({ routes, routeProps, close }) => {
const [currentStep, setCurrentStep] = useState<number>(0);
const previousStep = () => {
if (currentStep <= 0) return;
setTimeout(() => setCurrentStep((c) => c - 1), 500);
};
const nextStep = () => {
if (currentStep >= routes.length - 1) return;
setTimeout(() => setCurrentStep((c) => c + 1), 500);
};
const Component = routes[currentStep];
return (
<>
<Component
previousStep={previousStep}
nextStep={nextStep}
close={close}
routeProps={routeProps}
/>
</>
);
};
const DonationModal = ({ close }: ModalInstance): JSX.Element => {
const [donationType, setDonationType] = useState<DonationType | undefined>();
const [donationAmount, setDonationAmount] = useState<number>(0);
return (
<>
<div className="modal-titlebar">
<h2>Donate</h2>
</div>
<Stepper
routes={routes}
routeProps={{
donationType,
setDonationType,
donationAmount,
setDonationAmount,
}}
close={close}
/>
</>
);
};
export default DonationModal;

View File

@@ -1,128 +0,0 @@
import React, { useRef, useState, useEffect } from "react";
import dropin, { Dropin } from "braintree-web-drop-in";
import axios from "axios";
import { CurrencyCircleDollar } from "phosphor-react";
import { StepProps } from "./DonationModal";
const PaymentServer = axios.create({
baseURL: "https://us-central1-phosphor-14c61.cloudfunctions.net/paymentsApi",
});
const BT_PAYMENT_FIELDS = {
number: {
placeholder: "4111 1111 1111 1111",
},
cvv: {
placeholder: "123",
},
expirationDate: {
placeholder: "10/22",
},
cardholderName: {
placeholder: "Person McFace",
},
};
const PaymentModal: React.FC<StepProps> = ({
nextStep,
previousStep,
routeProps,
}) => {
const instance = useRef<Dropin>();
const [isValid, setIsValid] = useState<boolean>(false);
const [isLoading, setLoading] = useState<boolean>(false);
const { donationAmount } = routeProps;
const submit = async () => {
if (!instance.current) return;
setLoading(true);
try {
const payload = await instance.current.requestPaymentMethod();
console.log({ ...payload, donationAmount });
const response = await PaymentServer.post("/", {
...payload,
donationAmount,
});
console.log({ response });
if (!!response.data?.success) {
nextStep();
} else {
setIsValid(false);
}
} catch (_e) {
} finally {
setLoading(false);
}
};
useEffect(() => {
const initializePayments = async () => {
try {
instance.current = await dropin.create({
authorization: "sandbox_246jdjxq_8h7hm5rvngkykjds",
container: "#braintree-dropin",
card: {
cardholderName: {
required: true,
},
overrides: {
fields: BT_PAYMENT_FIELDS,
},
},
paypal: {
flow: "checkout",
amount: donationAmount.toFixed(2).toString(),
currency: "USD",
commit: false,
},
paypalCredit: {
flow: "checkout",
amount: donationAmount.toFixed(2).toString(),
currency: "USD",
commit: false,
},
venmo: { allowNewBrowserTab: false },
});
instance.current.on("paymentMethodRequestable", () => setIsValid(true));
instance.current.on("noPaymentMethodRequestable", () =>
setIsValid(false)
);
} catch (err) {
console.error(err);
}
};
initializePayments();
}, [donationAmount]);
return (
<>
<div id="braintree-dropin"></div>
<div className="donation-details">
<CurrencyCircleDollar size={64} weight="duotone" />
<span>
Preparing your ${routeProps.donationAmount.toFixed(2)} donation...
</span>
</div>
<div className="step-button-container">
<button className="main-button" onClick={previousStep}>
Back
</button>
<button
className="main-button"
onClick={submit}
disabled={isLoading || !isValid}
>
{isLoading ? "Processing..." : "Donate"}
</button>
</div>
</>
);
};
export default PaymentModal;

View File

@@ -1,123 +0,0 @@
import React from "react";
import { StepProps, DonationType } from "./DonationModal";
const DonationStepMethod: React.FC<StepProps> = ({
previousStep,
nextStep,
close,
routeProps,
}) => {
const { donationType, donationAmount, setDonationType, setDonationAmount } =
routeProps;
const onDonationChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setDonationType(+e.target.value as DonationType);
if (!(+e.target.value === DonationType.CUSTOM))
setDonationAmount(+e.target.value);
};
void previousStep;
return (
<>
<h3>
Thanks for using Phosphor! Would you like to make a donation to help
support our work?
</h3>
<div className="radio-group">
<div className="radio-button">
<input
type="radio"
id="donate-5"
name="donation-type"
value={DonationType.FIVE_DOLLARS}
checked={donationType === DonationType.FIVE_DOLLARS}
onChange={onDonationChange}
/>
<label htmlFor="donate-5">$5</label>
</div>
<div className="radio-button">
<input
type="radio"
id="donate-10"
name="donation-type"
value={DonationType.TEN_DOLLARS}
checked={donationType === DonationType.TEN_DOLLARS}
onChange={onDonationChange}
/>
<label htmlFor="donate-10">$10</label>
</div>
<div className="radio-button">
<input
type="radio"
id="donate-20"
name="donation-type"
value={DonationType.TWENTY_DOLLARS}
checked={donationType === DonationType.TWENTY_DOLLARS}
onChange={onDonationChange}
/>
<label htmlFor="donate-20">$20</label>
</div>
<div className="radio-button">
<input
type="radio"
id="donate-50"
name="donation-type"
value={DonationType.FIFTY_DOLLARS}
checked={donationType === DonationType.FIFTY_DOLLARS}
onChange={onDonationChange}
/>
<label htmlFor="donate-50">$50</label>
</div>
<div className="radio-button">
<input
type="radio"
id="donate-100"
name="donation-type"
value={DonationType.ONE_HUNDRED_DOLLARS}
checked={donationType === DonationType.ONE_HUNDRED_DOLLARS}
onChange={onDonationChange}
/>
<label htmlFor="donate-100">$100</label>
</div>
<div className="radio-button">
<input
type="radio"
id="donate-custom"
name="donation-type"
value={DonationType.CUSTOM}
checked={donationType === DonationType.CUSTOM}
onChange={onDonationChange}
/>
<label htmlFor="donate-custom">
Other: $
<input
type="number"
min={2}
max={1000000}
id="donate-custom"
value={donationAmount}
disabled={donationType !== DonationType.CUSTOM}
onChange={(e) => setDonationAmount(+e.target.value)}
/>
</label>
</div>
</div>
<div className="step-button-container">
<button className="main-button" onClick={close}>
No thanks
</button>
<button
className="main-button"
onClick={nextStep}
disabled={!(donationType && donationAmount)}
>
Next
</button>
</div>
</>
);
};
export default DonationStepMethod;

View File

@@ -1,7 +0,0 @@
import React from "react";
const DonationStepThanks: React.FC<{}> = () => {
return null;
};
export default DonationStepThanks;

View File

@@ -1,2 +0,0 @@
import DonationModal from "./DonationModal";
export default DonationModal;

View File

@@ -1,85 +0,0 @@
.modal-container {
position: fixed;
display: grid;
place-items: center;
inset: 0;
background-color: rgba(0, 0, 0, 0.4);
z-index: 10;
}
.modal-content {
position: relative;
display: flex;
flex-direction: column;
width: 400px;
min-height: 600px;
background-color: white;
border-radius: 8px;
border: 2px solid black;
padding: 24px;
}
@media screen and (max-width: 480px) {
.modal-content {
position: fixed;
inset: 0;
width: unset;
min-height: unset;
border: none;
border-radius: 0;
}
}
.step-button-container {
flex: 1;
display: flex;
flex-direction: row;
align-items: flex-end;
justify-content: center;
}
.donation-details {
display: grid;
place-items: center;
padding: 32px;
}
button.modal-close-button {
position: absolute;
top: 24px;
right: 24px;
background: transparent;
padding: 0;
cursor: pointer;
box-sizing: content-box;
z-index: 1;
}
.modal-titlebar {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.modal-titlebar > * {
margin: 0;
}
.payment-methods {
display: grid;
gap: 16px;
grid-template-rows: repeat(2, 1fr);
grid-template-columns: repeat(2, 1fr);
}
.payment-methods button {
margin: 0;
padding: 0;
justify-content: center;
height: 96px;
}
.payment-methods button > svg {
margin: 0;
}

View File

@@ -1,38 +0,0 @@
import React from "react";
import { useRecoilState, useRecoilValue } from "recoil";
import { useLockBodyScroll } from "react-use";
import { useHotkeys } from "react-hotkeys-hook";
import { X } from "phosphor-react";
import { modalAtom, modalOpenAtom } from "state/atoms";
import "./Modal.css";
export interface ModalInstance {
close: () => void;
children?: JSX.Element;
}
const Modal = (): JSX.Element | null => {
const Instance = useRecoilValue(modalAtom);
const [isModalOpen, setModalOpen] = useRecoilState(modalOpenAtom);
useHotkeys("esc", () => isModalOpen && setModalOpen(false), [isModalOpen]);
useLockBodyScroll(isModalOpen);
const close = () => setModalOpen(false);
if (!Instance || !isModalOpen) return null;
return (
<div className="modal-container">
<div className="modal-content">
<button className="modal-close-button" onClick={close}>
<X size={32} />
</button>
<Instance close={close} />
</div>
</div>
);
};
export default Modal;

View File

@@ -1,3 +0,0 @@
import Modal from "./Modal";
export default Modal;

View File

@@ -1,21 +1,17 @@
import React from "react"; import React from "react";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { useRecoilValue } from "recoil"; import { useRecoilValue } from "recoil";
import { HourglassMedium, Question, SmileyXEyes } from "phosphor-react";
import { isDarkThemeSelector } from "state/selectors"; import { isDarkThemeSelector } from "../../state/selectors";
import { searchQueryAtom } from "state/atoms"; import { searchQueryAtom } from "../../state/atoms";
import { HourglassMedium, Question, SmileyXEyes } from "phosphor-react";
interface NoticeProps { interface NoticeProps {
message?: string; message?: string;
type?: "wait" | "help" | "warn" | "none"; type?: "wait" | "help" | "warn" | "none";
} }
const Notice: React.FC<NoticeProps> = ({ const Notice: React.FC<NoticeProps> = ({ message, type = "warn", children }) => {
message,
type = "warn",
children,
}) => {
const isDark = useRecoilValue(isDarkThemeSelector); const isDark = useRecoilValue(isDarkThemeSelector);
const query = useRecoilValue(searchQueryAtom); const query = useRecoilValue(searchQueryAtom);

View File

@@ -1,2 +0,0 @@
import Notice from "./Notice";
export default Notice;

View File

@@ -5,8 +5,7 @@ import { useHotkeys } from "react-hotkeys-hook";
import { Command, MagnifyingGlass, X, HourglassHigh } from "phosphor-react"; import { Command, MagnifyingGlass, X, HourglassHigh } from "phosphor-react";
import ReactGA from "react-ga"; import ReactGA from "react-ga";
import { searchQueryAtom } from "state/atoms"; import { searchQueryAtom } from "../../state/atoms";
import "./SearchInput.css"; import "./SearchInput.css";
const apple = /iPhone|iPod|iPad|Macintosh|MacIntel|MacPPC/i; const apple = /iPhone|iPod|iPad|Macintosh|MacIntel|MacPPC/i;
@@ -20,8 +19,7 @@ type SearchInputProps = {};
const SearchInput: React.FC<SearchInputProps> = () => { const SearchInput: React.FC<SearchInputProps> = () => {
const [value, setValue] = useState<string>(""); const [value, setValue] = useState<string>("");
const [query, setQuery] = useRecoilState(searchQueryAtom); const [query, setQuery] = useRecoilState(searchQueryAtom);
const inputRef = const inputRef = useRef<HTMLInputElement>() as MutableRefObject<HTMLInputElement>;
useRef<HTMLInputElement>() as MutableRefObject<HTMLInputElement>;
useHotkeys("ctrl+k,cmd+k", (e) => { useHotkeys("ctrl+k,cmd+k", (e) => {
e.preventDefault(); e.preventDefault();

View File

@@ -1,2 +0,0 @@
import SearchInput from "./SearchInput";
export default SearchInput;

View File

@@ -0,0 +1,21 @@
button.action-button {
background-color: rgba(255, 255, 255, 0.05);
color: white;
padding: 8px;
border-radius: 8px;
cursor: pointer;
}
button.action-button:hover {
background-color: rgba(255, 255, 255, 0.1);
}
button.action-button:active {
background-color: rgba(255, 255, 255, 0.2);
}
@media screen and (max-width: 558px) {
.action-button {
display: none;
}
}

View File

@@ -0,0 +1,57 @@
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";
const SettingsActions: React.FC = () => {
const weight = useRecoilValue(iconWeightAtom);
const size = useRecoilValue(iconSizeAtom);
const color = useRecoilValue(iconColorAtom);
const reset = useResetRecoilState(resetSettingsSelector);
const [copied, setCopied] = useTransientState<boolean>(false, 2000);
const copyDeepLinkToClipboard = () => {
const paramString = new URLSearchParams([
["weight", weight.toString()],
["size", size.toString()],
["color", color.replace("#", "")],
]).toString();
void navigator.clipboard
?.writeText(`${window.location.host}?${paramString}`)
.then(() => {
setCopied(true);
})
.catch(() => {
alert("Clipboard permissions must be enabled to copy links!");
});
};
return (
<>
<button
className="action-button"
title="Restore default settings"
onClick={reset}
>
<ArrowCounterClockwise size={24} />
</button>
<button
className="action-button"
title="Copy URL for current settings"
onClick={copyDeepLinkToClipboard}
>
{copied ? (
<CheckCircle size={24} color="#1FA647" weight="fill" />
) : (
<Link size={24} />
)}
</button>
</>
);
};
export default SettingsActions;

View File

@@ -1,8 +1,7 @@
import React, { useCallback } from "react"; import React, { useCallback } from "react";
import { useRecoilState } from "recoil"; import { useRecoilState } from "recoil";
import { iconSizeAtom } from "state/atoms"; import { iconSizeAtom } from "../../state/atoms";
import "./SizeInput.css"; import "./SizeInput.css";
type SizeInputProps = {}; type SizeInputProps = {};

View File

@@ -1,2 +0,0 @@
import SizeInput from "./SizeInput";
export default SizeInput;

View File

@@ -3,9 +3,8 @@ import { useRecoilState } from "recoil";
import Select from "react-dropdown-select"; import Select from "react-dropdown-select";
import { PencilLine } from "phosphor-react"; import { PencilLine } from "phosphor-react";
import { iconWeightAtom } from "state/atoms"; import { iconWeightAtom } from "../../state/atoms";
import { IconStyle } from "lib"; import { IconStyle } from "../../lib";
import "./StyleInput.css"; import "./StyleInput.css";
type WeightOption = { key: string; value: IconStyle; icon: JSX.Element }; type WeightOption = { key: string; value: IconStyle; icon: JSX.Element };

View File

@@ -1,2 +0,0 @@
import StyleInput from "./StyleInput";
export default StyleInput;

View File

@@ -1,11 +1,11 @@
import React from "react"; 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 "./Toolbar.css"; 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 = {}; type ToolbarProps = {};
@@ -17,6 +17,7 @@ const Toolbar: React.FC<ToolbarProps> = () => {
<SearchInput /> <SearchInput />
<SizeInput /> <SizeInput />
<ColorInput /> <ColorInput />
<SettingsActions />
</div> </div>
</nav> </nav>
); );

View File

@@ -1,2 +0,0 @@
import Toolbar from "./Toolbar";
export default Toolbar;

View File

@@ -1,8 +0,0 @@
export { default as useDebounce } from "./useDebounce";
export { default as useGridSpans } from "./useGridSpans";
export { default as useIconParameters } from "./useIconParameters";
export { default as useThrottle } from "./useThrottle";
export { default as useThrottled } from "./useThrottled";
export { default as useTimeoutFn } from "./useTimeoutFn";
export { default as useTransientState } from "./useTransientState";
export { default as useUnmount } from "./useUnmount";

View File

@@ -34,4 +34,30 @@ export default () => {
if (normalizedColor.isValid()) setColor(normalizedColor.toHexString()); if (normalizedColor.isValid()) setColor(normalizedColor.toHexString());
} }
}, [color, setColor]); }, [color, setColor]);
useEffect(() => {
if (!weight && !size && !color) {
const persistedState = JSON.parse(
window.localStorage.getItem("__phosphor_settings__") || "null"
);
if (!!persistedState) {
const { weight, size, color } = persistedState;
if (weight) {
if (weight.toUpperCase() in IconStyle) setWeight(weight as IconStyle);
}
if (size) {
const normalizedSize = parseInt(size);
if (typeof normalizedSize === "number" && isFinite(normalizedSize))
setSize(Math.min(Math.max(normalizedSize, 16), 96));
}
if (color) {
const normalizedColor = TinyColor(color);
if (normalizedColor.isValid())
setColor(normalizedColor.toHexString());
}
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}; };

View File

@@ -0,0 +1,18 @@
import { useRecoilValue } from "recoil";
import useDebounce from "./useDebounce";
import { iconWeightAtom, iconSizeAtom, iconColorAtom } from "../state/atoms";
export default function usePersistSettings() {
const weight = useRecoilValue(iconWeightAtom);
const size = useRecoilValue(iconSizeAtom);
const color = useRecoilValue(iconColorAtom);
useDebounce(
() => {
const serializedState = JSON.stringify({ weight, size, color });
window.localStorage.setItem("__phosphor_settings__", serializedState);
},
2000,
[weight, size, color]
);
}

View File

@@ -2,7 +2,7 @@ import React from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import { RecoilRoot } from "recoil"; import { RecoilRoot } from "recoil";
import * as serviceWorker from "./serviceWorker"; import * as serviceWorker from "./serviceWorker";
import App from "./components/App"; import App from "./components/App/App";
import ReactGA from "react-ga"; import ReactGA from "react-ga";
ReactGA.initialize("UA-179205759-1", { titleCase: false }); ReactGA.initialize("UA-179205759-1", { titleCase: false });
@@ -22,8 +22,8 @@ ReactDOM.render(
// Learn more about service workers: https://bit.ly/CRA-PWA // Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister(); serviceWorker.unregister();
// prettier-ignore
console.log(` console.log(`
%c sphorphosphor %co%cspho %c sphorphosphor %co%cspho
%c s%cphorphosphor %co%csphorpho%cs %c s%cphorphosphor %co%csphorpho%cs
%c o %cp%chorphosphor %co%csphorphosph%co %c o %cp%chorphosphor %co%csphorphosph%co

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,4 @@
import { atom } from "recoil"; import { atom } from "recoil";
import { ModalInstance } from "../components/Modal/Modal";
import { IconStyle } from "../lib"; import { IconStyle } from "../lib";
export const searchQueryAtom = atom<string>({ export const searchQueryAtom = atom<string>({
@@ -14,7 +13,7 @@ export const iconWeightAtom = atom<IconStyle>({
export const iconSizeAtom = atom<number>({ export const iconSizeAtom = atom<number>({
key: "iconSizeAtom", key: "iconSizeAtom",
default: 48, default: 32,
}); });
export const iconColorAtom = atom<string>({ export const iconColorAtom = atom<string>({
@@ -26,13 +25,3 @@ export const iconPreviewOpenAtom = atom<string | false>({
key: "iconPreviewOpenAtom", key: "iconPreviewOpenAtom",
default: false, default: false,
}); });
export const modalAtom = atom<((props: ModalInstance) => JSX.Element) | null>({
key: "modalAtom",
default: null,
});
export const modalOpenAtom = atom<boolean>({
key: "modalOpenAtom",
default: false,
});

View File

@@ -1,16 +1,15 @@
import { DefaultValue, selector, selectorFamily } from "recoil"; import { selector, selectorFamily } from "recoil";
import TinyColor from "tinycolor2"; import TinyColor from "tinycolor2";
import Fuse from "fuse.js"; import Fuse from "fuse.js";
import { import {
searchQueryAtom, searchQueryAtom,
iconWeightAtom,
iconSizeAtom,
iconColorAtom, iconColorAtom,
modalAtom,
modalOpenAtom,
} from "./atoms"; } from "./atoms";
import { IconEntry, IconCategory } from "../lib"; import { IconEntry, IconCategory } from "../lib";
import { icons } from "../lib/icons"; import { icons } from "../lib/icons";
import { ModalInstance } from "../components/Modal/Modal";
const fuse = new Fuse(icons, { const fuse = new Fuse(icons, {
keys: [{ name: "name", weight: 4 }, "tags", "categories"], keys: [{ name: "name", weight: 4 }, "tags", "categories"],
@@ -75,23 +74,12 @@ export const isDarkThemeSelector = selector<boolean>({
get: ({ get }) => TinyColor(get(iconColorAtom)).isLight(), get: ({ get }) => TinyColor(get(iconColorAtom)).isLight(),
}); });
export const modalSelector = selector<{ export const resetSettingsSelector = selector<null>({
type: (props: ModalInstance) => JSX.Element; key: "resetSettings",
} | null>({ get: () => null,
key: "openModalSelector", set: ({ reset }) => {
set: ({ set }, instance) => { reset(iconWeightAtom);
if (instance instanceof DefaultValue || instance === null) { reset(iconSizeAtom);
set(modalAtom, null); reset(iconColorAtom);
set(modalOpenAtom, false);
return;
}
set(modalAtom, () => instance.type!!);
set(modalOpenAtom, true);
},
get: ({ get }) => {
const currentModal = get(modalAtom);
if (!currentModal) return null;
return { type: currentModal };
}, },
}); });

View File

@@ -1,27 +1,36 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es5",
"lib": [
"es6",
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true, "allowJs": true,
"allowSyntheticDefaultImports": true, "skipLibCheck": true,
"baseUrl": "./src/",
"declaration": true,
"esModuleInterop": true, "esModuleInterop": true,
"experimentalDecorators": true, "allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"jsx": "react",
"lib": ["es6", "dom", "dom.iterable", "esnext"],
"module": "esnext", "module": "esnext",
"moduleResolution": "node", "moduleResolution": "node",
"noEmit": true, "resolveJsonModule": true,
"noFallthroughCasesInSwitch": true, "isolatedModules": true,
"jsx": "react",
"sourceMap": true,
"declaration": true,
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"resolveJsonModule": true, "experimentalDecorators": true,
"skipLibCheck": true, "noFallthroughCasesInSwitch": true,
"sourceMap": true, "noEmit": true
"strict": true,
"target": "es5"
}, },
"include": ["src"], "include": [
"exclude": ["node_modules", "build"] "src"
],
"exclude": [
"node_modules",
"build"
]
} }