Skip to content

Commit

Permalink
feat: functional cards
Browse files Browse the repository at this point in the history
  • Loading branch information
guru-web3 committed Dec 10, 2024
1 parent 39eb597 commit 8e079da
Show file tree
Hide file tree
Showing 8 changed files with 818 additions and 0 deletions.
60 changes: 60 additions & 0 deletions demo/redirect-flow-example/src/components/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "./Card";

const avatarVariants = cva(
`relative inline-flex items-center justify-center overflow-hidden bg-app-gray-100 dark:bg-app-gray-600`,
{
variants: {
size: {
xs: "w-6 h-6",
sm: "w-8 h-8",
md: "w-10 h-10",
lg: "w-14 h-14",
xl: "w-20 h-20",
},
rounded: {
true: "rounded-full",
false: "rounded-lg",
},
border: {
true: "p-1 ring-2 ring-app-gray-300 dark:ring-app-gray-500",
false: "",
},
},
defaultVariants: {
size: "md",
rounded: true,
border: false,
},
},
);

export interface AvatarProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof avatarVariants> {
children: React.ReactNode;
id?: string;
classes?: Partial<Record<string, string>>;
}

const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
({ className, size, rounded, border, children, id, classes = {}, ...props }, ref) => {
return (
<div
id={id}
role="button"
className={cn(avatarVariants({ size, rounded, border }), classes.container, className)}
aria-hidden
ref={ref}
{...props}
>
{children}
</div>
);
},
);

Avatar.displayName = "Avatar";

export { Avatar, avatarVariants };
51 changes: 51 additions & 0 deletions demo/redirect-flow-example/src/components/DocDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import * as React from "react";
import { Button } from "./Button";
import { Card } from "./Card";
import { Link } from "./Link";

const DocDetails: React.FC = () => {
return (
<Card className="px-8 py-6 w-full !rounded-2xl !shadow-modal !border-0 dark:!border-app-gray-800 dark:!shadow-dark">
<div className="mb-4">
<h3 className="font-semibold text-app-gray-900 dark:text-app-white mb-1">
Experience Web3Auth, first hand
</h3>
<p className="text-xs text-app-gray-500 dark:text-app-gray-400">
Browse our full suite of features for your dApp with our docs. Access code examples for
these features by visiting our{" "}
<Link
href="https://web3auth.io/customers.html"
className="text-xs dark:text-app-primary-500"
target="_blank"
rel="noopener noreferrer"
>
playground
</Link>
.
</p>
</div>
<div className="space-y-2">
<Button
href="https://web3auth.io/docs"
size="sm"
className="gap-2 w-full !border-app-gray-300 !text-app-gray-800 dark:!text-app-white"
variant="secondary"
rel="noopener noreferrer"
>
Read our docs
</Button>
<Button
href="https://web3auth.io/customers"
size="sm"
className="gap-2 w-full !border-app-gray-300 !text-app-gray-800 dark:!text-app-white"
variant="secondary"
rel="noopener noreferrer"
>
Checkout Live Integrations
</Button>
</div>
</Card>
);
};

export { DocDetails };
86 changes: 86 additions & 0 deletions demo/redirect-flow-example/src/components/LoginCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import * as React from "react";
import { Card } from "./Card";
import { Icon } from "./Icon";
import { TextField } from "./TextField";
import { Button } from "./Button";
import { LoginForm } from "./LoginForm";
import { useSocialLogins } from "./useSocialLogin";
import { SocialLoginObj } from "./types";

interface LoginCardProps {
handleEmailPasswordLess: () => void;
handleSocialLogin: (item: SocialLoginObj) => void;
}

const LoginCard: React.FC<LoginCardProps> = ({handleEmailPasswordLess, handleSocialLogin}) => {
const [loginHint, setLoginHint] = React.useState<string>("");

const socialLogins = useSocialLogins();

const handlePasswordlessLogin = (e: React.FormEvent) => {
e.preventDefault();
console.log("loginHint", loginHint);
// Handle passwordless login
handleEmailPasswordLess();
};

const handleSocial = (item: SocialLoginObj, index: number) => {
// Handle social login
handleSocialLogin(item);
};

return (
<div className="mb-6 mt-24 flex justify-center items-center">
<Card
className="dapp-login-modal !shadow-modal !border-0 dark:!border-app-gray-800 dark:!shadow-dark"
classes={{ container: "!rounded-2xl p-8" }}
>
<div>
<div className="mb-6 flex justify-center items-center">
<img className="h-11 w-auto" src="https://images.web3auth.io/web3auth-logo-w.svg" alt="" />
</div>
<p className="text-2xl text-center font-bold text-app-gray-900 dark:text-app-white">Welcome to Web3Auth</p>
<p className="text-base text-center mb-5 text-app-gray-500 dark:text-app-gray-400">Login to continue</p>
</div>
<LoginForm
pill
socialLogins={socialLogins}
showInput={false}
// className="mt-6"
inputExpandBtnProps={{
pill: true,
size: "xs",
className: "!p-0 !h-[1em] active:!ring-0 focus:!ring-0 !rounded-0",
}}
socialLoginMessage="By continuing, you agree to our terms and conditions."
expandLabel="View more"
collapseLabel="View less"
primaryBtn="input"
onSocialLoginClick={ handleSocial }
>
<form onSubmit={handlePasswordlessLogin}>
<TextField
value={loginHint}
onChange={(e) => setLoginHint(e.target.value)}
label="Email or Phone"
pill={true}
type="email"
className="w-full"
required
placeholder="E.g. +00-123455/[email protected]"
/>
<Button type="submit" className="my-4" variant="primary" block>
Continue with Email or Phone
</Button>
</form>
</LoginForm>
<div className="flex items-center justify-center mt-8">
<img src="/assets/ws-trademark-light.svg" alt="web3auth logo" className="block dark:hidden h-5" />
<img src="/assets/ws-trademark-dark.svg" alt="web3auth logo" className="hidden dark:block h-5" />
</div>
</Card>
</div>
);
};

