diff --git a/src/components/App/App.tsx b/src/components/App/App.tsx
index 4bfd6e9..a5e3c0d 100644
--- a/src/components/App/App.tsx
+++ b/src/components/App/App.tsx
@@ -8,12 +8,14 @@ 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 = ;
const waitingFallback = ;
const App: React.FC = () => {
useIconParameters();
+ usePersistSettings();
return (
diff --git a/src/components/SettingsActions/SettingsActions.css b/src/components/SettingsActions/SettingsActions.css
new file mode 100644
index 0000000..540949c
--- /dev/null
+++ b/src/components/SettingsActions/SettingsActions.css
@@ -0,0 +1,13 @@
+.action-button {
+ background-color: rgba(255, 255, 255, 0.05);
+ color: white;
+ padding: 8px;
+ border-radius: 8px;
+ cursor: pointer;
+}
+
+@media screen and (max-width: 558px) {
+ .action-button {
+ display: none;
+ }
+}
diff --git a/src/components/SettingsActions/SettingsActions.tsx b/src/components/SettingsActions/SettingsActions.tsx
new file mode 100644
index 0000000..18341b0
--- /dev/null
+++ b/src/components/SettingsActions/SettingsActions.tsx
@@ -0,0 +1,53 @@
+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(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}`
+ );
+ setCopied(true);
+ };
+
+ return (
+ <>
+
+
+ >
+ );
+};
+
+export default SettingsActions;
diff --git a/src/components/Toolbar/Toolbar.tsx b/src/components/Toolbar/Toolbar.tsx
index e5a939c..ac3c17f 100644
--- a/src/components/Toolbar/Toolbar.tsx
+++ b/src/components/Toolbar/Toolbar.tsx
@@ -5,6 +5,7 @@ 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 = {};
@@ -16,6 +17,7 @@ const Toolbar: React.FC = () => {
+
);
diff --git a/src/hooks/useIconParameters.ts b/src/hooks/useIconParameters.ts
index b2881bf..356e754 100644
--- a/src/hooks/useIconParameters.ts
+++ b/src/hooks/useIconParameters.ts
@@ -34,4 +34,29 @@ export default () => {
if (normalizedColor.isValid()) setColor(normalizedColor.toHexString());
}
}, [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());
+ }
+ }
+ }
+ }, []);
};
diff --git a/src/hooks/usePersistSettings.ts b/src/hooks/usePersistSettings.ts
new file mode 100644
index 0000000..689c01d
--- /dev/null
+++ b/src/hooks/usePersistSettings.ts
@@ -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]
+ );
+}
diff --git a/src/state/atoms.ts b/src/state/atoms.ts
index 3ec5a2e..ba6c643 100644
--- a/src/state/atoms.ts
+++ b/src/state/atoms.ts
@@ -13,7 +13,7 @@ export const iconWeightAtom = atom({
export const iconSizeAtom = atom({
key: "iconSizeAtom",
- default: 48,
+ default: 32,
});
export const iconColorAtom = atom({
diff --git a/src/state/selectors.ts b/src/state/selectors.ts
index c76205a..bb6e3f3 100644
--- a/src/state/selectors.ts
+++ b/src/state/selectors.ts
@@ -2,7 +2,12 @@ import { selector, selectorFamily } from "recoil";
import TinyColor from "tinycolor2";
import Fuse from "fuse.js";
-import { searchQueryAtom, iconColorAtom } from "./atoms";
+import {
+ searchQueryAtom,
+ iconWeightAtom,
+ iconSizeAtom,
+ iconColorAtom,
+} from "./atoms";
import { IconEntry, IconCategory } from "../lib";
import { icons } from "../lib/icons";
@@ -52,17 +57,29 @@ export const singleCategoryQueryResultsSelector = selectorFamily<
IconCategory
>({
key: "singleCategoryQueryResultsSelector",
- get: (category: IconCategory) => ({ get }) => {
- const filteredResults = get(filteredQueryResultsSelector);
- return new Promise((resolve) =>
- resolve(
- filteredResults.filter((icon) => icon.categories.includes(category))
- )
- );
- },
+ get:
+ (category: IconCategory) =>
+ ({ get }) => {
+ const filteredResults = get(filteredQueryResultsSelector);
+ return new Promise((resolve) =>
+ resolve(
+ filteredResults.filter((icon) => icon.categories.includes(category))
+ )
+ );
+ },
});
export const isDarkThemeSelector = selector({
key: "isDarkThemeSelector",
get: ({ get }) => TinyColor(get(iconColorAtom)).isLight(),
});
+
+export const resetSettingsSelector = selector({
+ key: "resetSettings",
+ get: () => null,
+ set: ({ reset }) => {
+ reset(iconWeightAtom);
+ reset(iconSizeAtom);
+ reset(iconColorAtom);
+ },
+});