Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ add IconButton Component #1636

Merged
merged 11 commits into from
Sep 19, 2024
682 changes: 682 additions & 0 deletions site-new/assets/icons/GoIcon.tsx

Large diffs are not rendered by default.

678 changes: 678 additions & 0 deletions site-new/assets/icons/GoMobile.tsx

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions site-new/assets/icons/JSIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as React from "react";
import { SVGProps } from "react";
const JSIcon = ({
height = 60,
width = 60,
...props
}: SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={width}
height={height}
fill="none"
{...props}
>
<path
fill="#FFEC19"
fillRule="evenodd"
d="M60 0H0v60h60V0ZM47.191 38.323c5.171 2.096 6.917 4.345 7.346 7.022.413 2.304-.1 3.798-.178 4.022-1.907 6.526-12.544 6.738-16.795 2.427-.128-.145-.25-.28-.365-.409-.693-.77-1.173-1.304-1.632-2.308l2.274-1.308c.55-.317 1.099-.635 2.183-1.263 1.194 1.835 2.298 2.844 4.284 3.257 2.694.33 5.405-.597 4.797-3.458-.362-1.355-2.158-2.115-4.253-3.002-1.519-.643-3.194-1.353-4.594-2.403-3.38-2.27-4.172-7.787-1.394-10.938.926-1.166 2.504-2.036 4.161-2.454l1.729-.224c3.319-.067 5.394.81 6.917 2.51.423.43.769.893 1.416 1.896l-.91.58c-.965.614-1.378.876-3.385 2.176-.541-1.166-1.44-1.896-2.387-2.214-1.473-.447-3.33.039-3.715 1.595-.134.48-.106.926.106 1.718.483 1.102 1.887 1.699 3.357 2.324.346.148.696.296 1.038.454Zm-20.12-10.694h5.49c0 1.675.002 3.348.005 5.02.006 3.366.012 6.729-.006 10.092.001.455.009.904.016 1.346.041 2.551.078 4.867-.987 6.83-.864 1.758-2.515 2.912-4.428 3.47-2.94.675-5.752.29-7.843-.965-1.4-.86-2.493-2.181-3.235-3.704l4.462-2.733c.036.015.134.188.267.422l.046.08c.568.954 1.06 1.628 2.024 2.102.949.324 3.03.53 3.832-1.137.392-.677.372-2.574.348-4.847-.007-.575-.013-1.175-.013-1.785l.023-14.19Z"
clipRule="evenodd"
/>
</svg>
);
export default JSIcon;
19 changes: 19 additions & 0 deletions site-new/assets/icons/JSMobile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as React from "react";
import { SVGProps } from "react";
const JSMobile = (props: SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={40}
height={40}
fill="none"
{...props}
>
<path
fill="#FFEC19"
fillRule="evenodd"
d="M40 0H0v40h40V0Zm-8.54 25.548c3.448 1.398 4.612 2.897 4.898 4.682.276 1.536-.067 2.532-.118 2.682-1.272 4.35-8.364 4.491-11.197 1.617l-.244-.272c-.462-.514-.781-.87-1.088-1.539l1.516-.872 1.456-.842c.796 1.223 1.532 1.896 2.856 2.172 1.796.219 3.603-.398 3.198-2.306-.242-.903-1.44-1.41-2.836-2.002-1.012-.428-2.129-.901-3.062-1.601-2.253-1.514-2.782-5.191-.93-7.293.617-.777 1.67-1.357 2.774-1.636l1.153-.149c2.212-.044 3.596.54 4.611 1.674.283.286.513.595.944 1.264l-.606.386c-.644.41-.919.585-2.257 1.451-.36-.777-.96-1.264-1.592-1.476-.981-.298-2.22.026-2.476 1.063-.09.32-.07.617.07 1.146.322.734 1.258 1.132 2.239 1.55.23.097.464.197.692.301ZM18.049 18.42h3.659c0 1.116.002 2.231.004 3.345.004 2.245.008 4.487-.005 6.729.002.303.006.603.011.898.028 1.7.053 3.244-.657 4.553-.577 1.172-1.677 1.941-2.953 2.313-1.96.45-3.834.193-5.228-.644-.934-.572-1.663-1.453-2.157-2.469l2.975-1.822c.023.01.089.126.178.282l.03.053c.38.636.706 1.085 1.35 1.402.632.215 2.02.353 2.555-.759.26-.45.247-1.716.231-3.23-.004-.385-.008-.785-.008-1.191l.015-9.46Z"
clipRule="evenodd"
/>
</svg>
);
export default JSMobile;
32 changes: 32 additions & 0 deletions site-new/assets/icons/Kotlin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as React from "react";
import { SVGProps } from "react";
const Kotlin = (props: SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={60}
height={60}
fill="none"
{...props}
>
<path fill="#fff" d="M0 0h60L30.466 29.534 60 60H0V0Z" />
<path
fill="url(#paint0_linear_102_14452)"
d="M0 0h60L30.466 29.534 60 60H0V0Z"
/>
<defs>
<linearGradient
x1={60}
x2={0}
y1={0}
y2={60}
gradientUnits="userSpaceOnUse"
id="paint0_linear_102_14452"
>
<stop offset={0.003} stopColor="#E44857" />
<stop offset={0.469} stopColor="#C711E1" />
<stop offset={1} stopColor="#7F52FF" />
</linearGradient>
</defs>
</svg>
);
export default Kotlin;
29 changes: 29 additions & 0 deletions site-new/assets/icons/KotlinMobile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as React from "react";
import { SVGProps } from "react";
const KotlinMobile = (props: SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={40}
height={40}
fill="none"
{...props}
>
<path fill="#fff" d="M0 0h40L20.31 19.69 40 40H0V0Z" />
<path fill="url(#kotlinIconMobile)" d="M0 0h40L20.31 19.69 40 40H0V0Z" />
<defs>
<linearGradient
id="kotlinIconMobile"
x1={40}
x2={0}
y1={0}
y2={40}
gradientUnits="userSpaceOnUse"
>
<stop offset={0.003} stopColor="#E44857" />
<stop offset={0.469} stopColor="#C711E1" />
<stop offset={1} stopColor="#7F52FF" />
</linearGradient>
</defs>
</svg>
);
export default KotlinMobile;
25 changes: 25 additions & 0 deletions site-new/assets/icons/Swift.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as React from "react";
import { SVGProps } from "react";
const Swift = ({
height = 60,
width = 60,
...props
}: SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={width}
height={height}
fill="none"
{...props}
>
<path
fill="#F05138"
d="M59.996 16.619a84.175 84.175 0 0 0-.027-1.81c-.035-1.315-.113-2.641-.346-3.941-.237-1.32-.625-2.548-1.236-3.748a12.592 12.592 0 0 0-5.508-5.508c-1.198-.61-2.426-.997-3.745-1.234-1.301-.234-2.627-.311-3.942-.347a86.595 86.595 0 0 0-1.81-.027C42.664 0 41.947 0 41.231 0H18.768c-.717 0-1.433 0-2.15.004-.604.004-1.207.01-1.81.027-.33.009-.659.02-.988.036-.99.048-1.98.135-2.955.311-.989.178-1.927.44-2.84.818A12.631 12.631 0 0 0 3.93 3.93a12.587 12.587 0 0 0-2.317 3.19C1.002 8.32.615 9.547.378 10.868c-.234 1.3-.311 2.626-.347 3.94a86.659 86.659 0 0 0-.027 1.81c-.005.717-.004 1.434-.004 2.15v22.463c0 .717 0 1.433.004 2.15.004.604.01 1.207.027 1.81.036 1.315.113 2.641.347 3.94.237 1.32.624 2.55 1.235 3.749a12.585 12.585 0 0 0 5.509 5.508c1.198.61 2.425.997 3.745 1.234 1.3.234 2.627.312 3.942.347.603.016 1.207.023 1.81.026.717.006 1.433.005 2.15.005H41.23c.717 0 1.434 0 2.15-.005.604-.003 1.207-.01 1.81-.026 1.316-.035 2.642-.113 3.943-.347 1.319-.237 2.547-.624 3.745-1.234a12.589 12.589 0 0 0 5.508-5.508c.61-1.2.998-2.428 1.235-3.748.234-1.3.312-2.626.347-3.94.016-.604.023-1.207.027-1.81.004-.718.004-1.434.004-2.151V18.769c0-.717 0-1.434-.004-2.15Z"
/>
<path
fill="#fff"
d="m47.542 37.034-.004-.005c.066-.226.135-.45.193-.682 2.49-9.921-3.588-21.651-13.873-27.827 4.507 6.11 6.5 13.51 4.73 19.982a16.801 16.801 0 0 1-.558 1.67c-.228-.15-.514-.32-.9-.532 0 0-10.23-6.317-21.319-17.49-.291-.293 5.913 8.867 12.953 16.305-3.317-1.861-12.561-8.587-18.413-13.943.719 1.199 1.574 2.353 2.514 3.465 4.887 6.198 11.26 13.845 18.896 19.717-5.365 3.283-12.945 3.538-20.493.003a30.962 30.962 0 0 1-5.247-3.13c3.195 5.11 8.116 9.52 14.105 12.094 7.142 3.07 14.244 2.861 19.534.05l-.004.006.08-.047c.217-.117.432-.236.643-.361 2.542-1.32 7.562-2.658 10.256 2.585.66 1.283 2.062-5.517-3.093-11.86Z"
/>
</svg>
);
export default Swift;
21 changes: 21 additions & 0 deletions site-new/assets/icons/SwiftMobile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as React from "react";
import { SVGProps } from "react";
const SwiftMobile = (props: SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={40}
height={40}
fill="none"
{...props}
>
<path
fill="#F05138"
d="M39.997 11.08a55.89 55.89 0 0 0-.018-1.208c-.023-.876-.075-1.76-.23-2.627a8.861 8.861 0 0 0-.824-2.498 8.396 8.396 0 0 0-3.672-3.672 8.865 8.865 0 0 0-2.497-.823c-.867-.156-1.751-.208-2.628-.231-.403-.011-.805-.016-1.207-.018C28.443 0 27.965 0 27.488 0H12.512c-.478 0-.955 0-1.433.003-.402.002-.805.007-1.207.018-.219.006-.438.013-.658.024-.66.032-1.32.09-1.97.207a9.141 9.141 0 0 0-1.893.545A8.421 8.421 0 0 0 2.62 2.62a8.392 8.392 0 0 0-1.546 2.127 8.863 8.863 0 0 0-.823 2.498C.096 8.112.045 8.995.02 9.872c-.011.403-.016.805-.018 1.207C0 11.557 0 12.035 0 12.512V27.487c0 .479 0 .956.003 1.434.002.402.007.804.018 1.206.024.877.075 1.761.23 2.628.159.88.417 1.699.824 2.498a8.382 8.382 0 0 0 3.672 3.672 8.865 8.865 0 0 0 2.498.823c.866.156 1.75.208 2.627.231.402.01.805.016 1.208.018.477.004.954.003 1.432.003h14.976c.477 0 .955 0 1.433-.003.402-.002.804-.007 1.207-.018.877-.023 1.76-.075 2.628-.231a8.868 8.868 0 0 0 2.497-.823 8.392 8.392 0 0 0 3.672-3.672 8.86 8.86 0 0 0 .823-2.498c.156-.867.208-1.751.231-2.628.011-.402.016-.804.018-1.206.003-.478.003-.956.003-1.434V12.512c0-.477 0-.955-.003-1.433Z"
/>
<path
fill="#fff"
d="M31.695 24.69c0-.002-.002-.003-.003-.004.044-.15.09-.3.129-.454 1.66-6.615-2.392-14.435-9.248-18.552 3.004 4.073 4.333 9.007 3.152 13.321-.105.385-.231.754-.371 1.113a9.239 9.239 0 0 0-.6-.354S17.934 15.549 10.54 8.1c-.194-.196 3.942 5.911 8.636 10.87-2.212-1.24-8.375-5.725-12.276-9.296.48.8 1.05 1.57 1.676 2.31 3.258 4.132 7.507 9.23 12.597 13.145-3.576 2.189-8.63 2.36-13.662.002a20.642 20.642 0 0 1-3.497-2.086 20.949 20.949 0 0 0 9.403 8.062c4.761 2.047 9.496 1.908 13.022.034l-.003.004.054-.032c.145-.078.288-.157.429-.24 1.694-.88 5.04-1.772 6.837 1.723.44.855 1.375-3.678-2.062-7.906Z"
/>
</svg>
);
export default SwiftMobile;
21 changes: 21 additions & 0 deletions site-new/src/components/IconButton/IconButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { cn } from "@site/lib/utils";
import React from "react";

