mirror of
https://github.com/ershisan99/DevToysWeb.git
synced 2025-12-16 20:49:23 +00:00
refactor: memoize icons on export
This commit is contained in:
@@ -83,11 +83,9 @@ export default function Page() {
|
||||
const onJsonChange: EditorProps["onChange"] = value => setJsonReactively(value ?? "");
|
||||
const onYamlChange: EditorProps["onChange"] = value => setYamlReactively(value ?? "");
|
||||
|
||||
const indentationIcon = useMemo(() => <icons.Space size={24} className="-translate-y-1.5" />, []);
|
||||
|
||||
const indentationConfig = (
|
||||
<Configuration
|
||||
icon={indentationIcon}
|
||||
icon={<icons.Space size={24} className="-translate-y-1.5" />}
|
||||
title="Indentation"
|
||||
control={
|
||||
<Select value={form.indentation} onValueChange={onIndentationChange}>
|
||||
|
||||
@@ -58,12 +58,10 @@ export default function Page() {
|
||||
const onOctChange: InputProps["onChange"] = ({ currentTarget: { value } }) => trySetOct(value);
|
||||
const onBinChange: InputProps["onChange"] = ({ currentTarget: { value } }) => trySetBin(value);
|
||||
|
||||
const formatNumberIcon = useMemo(() => <icons.CaseSensitive size={24} />, []);
|
||||
|
||||
const formatNumberConfig = useMemo(
|
||||
() => (
|
||||
<Configuration
|
||||
icon={formatNumberIcon}
|
||||
icon={<icons.CaseSensitive size={24} />}
|
||||
title="Format number"
|
||||
control={
|
||||
<LabeledSwitch
|
||||
@@ -76,7 +74,7 @@ export default function Page() {
|
||||
}
|
||||
/>
|
||||
),
|
||||
[format, formatNumberIcon]
|
||||
[format]
|
||||
);
|
||||
|
||||
const decPasteButton = useMemo(() => <PasteButton onClipboardRead={trySetDec} />, [trySetDec]);
|
||||
|
||||
@@ -41,12 +41,10 @@ export default function Page() {
|
||||
|
||||
const onJsonChange: EditorProps["onChange"] = value => setInput(value ?? "");
|
||||
|
||||
const indentationIcon = useMemo(() => <icons.Space size={24} className="-translate-y-1.5" />, []);
|
||||
|
||||
const indentationConfig = useMemo(
|
||||
() => (
|
||||
<Configuration
|
||||
icon={indentationIcon}
|
||||
icon={<icons.Space size={24} className="-translate-y-1.5" />}
|
||||
title="Indentation"
|
||||
control={
|
||||
<Select value={indentation} onValueChange={setIndentation}>
|
||||
@@ -66,7 +64,7 @@ export default function Page() {
|
||||
}
|
||||
/>
|
||||
),
|
||||
[indentation, indentationIcon]
|
||||
[indentation]
|
||||
);
|
||||
|
||||
const inputPasteButton = useMemo(() => <PasteButton onClipboardRead={setInput} />, []);
|
||||
|
||||
@@ -37,12 +37,10 @@ export default function Page() {
|
||||
const onInputChange: TextareaProps["onChange"] = ({ currentTarget: { value } }) =>
|
||||
setInput(value);
|
||||
|
||||
const uppercaseIcon = useMemo(() => <icons.CaseSensitive size={24} />, []);
|
||||
|
||||
const uppercaseConfig = useMemo(
|
||||
() => (
|
||||
<Configuration
|
||||
icon={uppercaseIcon}
|
||||
icon={<icons.CaseSensitive size={24} />}
|
||||
title="Uppercase"
|
||||
control={
|
||||
<LabeledSwitch
|
||||
@@ -55,7 +53,7 @@ export default function Page() {
|
||||
}
|
||||
/>
|
||||
),
|
||||
[uppercase, uppercaseIcon]
|
||||
[uppercase]
|
||||
);
|
||||
|
||||
const inputPasteButton = useMemo(() => <PasteButton onClipboardRead={setInput} />, []);
|
||||
|
||||
@@ -20,12 +20,10 @@ import { PageSection } from "@/components/page-section";
|
||||
export default function Page() {
|
||||
const { theme = "system", setTheme } = useTheme();
|
||||
|
||||
const appThemeIcon = useMemo(() => <icons.Paintbrush size={24} />, []);
|
||||
|
||||
const appThemeConfig = useMemo(
|
||||
() => (
|
||||
<Configuration
|
||||
icon={appThemeIcon}
|
||||
icon={<icons.Paintbrush size={24} />}
|
||||
title="App theme"
|
||||
description="Select which app theme to display"
|
||||
control={
|
||||
@@ -45,7 +43,7 @@ export default function Page() {
|
||||
}
|
||||
/>
|
||||
),
|
||||
[appThemeIcon, setTheme, theme]
|
||||
[setTheme, theme]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { icons } from "@/components/icons";
|
||||
|
||||
import { BaseButton, BaseButtonProps } from "./base";
|
||||
@@ -7,7 +5,5 @@ import { BaseButton, BaseButtonProps } from "./base";
|
||||
export type ClearButtonProps = Omit<BaseButtonProps, "icon" | "labelText">;
|
||||
|
||||
export function ClearButton({ iconOnly, ...props }: ClearButtonProps) {
|
||||
const icon = useMemo(() => <icons.X size={16} />, []);
|
||||
|
||||
return <BaseButton {...props} {...{ icon, iconOnly }} labelText="Clear" />;
|
||||
return <BaseButton {...props} icon={<icons.X size={16} />} {...{ iconOnly }} labelText="Clear" />;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { useCallback } from "react";
|
||||
|
||||
import { icons } from "@/components/icons";
|
||||
|
||||
@@ -18,7 +18,12 @@ export function CopyButton({ text, iconOnly, ...props }: CopyButtonProps) {
|
||||
});
|
||||
}, [text]);
|
||||
|
||||
const icon = useMemo(() => <icons.Copy size={16} />, []);
|
||||
|
||||
return <BaseButton {...props} {...{ icon, iconOnly, onClick }} labelText="Copy" />;
|
||||
return (
|
||||
<BaseButton
|
||||
{...props}
|
||||
icon={<icons.Copy size={16} />}
|
||||
{...{ iconOnly, onClick }}
|
||||
labelText="Copy"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useMemo, useRef } from "react";
|
||||
import { useCallback, useRef } from "react";
|
||||
|
||||
import { icons } from "@/components/icons";
|
||||
|
||||
@@ -55,11 +55,14 @@ export function FileButton({
|
||||
[maxFileSizeMb, onFileRead]
|
||||
);
|
||||
|
||||
const icon = useMemo(() => <icons.File size={16} />, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<BaseButton {...props} {...{ icon, iconOnly, onClick }} labelText="Load a file" />
|
||||
<BaseButton
|
||||
{...props}
|
||||
icon={<icons.File size={16} />}
|
||||
{...{ iconOnly, onClick }}
|
||||
labelText="Load a file"
|
||||
/>
|
||||
<input hidden type="file" {...{ ref, accept, onChange }} />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { useCallback } from "react";
|
||||
|
||||
import { icons } from "@/components/icons";
|
||||
|
||||
@@ -21,7 +21,12 @@ export function PasteButton({ iconOnly, onClipboardRead, ...props }: PasteButton
|
||||
});
|
||||
}, [onClipboardRead]);
|
||||
|
||||
const icon = useMemo(() => <icons.Clipboard size={16} />, []);
|
||||
|
||||
return <BaseButton {...props} {...{ icon, iconOnly, onClick }} labelText="Paste" />;
|
||||
return (
|
||||
<BaseButton
|
||||
{...props}
|
||||
icon={<icons.Clipboard size={16} />}
|
||||
{...{ iconOnly, onClick }}
|
||||
labelText="Paste"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { memo, MemoExoticComponent } from "react";
|
||||
import {
|
||||
AlignLeft,
|
||||
ArrowRightLeft,
|
||||
@@ -29,37 +30,38 @@ import {
|
||||
X,
|
||||
type Icon as LucideIcon,
|
||||
} from "lucide-react";
|
||||
import equal from "react-fast-compare";
|
||||
|
||||
export type Icon = LucideIcon;
|
||||
export type Icon = LucideIcon | MemoExoticComponent<LucideIcon>;
|
||||
|
||||
export const icons = {
|
||||
AlignLeft,
|
||||
ArrowRightLeft,
|
||||
Binary,
|
||||
Braces,
|
||||
CaseSensitive,
|
||||
Check,
|
||||
ChevronDown,
|
||||
Clipboard,
|
||||
Code: Code2,
|
||||
Copy,
|
||||
Equal,
|
||||
File: FileIcon,
|
||||
Fingerprint,
|
||||
Hash,
|
||||
Home,
|
||||
Key,
|
||||
Link: Link2,
|
||||
PackagePlus,
|
||||
Paintbrush: Paintbrush2,
|
||||
Search,
|
||||
Settings,
|
||||
Settings2,
|
||||
Space,
|
||||
Sun: SunMedium,
|
||||
Minus,
|
||||
Moon,
|
||||
X,
|
||||
AlignLeft: memo(AlignLeft, equal),
|
||||
ArrowRightLeft: memo(ArrowRightLeft, equal),
|
||||
Binary: memo(Binary, equal),
|
||||
Braces: memo(Braces, equal),
|
||||
CaseSensitive: memo(CaseSensitive, equal),
|
||||
Check: memo(Check, equal),
|
||||
ChevronDown: memo(ChevronDown, equal),
|
||||
Clipboard: memo(Clipboard, equal),
|
||||
Code: memo(Code2, equal),
|
||||
Copy: memo(Copy, equal),
|
||||
Equal: memo(Equal, equal),
|
||||
File: memo(FileIcon, equal),
|
||||
Fingerprint: memo(Fingerprint, equal),
|
||||
Hash: memo(Hash, equal),
|
||||
Home: memo(Home, equal),
|
||||
Key: memo(Key, equal),
|
||||
Link: memo(Link2, equal),
|
||||
PackagePlus: memo(PackagePlus, equal),
|
||||
Paintbrush: memo(Paintbrush2, equal),
|
||||
Search: memo(Search, equal),
|
||||
Settings: memo(Settings, equal),
|
||||
Settings2: memo(Settings2, equal),
|
||||
Space: memo(Space, equal),
|
||||
Sun: memo(SunMedium, equal),
|
||||
Minus: memo(Minus, equal),
|
||||
Moon: memo(Moon, equal),
|
||||
X: memo(X, equal),
|
||||
GitHub: (props: LucideProps) => (
|
||||
<svg viewBox="0 0 438.549 438.549" {...props}>
|
||||
<path
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useSetSearchText } from "@/contexts/search-text";
|
||||
|
||||
@@ -40,13 +40,6 @@ export function SearchBar() {
|
||||
inputRef.current?.focus();
|
||||
};
|
||||
|
||||
const clearIcon = useMemo(() => <icons.X className="p-1 text-muted-foreground" />, []);
|
||||
|
||||
const searchIcon = useMemo(
|
||||
() => <icons.Search className="-scale-x-100 p-1 text-muted-foreground" />,
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="relative flex w-full items-center">
|
||||
<Input
|
||||
@@ -59,11 +52,11 @@ export function SearchBar() {
|
||||
/>
|
||||
<div className="absolute right-1 flex gap-1">
|
||||
<Button className={cn("h-6 p-0", !text && "hidden")} variant="ghost" onClick={clearText}>
|
||||
{clearIcon}
|
||||
<icons.X className="p-1 text-muted-foreground" />
|
||||
<span className="sr-only">Clear search text</span>
|
||||
</Button>
|
||||
<Button className="h-6 p-0" variant="ghost" onClick={search} aria-label="search">
|
||||
{searchIcon}
|
||||
<icons.Search className="-scale-x-100 p-1 text-muted-foreground" />
|
||||
<span className="sr-only">Search tools</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useMemo, useRef } from "react";
|
||||
import { useCallback, useRef } from "react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import * as Accordion from "@radix-ui/react-accordion";
|
||||
|
||||
@@ -20,11 +20,6 @@ export function ToolGroup({ Icon, title, href, tools, isOpend }: Props) {
|
||||
|
||||
const onClick = useCallback(() => triggerRef.current?.click(), []);
|
||||
|
||||
const chevronIcon = useMemo(
|
||||
() => <icons.ChevronDown className="h-4 w-4 transition-transform duration-200" />,
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<Accordion.AccordionItem value={href}>
|
||||
<Accordion.Header asChild>
|
||||
@@ -46,7 +41,7 @@ export function ToolGroup({ Icon, title, href, tools, isOpend }: Props) {
|
||||
className="absolute right-0 flex h-10 w-10 items-center justify-center rounded transition-all duration-0 hover:bg-accent [&[data-state=open]>svg]:rotate-180"
|
||||
aria-label="toggle open/close state of the tool group"
|
||||
>
|
||||
{chevronIcon}
|
||||
<icons.ChevronDown className="h-4 w-4 transition-transform duration-200" />
|
||||
</Accordion.Trigger>
|
||||
</div>
|
||||
</Accordion.Header>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { memo, useMemo } from "react";
|
||||
import { memo } from "react";
|
||||
import Link, { LinkProps } from "next/link";
|
||||
|
||||
import { Tool } from "@/config/tools";
|
||||
@@ -13,8 +13,6 @@ type Props = Pick<Tool, "Icon" | "shortTitle"> &
|
||||
};
|
||||
|
||||
function RawToolLink({ Icon, shortTitle: title, href, onClick, className, highlight }: Props) {
|
||||
const icon = useMemo(() => <Icon size={16} />, [Icon]);
|
||||
|
||||
return (
|
||||
<Link
|
||||
className={cn(
|
||||
@@ -28,7 +26,7 @@ function RawToolLink({ Icon, shortTitle: title, href, onClick, className, highli
|
||||
<Indicator />
|
||||
</span>
|
||||
<span className="flex select-none items-center">
|
||||
{icon}
|
||||
<Icon size={16} />
|
||||
<span className="ml-4">{title}</span>
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { useMemo } from "react";
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -9,27 +8,14 @@ import { icons } from "@/components/icons";
|
||||
export function ThemeToggle() {
|
||||
const { resolvedTheme, setTheme } = useTheme();
|
||||
|
||||
const sunIcon = useMemo(
|
||||
() => (
|
||||
<icons.Sun className="h-7 w-7 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
||||
),
|
||||
[]
|
||||
);
|
||||
const moonIcon = useMemo(
|
||||
() => (
|
||||
<icons.Moon className="absolute h-7 w-7 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<Button
|
||||
className="h-10 w-10 p-0"
|
||||
variant="ghost"
|
||||
onClick={() => setTheme(resolvedTheme === "light" ? "dark" : "light")}
|
||||
>
|
||||
{sunIcon}
|
||||
{moonIcon}
|
||||
<icons.Sun className="h-7 w-7 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
||||
<icons.Moon className="absolute h-7 w-7 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
||||
<span className="sr-only">Toggle theme</span>
|
||||
</Button>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user