diff --git a/src/components/App/App.css b/src/components/App/App.css index 9ede2c8..2ec16d4 100644 --- a/src/components/App/App.css +++ b/src/components/App/App.css @@ -31,9 +31,9 @@ pre { box-sizing: border-box; padding: 20px 16px 20px 24px; margin: 12px 0px; - background-color: white; + /* background-color: white; */ border-radius: 6px; - border: 1px solid #e1d4d7; + /* border: 1px solid #e1d4d7; */ white-space: pre-wrap; } @@ -95,17 +95,6 @@ button.main-button svg { /* gap: 24px; */ } -figure { - margin: 0; - display: grid; - place-items: center; -} - -figcaption { - font-size: 14px; - text-align: center; -} - a.main-link { text-decoration: none; position: relative; diff --git a/src/components/ColorInput/ColorInput.tsx b/src/components/ColorInput/ColorInput.tsx index f091dbb..4f9201c 100644 --- a/src/components/ColorInput/ColorInput.tsx +++ b/src/components/ColorInput/ColorInput.tsx @@ -1,9 +1,8 @@ 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 { useThrottled } from "@/hooks"; +import { iconColorAtom, isDarkThemeSelector } from "@/state"; import "./ColorInput.css"; diff --git a/src/components/Footer/Footer.css b/src/components/Footer/Footer.css index 5168379..31b40cd 100644 --- a/src/components/Footer/Footer.css +++ b/src/components/Footer/Footer.css @@ -67,7 +67,7 @@ footer .links { padding: 0; } - #back-to-top-button img { + #back-to-top-button svg { width: 28px; height: 28px; } diff --git a/src/components/IconGrid/DetailFooter.tsx b/src/components/IconGrid/DetailFooter.tsx index 33b2fc7..60a1705 100644 --- a/src/components/IconGrid/DetailFooter.tsx +++ b/src/components/IconGrid/DetailFooter.tsx @@ -4,27 +4,27 @@ import { useHotkeys } from "react-hotkeys-hook"; import { motion, AnimatePresence, Variants } from "framer-motion"; import { Svg2Png } from "svg2png-converter"; import { saveAs } from "file-saver"; -import { Copy, CheckCircle, Download } from "phosphor-react"; +import { Copy, CheckCircle, DownloadSimple } from "phosphor-react"; import ReactGA from "react-ga4"; +import Tabs, { Tab } from "@/components/Tabs"; +import { useTransientState } from "@/hooks"; +import { SnippetType } from "@/lib"; import { iconWeightAtom, iconSizeAtom, iconColorAtom, selectionEntryAtom, -} from "@/state/atoms"; -import { isDarkThemeSelector } from "@/state/selectors"; -import Tabs, { Tab } from "@/components/Tabs"; -import useTransientState from "@/hooks/useTransientState"; -import { SnippetType } from "@/lib"; + isDarkThemeSelector, +} from "@/state"; import { getCodeSnippets, supportsWeight } from "@/utils"; import TagCloud from "./TagCloud"; const variants: Variants = { - initial: { opacity: 0 }, - animate: { opacity: 1 }, - exit: { opacity: 0 }, + initial: { y: 188 }, + animate: { y: 0 }, + exit: { y: 188 }, }; const RENDERED_SNIPPETS = [ @@ -94,10 +94,7 @@ const DetailFooter = () => { header: type, content: (
+
{isWeightSupported
? snippets[type]
@@ -213,31 +210,44 @@ const DetailFooter = () => {
- {entry.name}
+
+ {entry.name}
+
+ available in v{entry.published_in.toFixed(1)}.0+
+
+
-
- in ≥ {entry.published_in.toFixed(1)}.0
-
+
+
+
+
+
+
-
-
-
-
-
)}
diff --git a/src/components/IconGrid/DetailsPanel.tsx b/src/components/IconGrid/DetailsPanel.tsx
index f02a056..8c6609e 100644
--- a/src/components/IconGrid/DetailsPanel.tsx
+++ b/src/components/IconGrid/DetailsPanel.tsx
@@ -7,14 +7,14 @@ import { saveAs } from "file-saver";
import { Copy, X, CheckCircle, Download } from "phosphor-react";
import ReactGA from "react-ga4";
+import { useTransientState } from "@/hooks";
+import { IconEntry, SnippetType } from "@/lib";
import {
iconWeightAtom,
iconSizeAtom,
iconColorAtom,
iconPreviewOpenAtom,
-} from "@/state/atoms";
-import useTransientState from "@/hooks/useTransientState";
-import { IconEntry, SnippetType } from "@/lib";
+} from "@/state";
import { getCodeSnippets, supportsWeight } from "@/utils";
import TagCloud from "./TagCloud";
diff --git a/src/components/IconGrid/IconGrid.css b/src/components/IconGrid/IconGrid.css
index 2a58caa..6a25fe8 100644
--- a/src/components/IconGrid/IconGrid.css
+++ b/src/components/IconGrid/IconGrid.css
@@ -119,14 +119,15 @@
}
.snippet {
- margin-bottom: 24px;
+ /* margin-bottom: 24px; */
+ width: 100%;
}
.snippet pre {
display: flex;
align-items: center;
text-overflow: ellipsis;
- color: black;
+ /* color: black; */
-moz-user-select: all;
-webkit-user-select: all;
user-select: all;
@@ -218,16 +219,40 @@ aside.detail-footer {
margin: auto;
max-width: 1120px;
display: grid;
- grid-template-columns: 144px 1fr 160px;
+ grid-template-columns: 232px 1fr;
+ gap: 24px;
+ padding: 12px 24px;
+ height: 136px;
+}
+
+figure {
+ margin: 0;
+ display: grid;
+ grid-template-columns: 64px 1fr;
+ gap: 24px;
+ align-items: center;
+}
+
+figcaption {
+ display: flex;
+ flex-direction: column;
+ font-size: 14px;
+}
+
+figcaption > p {
+ margin: 0;
}
.detail-preview {
display: flex;
flex-direction: column;
- align-items: center;
- padding: 16px;
+ justify-content: center;
+ gap: 24px;
}
.detail-actions {
- padding: 16px;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 8px;
}
diff --git a/src/components/IconGrid/IconGrid.tsx b/src/components/IconGrid/IconGrid.tsx
index b2f06af..4679ae9 100644
--- a/src/components/IconGrid/IconGrid.tsx
+++ b/src/components/IconGrid/IconGrid.tsx
@@ -1,13 +1,15 @@
-import { useRef, useEffect } from "react";
+import { useRef, useEffect, CSSProperties } 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,
filteredQueryResultsSelector,
isDarkThemeSelector,
-} from "@/state/selectors";
+} from "@/state";
import useGridSpans from "@/hooks/useGridSpans";
import Notice from "@/components/Notice";
@@ -26,6 +28,13 @@ const defaultSearchTags = [
"weather",
];
+const gridStyle: Record = {
+ light: {},
+ dark: {
+ backgroundColor: "#35313D",
+ },
+} as const;
+
type IconGridProps = {};
const IconGrid = (_: IconGridProps) => {
@@ -56,15 +65,10 @@ const IconGrid = (_: IconGridProps) => {
-
+
{filteredQueryResults.map((iconEntry, index) => (
{
className="grid-item"
key={name}
ref={ref}
- tabIndex={0}
+ tabIndex={1}
style={{
order: index,
backgroundColor: isOpen ? "rgba(163, 159, 171, 0.1)" : undefined,
@@ -88,9 +86,6 @@ const IconGridItem = (props: IconGridItemProps) => {
{isUpdated && •}
- {/*
- {isOpen && }
- */}
>
);
};
diff --git a/src/components/IconGrid/TagCloud.tsx b/src/components/IconGrid/TagCloud.tsx
index e2ab621..817163b 100644
--- a/src/components/IconGrid/TagCloud.tsx
+++ b/src/components/IconGrid/TagCloud.tsx
@@ -1,7 +1,7 @@
import { useCallback } from "react";
import { useSetRecoilState } from "recoil";
-import { searchQueryAtom } from "@/state/atoms";
+import { searchQueryAtom } from "@/state";
import "./TagCloud.css";
interface TagCloudProps {
diff --git a/src/components/Notice/Notice.tsx b/src/components/Notice/Notice.tsx
index 43f5082..cacc302 100644
--- a/src/components/Notice/Notice.tsx
+++ b/src/components/Notice/Notice.tsx
@@ -3,8 +3,7 @@ import { motion } from "framer-motion";
import { useRecoilValue } from "recoil";
import { HourglassMedium, Question, SmileyXEyes } from "phosphor-react";
-import { isDarkThemeSelector } from "@/state/selectors";
-import { searchQueryAtom } from "@/state/atoms";
+import { searchQueryAtom, isDarkThemeSelector } from "@/state";
interface NoticeProps {
message?: string;
diff --git a/src/components/SearchInput/SearchInput.tsx b/src/components/SearchInput/SearchInput.tsx
index 688f6e3..de10ec7 100644
--- a/src/components/SearchInput/SearchInput.tsx
+++ b/src/components/SearchInput/SearchInput.tsx
@@ -11,7 +11,7 @@ import { useHotkeys } from "react-hotkeys-hook";
import { Command, MagnifyingGlass, X, HourglassHigh } from "phosphor-react";
import ReactGA from "react-ga4";
-import { searchQueryAtom } from "@/state/atoms";
+import { searchQueryAtom } from "@/state";
import "./SearchInput.css";
const apple = /iPhone|iPod|iPad|Macintosh|MacIntel|MacPPC/i;
diff --git a/src/components/SettingsActions/SettingsActions.tsx b/src/components/SettingsActions/SettingsActions.tsx
index ea9b34e..9de39af 100644
--- a/src/components/SettingsActions/SettingsActions.tsx
+++ b/src/components/SettingsActions/SettingsActions.tsx
@@ -1,9 +1,13 @@
import { useRecoilValue, useResetRecoilState } from "recoil";
import { ArrowCounterClockwise, CheckCircle, Link } from "phosphor-react";
-import { iconWeightAtom, iconSizeAtom, iconColorAtom } from "@/state/atoms";
-import useTransientState from "@/hooks/useTransientState";
-import { resetSettingsSelector } from "@/state/selectors";
+import { useTransientState } from "@/hooks";
+import {
+ iconWeightAtom,
+ iconSizeAtom,
+ iconColorAtom,
+ resetSettingsSelector,
+} from "@/state";
import "./SettingsActions.css";
diff --git a/src/components/SizeInput/SizeInput.tsx b/src/components/SizeInput/SizeInput.tsx
index 071077f..7d8aaf9 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";
import "./SizeInput.css";
type SizeInputProps = {};
diff --git a/src/components/StyleInput/StyleInput.tsx b/src/components/StyleInput/StyleInput.tsx
index f2843ff..0028094 100644
--- a/src/components/StyleInput/StyleInput.tsx
+++ b/src/components/StyleInput/StyleInput.tsx
@@ -4,7 +4,7 @@ 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";
import "./StyleInput.css";
diff --git a/src/components/Tabs/Tabs.css b/src/components/Tabs/Tabs.css
index 36428ae..37a7a52 100644
--- a/src/components/Tabs/Tabs.css
+++ b/src/components/Tabs/Tabs.css
@@ -1,15 +1,12 @@
.tabs {
display: flex;
flex-direction: column;
- border-left: 2px solid rgba(163, 159, 171, 0.1);
- border-right: 2px solid rgba(163, 159, 171, 0.1);
}
.tabs-header {
display: flex;
align-items: center;
gap: 8px;
- border-bottom: 2px solid rgba(163, 159, 171, 0.1);
}
button.tab {
@@ -19,14 +16,25 @@ button.tab {
text-align: center;
cursor: pointer;
flex: 1;
+ border-top-left-radius: 8px;
+ border-top-right-radius: 8px;
+}
+
+button.tab:focus-within {
+ /* background-color: var(--tabs-background); */
}
.tab.active {
- background-color: rgba(194, 186, 196, 0.25);
+ background-color: var(--tabs-background);
+ border-bottom: none;
}
.tab-content {
flex: 1;
+ padding: 8px 16px;
display: grid;
place-items: center;
+ border-radius: 8px;
+ background-color: var(--tabs-background);
+ overflow-y: auto;
}
diff --git a/src/components/Tabs/Tabs.tsx b/src/components/Tabs/Tabs.tsx
index 5de51a2..341f6f2 100644
--- a/src/components/Tabs/Tabs.tsx
+++ b/src/components/Tabs/Tabs.tsx
@@ -1,4 +1,7 @@
-import { ReactNode, useState } from "react";
+import { CSSProperties, ReactNode, useState } from "react";
+import { useRecoilValue } from "recoil";
+
+import { isDarkThemeSelector } from "@/state";
import "./Tabs.css";
@@ -11,15 +14,37 @@ type TabsProps = {
tabs: Tab[];
};
+type CSSCustomPropertyName = `--${string}`;
+
+type CSSCustomProperties = {
+ [property: CSSCustomPropertyName]: string;
+};
+
+const colorStyles: Record = {
+ light: { "--tabs-background": "white" },
+ dark: { "--tabs-background": "rgba(194, 186, 196, 0.25)" },
+} as const;
+
+const contentStyles: Record = {
+ activeLeft: { borderTopLeftRadius: 0 },
+ activeRight: { borderTopRightRadius: 0 },
+} as const;
+
const Tabs = ({ tabs }: TabsProps) => {
const [activeIndex, setActiveIndex] = useState(0);
+ const isDark = useRecoilValue(isDarkThemeSelector);
return (
-
+
{tabs.map((tab, i) => (
))}
- {tabs[activeIndex]?.content}
+
+ {tabs[activeIndex]?.content}
+
);
};
diff --git a/src/hooks/index.ts b/src/hooks/index.ts
new file mode 100644
index 0000000..17d8bf1
--- /dev/null
+++ b/src/hooks/index.ts
@@ -0,0 +1,9 @@
+export { default as useDebounce } from "./useDebounce";
+export { default as useEvent } from "./useEvent";
+export { default as useIconParameters } from "./useIconParameters";
+export { default as usePersistSettings } from "./usePersistSettings";
+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";
diff --git a/src/hooks/useEvent.ts b/src/hooks/useEvent.ts
new file mode 100644
index 0000000..27929a9
--- /dev/null
+++ b/src/hooks/useEvent.ts
@@ -0,0 +1,45 @@
+import { useEffect } from "react";
+
+export type UseEventTarget = HTMLElement | SVGElement | Document | Window;
+
+export type UseEventMap = E extends HTMLElement
+ ? HTMLElementEventMap
+ : E extends SVGElement
+ ? SVGElementEventMap
+ : E extends Document
+ ? DocumentEventMap
+ : WindowEventMap;
+
+export type UseEventType = keyof UseEventMap;
+
+/**
+ * Attach event listeners to arbitrary targets, and perform necessary cleanup
+ * when unmounting. Provides type inference for the listener based on the
+ * provided event name (currently supports {@link Window}, {@link Document},
+ * and subclasses of {@link HTMLElement} and {@link SVGElement}).
+ *
+ * @param type an {@link https://developer.mozilla.org/en-US/docs/Web/Events#event_listing event type}
+ * @param listener a callback to be fired on the event
+ * @param options {@link AddEventListenerOptions}
+ * @param el the target element to attack the listener. Defaults to
+ * {@link Document} when omitted.
+ */
+export default function useEvent<
+ K extends UseEventType,
+ M extends UseEventMap,
+ T extends UseEventTarget = Document
+>(
+ type: K,
+ listener: (this: T, ev: M[K]) => any,
+ options?: boolean | AddEventListenerOptions,
+ el?: T
+) {
+ useEffect(() => {
+ const target = el ?? document;
+ // @ts-ignore
+ target.addEventListener(type, listener, options);
+
+ // @ts-ignore
+ return () => target.removeEventListener(type, listener);
+ }, [el, type]);
+}
diff --git a/src/state/index.ts b/src/state/index.ts
new file mode 100644
index 0000000..bd7725b
--- /dev/null
+++ b/src/state/index.ts
@@ -0,0 +1,2 @@
+export * from "./atoms";
+export * from "./selectors";