type ButtonProps = Omit<React.ComponentProps<"button">, "children"> & {
children: React.ReactNode;
};

export const iconButtonClasses =
"unset w-max cursor-pointer leading-[0] outline-none hover:opacity-50 focus-visible:ring-1 focus-visible:ring-white active:opacity-20 transition-opacity";

const IconButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, children, ...props }, ref) => {
return (
<button {...props} ref={ref} className={cn(iconButtonClasses, className)}>
{children}
</button>
);
},
);

export default IconButton;
18 changes: 18 additions & 0 deletions site-new/src/components/IconButton/IconButtonLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Link from "@docusaurus/Link";
import { cn } from "@site/lib/utils";
import React from "react";
import { iconButtonClasses } from "./IconButton";

type LinkProps = React.ComponentProps<typeof Link>;

const IconButtonLink = React.forwardRef<HTMLAnchorElement, LinkProps>(
({ className, children, ...props }, ref) => {
return (
<Link {...props} ref={ref} className={cn(iconButtonClasses, className)}>
{children}
</Link>
);
},
);

export default IconButtonLink;
2 changes: 2 additions & 0 deletions site-new/src/components/IconButton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as IconButton } from "./IconButton";
export { default as IconButtonLink } from "./IconButtonLink";
1 change: 1 addition & 0 deletions site-new/src/components/ImageCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ function ImageCard({
className={`${currentSize.image} object-cover`}
/>
<TextIconCard
type="buttonText"
hasBorder={false}
theme="yellow"
title={title}
Expand Down
137 changes: 118 additions & 19 deletions site-new/src/components/TextIconCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,113 @@ import React from "react";
import TbdArrow from "@site/static/img/tbd-arrow";
import Heading from "@theme/Heading";
import Link from "@docusaurus/Link";
import Swift from "@site/assets/icons/Swift";
import SwiftMobile from "@site/assets/icons/SwiftMobile";
import Kotlin from "@site/assets/icons/Kotlin";
import JSIcon from "@site/assets/icons/JSIcon";
import GoIcon from "@site/assets/icons/GoIcon";
import { cn } from "@site/lib/utils";
import JSMobile from "@site/assets/icons/JSMobile";
import GoMobile from "@site/assets/icons/GoMobile";
import KotlinMobile from "@site/assets/icons/KotlinMobile";
import { IconButtonLink } from "./IconButton";

type Theme = "yellow" | "teal" | "purple" | "grey";
type Theme =
| "yellow"
| "teal"
| "purple"
| "grey"
| "iconyellow"
| "iconteal"
| "iconpurple"
| "icongrey";

type Languages = "swift" | "kotlin" | "js" | "go";

type ButtonProps = {
type: "buttonText";
buttonText: string;
url: string;
};

type LanguageButtonProps = {
type: "languageButton";
resources: Partial<Record<Languages, string>>;
};

const ResponsiveIcon = ({
mobileIcon,
desktopIcon,
}: {
mobileIcon: React.ReactNode;
desktopIcon: React.ReactNode;
}) => (
<>
<span className="hidden lg:inline">{desktopIcon}</span>
<span className="lg:hidden">{mobileIcon}</span>
</>
);

const languageIconMap: Record<Languages, JSX.Element> = {
swift: (
<ResponsiveIcon mobileIcon={<SwiftMobile />} desktopIcon={<Swift />} />
),
js: <ResponsiveIcon mobileIcon={<JSMobile />} desktopIcon={<JSIcon />} />,
go: <ResponsiveIcon mobileIcon={<GoMobile />} desktopIcon={<GoIcon />} />,
kotlin: (
<ResponsiveIcon mobileIcon={<KotlinMobile />} desktopIcon={<Kotlin />} />
),
};

type DefaultProps = {
type?: "default";
};

type TextIconCardProps = {
icon?: React.ComponentType<{ className: string; fill?: string }>;
title: string;
text: string;
url?: string;
className?: string;
theme?: Theme;
buttonText?: string;
hasBorder?: boolean;
} & (ButtonProps | LanguageButtonProps | DefaultProps);

const themeClasses: Record<Theme, string> = {
yellow: "text-white ",
teal: "text-white ",
purple: "text-white ",
grey: "text-white ",
iconyellow: "fill-tbd-yellow ",
iconteal: "fill-tbd-teal ",
iconpurple: "fill-tbd-purple-tint-2 ",
icongrey: "fill-tbd-grey ",
};

const themeClasses = {
yellow: "text-white hover:bg-tbd-yellow hover:text-tbd-gray",
teal: "text-white hover:bg-tbd-teal hover:text-tbd-gray",
purple: "text-white hover:bg-tbd-purple hover:text-tbd-gray",
grey: "text-white hover:bg-dark-grey hover:text-tbd-gray",
iconyellow: "fill-tbd-yellow group-hover:fill-tbd-gray",
iconteal: "fill-tbd-teal group-hover:fill-tbd-gray",
iconpurple: "fill-tbd-purple-tint-2 group-hover:fill-tbd-gray",
icongrey: "fill-tbd-grey group-hover:fill-tbd-gray",
const themeClassesHover: Record<Theme, string> = {
yellow: "hover:bg-tbd-yellow hover:text-tbd-gray",
teal: "hover:bg-tbd-teal hover:text-tbd-gray",
purple: "hover:bg-tbd-purple hover:text-tbd-gray",
grey: "hover:bg-dark-grey hover:text-tbd-gray",
iconyellow: "group-hover:fill-tbd-gray",
iconteal: "group-hover:fill-tbd-gray",
iconpurple: "group-hover:fill-tbd-gray",
icongrey: "group-hover:fill-tbd-gray",
};

function TextIconCard({
icon: Icon,
title,
text,
url,
className = "",
theme = "grey",
buttonText,
hasBorder = true,
...props
}: TextIconCardProps) {
const themeClass = themeClasses[theme];
const isHoverEnabled =
props.type === "buttonText" || props.type === "default" || !props.type;
const themeClass = cn(themeClasses[theme], {
[themeClassesHover[theme]]: isHoverEnabled,
});
const iconClass = themeClasses[`icon${theme}`];

// Conditionally apply border styles based on `hasBorder` (only for TextIconCard)
Expand All @@ -58,26 +129,54 @@ function TextIconCard({
)}
<Heading
as="h3"
className={`mt-4 text-lg font-bold md:text-2xl text-tbd-${theme} transition-all duration-300 group-hover:text-tbd-gray`}
className={cn(
`mt-4 text-lg font-bold md:text-2xl text-tbd-${theme} transition-all duration-300`,
{
"group-hover:text-tbd-gray": isHoverEnabled,
},
)}
>
{title}
</Heading>
<p className={`mt-2 text-sm md:text-lg`}>{text}</p>
</div>
{url && buttonText && (
{props.type === "buttonText" && (
<Link
href={url}
href={props.url}
className={`mt-auto inline-flex w-fit items-center border-x-0 border-b-0 border-t-4 border-solid border-tbd-yellow bg-tbd-yellow px-4 pb-2 pt-2 text-[12px] text-sm text-tbd-gray transition-all duration-300 group-hover:border-t-white group-hover:bg-tbd-gray group-hover:text-white md:text-lg`}
target="_blank"
rel="noreferrer"
>
{buttonText}
{props.buttonText}
<TbdArrow
fill=""
className="ml-4 size-5 rotate-180 fill-tbd-gray transition-all duration-300 group-hover:fill-white md:size-6"
/>
</Link>
)}
<>
{props.type === "languageButton" &&
Object.keys(props.resources).some(
(key) => props.resources[key as Languages],
) && (
<div className="flex gap-twist-core-spacing-8">
{Object.keys(props.resources).map((key) => {
const value = props.resources[key as Languages];
return (
value && (
<IconButtonLink
key={key}
href={value}
className="leading-[0]"
>
{languageIconMap[key as Languages]}
</IconButtonLink>
)
);
})}
</div>
)}
</>
</div>
</div>
);
Expand Down
Loading
Loading