Skip to content

Commit

Permalink
feat: upload dynamic icon in isolation (#44)
Browse files Browse the repository at this point in the history
* Rework dynamic icons component

* Add icon/[iconSymbol] page to make sure that icons loaded separately

* Remove 'use client' in DynamicIcon

* Use <GithubSVGIcon /> in <StaticIcon />. Get rid of useEffect

* Create UnknownIcon component

* Move GithubSvgIcon, Image, SVG, UnknownIcon into Base directory

* Fix mono icon displaying

---------

Co-authored-by: Aleksei <al.gorbunov@slotegrator.space>
  • Loading branch information
isArlekin and Aleksei authored Oct 26, 2024
1 parent 28899a4 commit 8613032
Show file tree
Hide file tree
Showing 18 changed files with 2,833 additions and 2,323 deletions.
29 changes: 29 additions & 0 deletions apps/docs/src/app/icon/[iconSymbol]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use client";

import { Web3Icon } from "@bgd-labs/react-web3-icons";
import { FC } from "react";

import { IconLoader } from "@/components/IconCard";

type PageProps = {
params: {
iconSymbol: string;
};
};

const Page: FC<PageProps> = ({ params }) => {
const { iconSymbol } = params;
return (
<div>
<Web3Icon
symbol={iconSymbol}
assetTag="a"
mono={false}
loader={<IconLoader />}
className="size-[70px]"
/>
</div>
);
};

export default Page;
4 changes: 4 additions & 0 deletions apps/docs/src/components/AssetIconCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { Web3Icon } from "@bgd-labs/react-web3-icons";
import { githubIconsPath } from "@bgd-labs/react-web3-icons/dist/constants";
import { IconVariant } from "@bgd-labs/react-web3-icons/dist/utils";
import { useRouter } from "next/navigation";
import { useState } from "react";

import { IconCard, IconLoader } from "@/components/IconCard";
Expand All @@ -24,6 +25,8 @@ export const AssetIconCard = ({
chainName?: string;
assetTag?: "a" | "stata" | "stk";
}) => {
const router = useRouter();

const [variant, setVariant] = useState(IconVariant.Full);
const iconPath = assetTag
? // eslint-disable-next-line @typescript-eslint/ban-ts-comment
Expand Down Expand Up @@ -53,6 +56,7 @@ export const AssetIconCard = ({
fileName={`${(assetTag ? assetTag : "").toLowerCase()}${symbol.toUpperCase()}${variant === IconVariant.Full ? "" : variant}`}
setActiveType={setVariant}
activeType={variant}
onTitleClick={() => router.push(`/icon/${symbol}`)}
>
<Web3Icon
className="size-[70px]"
Expand Down
9 changes: 8 additions & 1 deletion apps/docs/src/components/IconCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const IconCard = ({
fileName,
setActiveType,
activeType,
onTitleClick,
}: {
children: ReactNode;
name: string;
Expand All @@ -39,6 +40,7 @@ export const IconCard = ({
fileName: string;
setActiveType: (type: IconVariant) => void;
activeType: IconVariant;
onTitleClick?: () => void;
}) => {
const handleSetIconVariant = () => {
if (activeType === IconVariant.Full) {
Expand All @@ -52,7 +54,12 @@ export const IconCard = ({
<Box>
<div className="relative flex min-h-[285px] w-[200px] flex-col justify-center overflow-hidden rounded-lg pb-4">
<div className="relative flex-1 p-2">
<div className="text-sm font-semibold text-gray-800">{name}</div>
<div
onClick={onTitleClick}
className="cursor-pointer text-sm font-semibold text-gray-800"
>
{name}
</div>
{subName && (
<div className="font-mono text-xs uppercase text-gray-400">
{subName}
Expand Down
29 changes: 29 additions & 0 deletions packages/react-web3-icons/src/components/Base/GithubSVGIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React, { FC } from "react";
import InlineSVG from "react-inlinesvg";

import { IconComponentBaseProps } from "../../utils";
import { generateUniqueHash } from "../../utils/generateUniqueHash";

type GithubSVGIconProps = {
githubSrc: string;
} & IconComponentBaseProps;

const GithubSvgIcon: FC<GithubSVGIconProps> = ({
githubSrc,
loader,
...props
}) => {
return (
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
<InlineSVG
{...props}
src={githubSrc}
uniqueHash={generateUniqueHash()}
uniquifyIDs
loader={loader}
/>
);
};

export default GithubSvgIcon;
17 changes: 17 additions & 0 deletions packages/react-web3-icons/src/components/Base/Image.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React, { ComponentProps } from "react";

export const Image = ({
svgCode,
...props
}: { svgCode: string } & ComponentProps<"img">) => {
return (
<img
{...props}
draggable={false}
onDragStart={(e) => e.preventDefault()}
src={`data:image/svg+xml;base64,${btoa(svgCode)}`}
style={{ outline: "none !important", pointerEvents: "none" }}
alt={props.alt}
/>
);
};
20 changes: 20 additions & 0 deletions packages/react-web3-icons/src/components/Base/SVG.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"use client";

import React, { ComponentProps } from "react";

import { Image } from "./Image";
import { UnknownIcon } from "./UnknownIcon";

type SVGProps = {
svgCode?: string;
} & ComponentProps<"img">;

/**
* Wrapper for get svg image from svg code
*/
export const SVG = ({ svgCode, ...props }: SVGProps) => {
if (!svgCode) {
return <UnknownIcon />;
}
return <Image svgCode={svgCode} {...props} />;
};
10 changes: 10 additions & 0 deletions packages/react-web3-icons/src/components/Base/UnknownIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React, { ComponentProps } from "react";

import { iconUnknown } from "../../icons/full/build/icon-unknown.icon";
import { Image } from "./Image";

type UnknownIconProps = ComponentProps<"img">;

export const UnknownIcon = (props: UnknownIconProps) => {
return <Image svgCode={iconUnknown.data} {...props} />;
};
83 changes: 0 additions & 83 deletions packages/react-web3-icons/src/components/DynamicIcon.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React, { FC } from "react";

import { Web3IconType } from "../../icons/full";
import { IconComponentBaseProps } from "../../utils";
import GithubSVGIcon from "../Base/GithubSVGIcon";
import { LoadableIcon } from "./LoadableIcon";

export type DynamicIconProps = IconComponentBaseProps & {
iconKey: Web3IconType | string;
githubSrc?: string;
};

/**
* Wrapper for get icons dynamically
*/
export const DynamicIcon: FC<DynamicIconProps> = ({
githubSrc,
loader,
...props
}) => {
return (
<LoadableIcon
{...props}
fallback={loader}
fallbackComponent={
githubSrc ? (
<GithubSVGIcon githubSrc={githubSrc} loader={loader} />
) : undefined
}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import loadable from "@loadable/component";
import camelCase from "lodash.camelcase";
import React, { JSX } from "react";

import { Web3IconType } from "../../icons/full";
import {
capitalize,
formatMonoSvgCode,
IconComponentBaseProps,
} from "../../utils";
import { SVG } from "../Base/SVG";

export type LoadableIconProps = IconComponentBaseProps & {
iconKey: Web3IconType | string;
fallbackComponent?: JSX.Element;
};

export const LoadableIcon = loadable(
async ({ iconKey, mono, fallbackComponent, ...props }: LoadableIconProps) => {
try {
const lowerCasedIconKey = iconKey.toLowerCase();
const iconFileName = `icon-${lowerCasedIconKey}`;
const folder = mono ? "mono" : "full";
const icon = await import(
`../../icons/${folder}/build/${iconFileName}.icon.ts`
);
const iconData = icon[`icon${capitalize(camelCase(lowerCasedIconKey))}`];

if (!iconData && fallbackComponent) {
return {
default: () => <>{fallbackComponent}</>,
};
}

return {
default: () => (
<SVG
svgCode={formatMonoSvgCode({
mono,
svgCode: iconData?.data,
...props,
})}
{...props}
/>
),
};
} catch (e) {
return {
default: () => <SVG svgCode={undefined} {...props} />,
};
}
},
{
ssr: true,
cacheKey: ({ iconKey, mono }) => `${iconKey}-${mono}`,
},
);
Loading

0 comments on commit 8613032

Please sign in to comment.