export { LoginCard };
156 changes: 156 additions & 0 deletions demo/redirect-flow-example/src/components/LoginForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import * as React from "react";
import { useState } from "react";
import { Button } from "./Button";
import { Icon } from "./Icon";
import { TextField } from "./TextField";
import { cn } from "./Card";
import { Image } from "./Image";
import { LoginFormProps } from "./types";

const LoginForm: React.FC<LoginFormProps> = ({
socialLogins = [],
pill = false,
expandLabel = "View More",
collapseLabel = "View Less",
socialLoginMessage = "We do not store any data related to your social logins.",
showInput = true,
inputBtnLabel = "",
inputProps = {},
inputExpandBtnProps = {},
gridCols = 3,
inputBtnProps = {},
primaryBtn = "social",
classes = {},
isExistingLogin = false,
existingLoginProps = {},
existingLabelInline = false,
divider = false,
onSocialLoginClick,
onExistingLoginClick,
onInputBtnClick,
children,
}) => {
const [viewMoreOptions, setViewMoreOptions] = useState(false);
const [imagesLoaded, setImagesLoaded] = useState(false);

const getIcon = (provider: string) => {
if (provider === "twitter") {
return "twitter-x";
}
return provider;
};

const toggleViewMoreOptions = () => {
setViewMoreOptions(!viewMoreOptions);
if (!imagesLoaded) {
setImagesLoaded(true);
}
};

const isHiddenIcon = (index: number) => {
if (imagesLoaded) return false;
return !viewMoreOptions && index > 3;
};

return (
<div className={cn("w-full flex flex-col", classes.container)}>
{isExistingLogin && existingLoginProps?.verifier && (
<Button
size={existingLabelInline ? "xl" : "md"}
className={cn("mb-4", classes.existingBtn)}
onClick={() => onExistingLoginClick && onExistingLoginClick(existingLoginProps)}
>
<div className="flex items-center justify-center gap-3">
<Icon
name={existingLoginProps?.icon || ""}
className={cn("w-6", existingLoginProps?.iconStyle)}
size={existingLoginProps?.iconSize || "24"}
/>
<p className={cn("text-sm font-medium flex items-center gap-1", { "flex-col": existingLabelInline })}>
<span className="capitalize">{existingLoginProps?.label || "Continue With"} {existingLoginProps?.verifier || ""}</span>
<span className="font-semibold">{existingLoginProps.loginHint}</span>
</p>
</div>
</Button>
)}
{socialLogins.length > 0 && (
<div className={cn(
"overflow-y-hidden grid gap-4 -m-1 p-1 transition-all duration-300 ease-in-out",
{ "max-h-[500px]": viewMoreOptions, "max-h-[106px]": !viewMoreOptions, "grid-cols-3": gridCols === 3, "grid-cols-4": gridCols === 2 },
classes.loginItemContainer
)}>
{socialLogins.map((item, index) => (
<div
key={item.icon}
className={cn("group", {
"col-span-3": !isExistingLogin && (item?.block || index === 0),
"col-span-2": gridCols === 2,
"col-span-4": gridCols === 2 && (item?.block || index === 0),
})}
>
<Button
type="button"
block
className={"rounded-full"}
aria-label={`${item.icon} Login Button`}
variant="secondary"
onClick={() => onSocialLoginClick && onSocialLoginClick(item, index)}
>
<div>
<Image isHidden={isHiddenIcon(index)} icon={getIcon(item.icon || "")} />
</div>
{item?.description && !isExistingLogin && (
<div className="flex flex-col items-center ml-2">
{item?.description}
</div>
)}
</Button>
</div>
))}
</div>
)}
{socialLoginMessage && (
<p className={cn("text-xs font-medium text-app-gray-500 dark:text-app-gray-200 mt-4", classes.socialLoginMsg)}>
{socialLoginMessage}
</p>
)}
{socialLogins.length > 0 && socialLogins.length > 4 && (
<div className={cn("flex items-center justify-end my-4", { "my-2": divider }, classes.expandBtnContainer)}>
<Button
variant="text"
pill={pill}
{...inputExpandBtnProps}
className={cn("text-sm font-medium", classes.expandBtn)}
onClick={toggleViewMoreOptions}
>
{viewMoreOptions ? collapseLabel : expandLabel}
</Button>
</div>
)}
{divider && (
<div className="flex items-center mb-2 gap-2">
<div className="h-[1px] w-full bg-app-gray-200 dark:bg-app-gray-600"></div>
<span className="text-app-gray-600 dark:text-app-gray-200 text-sm font-normal">or</span>
<div className="h-[1px] w-full bg-app-gray-200 dark:bg-app-gray-600"></div>
</div>
)}
{children}
{showInput && (
<>
<TextField {...inputProps} pill={pill} />
<Button
className="my-4"
{...inputBtnProps}
variant={!isExistingLogin && primaryBtn === "input" ? "primary" : "tertiary"}
block
onClick={onInputBtnClick}
>
{inputBtnLabel}
</Button>
</>
)}
</div>
);
};

export { LoginForm };
Loading

0 comments on commit 8e079da

Please sign in to comment.