I fixed some issues with how we were setting up styles. should be consistent now
This commit is contained in:
@@ -1,3 +1,3 @@
|
|||||||
export { SignInWithApple } from './sign-in-with-apple';
|
export { SignInWithApple, type SignInWithAppleProps } from './sign-in-with-apple';
|
||||||
export { SignInWithMicrosoft } from './sign-in-with-microsoft';
|
export { SignInWithMicrosoft, type SignInWithMicrosoftProps } from './sign-in-with-microsoft';
|
||||||
export { SignInLinkButton } from './sign-in-link';
|
export { SignInLinkButton } from './sign-in-link';
|
||||||
|
@@ -1,12 +1,11 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { Button, type buttonVariants } from '@/components/ui';
|
import { Button } from '@/components/ui';
|
||||||
import { type ComponentProps } from 'react';
|
import { type ComponentProps } from 'react';
|
||||||
import { type VariantProps } from 'class-variance-authority';
|
|
||||||
|
|
||||||
type SignInProps = ComponentProps<'button'> & VariantProps<typeof buttonVariants>;
|
type SignInLinkButtonProps = Omit<ComponentProps<typeof Button>, 'asChild'>;
|
||||||
|
|
||||||
export const SignInLinkButton = (props: SignInProps) => {
|
export const SignInLinkButton = (props: SignInLinkButtonProps) => {
|
||||||
return (
|
return (
|
||||||
<Button asChild {...props}>
|
<Button asChild {...props}>
|
||||||
<Link href='/sign-in'>Sign In</Link>
|
<Link href='/sign-in'>Sign In</Link>
|
||||||
|
@@ -1,36 +1,36 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import { signInWithApple } from '@/lib/queries';
|
import { signInWithApple } from '@/lib/queries';
|
||||||
import { StatusMessage, SubmitButton } from '@/components/default/forms';
|
import {
|
||||||
|
StatusMessage,
|
||||||
|
SubmitButton,
|
||||||
|
type SubmitButtonProps,
|
||||||
|
} from '@/components/default/forms';
|
||||||
import { useAuth } from '@/lib/hooks/context';
|
import { useAuth } from '@/lib/hooks/context';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useSupabaseClient } from '@/utils/supabase';
|
import { useSupabaseClient } from '@/utils/supabase';
|
||||||
import { FaApple } from 'react-icons/fa';
|
import { FaApple } from 'react-icons/fa';
|
||||||
import { type buttonVariants } from '@/components/ui';
|
|
||||||
import { type ComponentProps } from 'react';
|
import { type ComponentProps } from 'react';
|
||||||
import { type VariantProps } from 'class-variance-authority';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
type SignInWithAppleProps = {
|
export type SignInWithAppleProps = {
|
||||||
buttonProps?: ComponentProps<'button'> & VariantProps<typeof buttonVariants>;
|
submitButtonProps?: SubmitButtonProps;
|
||||||
formProps?: ComponentProps<'form'>;
|
formClassName?: ComponentProps<'form'>['className'];
|
||||||
textProps?: ComponentProps<'p'>;
|
formProps?: Omit<ComponentProps<'form'>, 'className'>;
|
||||||
iconProps?: ComponentProps<'svg'>;
|
textClassName?: ComponentProps<'p'>['className'];
|
||||||
|
textProps?: Omit<ComponentProps<'p'>, 'className'>;
|
||||||
|
iconClassName?: ComponentProps<'svg'>['className'];
|
||||||
|
iconProps?: Omit<ComponentProps<'svg'>, 'className'>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SignInWithApple = ({
|
export const SignInWithApple = ({
|
||||||
buttonProps = {
|
submitButtonProps,
|
||||||
className: 'w-full cursor-pointer',
|
formClassName,
|
||||||
type: 'submit',
|
formProps,
|
||||||
},
|
textClassName,
|
||||||
formProps = {
|
textProps,
|
||||||
className: 'my-4',
|
iconClassName,
|
||||||
},
|
iconProps,
|
||||||
textProps = {
|
|
||||||
className: 'text-[1.0rem]',
|
|
||||||
},
|
|
||||||
iconProps = {
|
|
||||||
className: 'size-5',
|
|
||||||
}
|
|
||||||
} : SignInWithAppleProps) => {
|
} : SignInWithAppleProps) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { loading, refreshUser } = useAuth();
|
const { loading, refreshUser } = useAuth();
|
||||||
@@ -57,15 +57,22 @@ export const SignInWithApple = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSignInWithApple} {...formProps}>
|
<form
|
||||||
|
onSubmit={handleSignInWithApple}
|
||||||
|
className={cn('my-4', formClassName)}
|
||||||
|
{...formProps}
|
||||||
|
>
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
disabled={isLoading || loading}
|
disabled={isLoading || loading}
|
||||||
pendingText='Signing in...'
|
pendingText='Signing in...'
|
||||||
{...buttonProps}
|
className={cn('w-full', submitButtonProps?.className)}
|
||||||
|
{...submitButtonProps}
|
||||||
>
|
>
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
<FaApple {...iconProps} />
|
<FaApple className={cn('size-5', iconClassName)} {...iconProps} />
|
||||||
<p className={textProps.className} {...textProps}>Sign In with Apple</p>
|
<p className={cn('text-[1.0rem]', textClassName)} {...textProps}>
|
||||||
|
Sign In with Apple
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</SubmitButton>
|
</SubmitButton>
|
||||||
{statusMessage && <StatusMessage message={{ error: statusMessage }} />}
|
{statusMessage && <StatusMessage message={{ error: statusMessage }} />}
|
||||||
|
@@ -1,36 +1,36 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import { signInWithMicrosoft } from '@/lib/queries';
|
import { signInWithMicrosoft } from '@/lib/queries';
|
||||||
import { StatusMessage, SubmitButton } from '@/components/default/forms';
|
import {
|
||||||
|
StatusMessage,
|
||||||
|
SubmitButton,
|
||||||
|
type SubmitButtonProps,
|
||||||
|
} from '@/components/default/forms';
|
||||||
import { useAuth } from '@/lib/hooks/context';
|
import { useAuth } from '@/lib/hooks/context';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useSupabaseClient } from '@/utils/supabase';
|
import { useSupabaseClient } from '@/utils/supabase';
|
||||||
import { FaMicrosoft } from 'react-icons/fa';
|
import { FaMicrosoft } from 'react-icons/fa';
|
||||||
import { type buttonVariants } from '@/components/ui';
|
|
||||||
import { type ComponentProps } from 'react';
|
import { type ComponentProps } from 'react';
|
||||||
import { type VariantProps } from 'class-variance-authority';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
type SignInWithMicrosoftProps = {
|
export type SignInWithMicrosoftProps = {
|
||||||
buttonProps?: ComponentProps<'button'> & VariantProps<typeof buttonVariants>;
|
submitButtonProps?: SubmitButtonProps;
|
||||||
formProps?: ComponentProps<'form'>;
|
formClassName?: ComponentProps<'form'>['className'];
|
||||||
textProps?: ComponentProps<'p'>;
|
formProps?: Omit<ComponentProps<'form'>, 'className'>;
|
||||||
iconProps?: ComponentProps<'svg'>;
|
textClassName?: ComponentProps<'p'>['className'];
|
||||||
|
textProps?: Omit<ComponentProps<'p'>, 'className'>;
|
||||||
|
iconClassName?: ComponentProps<'svg'>['className'];
|
||||||
|
iconProps?: Omit<ComponentProps<'svg'>, 'className'>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SignInWithMicrosoft = ({
|
export const SignInWithMicrosoft = ({
|
||||||
buttonProps = {
|
submitButtonProps,
|
||||||
className: 'w-full cursor-pointer',
|
formClassName,
|
||||||
type: 'submit',
|
formProps,
|
||||||
},
|
textClassName,
|
||||||
formProps = {
|
textProps,
|
||||||
className: 'my-4',
|
iconClassName,
|
||||||
},
|
iconProps,
|
||||||
textProps = {
|
|
||||||
className: 'text-[1.0rem]',
|
|
||||||
},
|
|
||||||
iconProps = {
|
|
||||||
className: 'size-5',
|
|
||||||
}
|
|
||||||
} : SignInWithMicrosoftProps) => {
|
} : SignInWithMicrosoftProps) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { loading, refreshUser } = useAuth();
|
const { loading, refreshUser } = useAuth();
|
||||||
@@ -57,16 +57,22 @@ export const SignInWithMicrosoft = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSignInWithMicrosoft} {...formProps}>
|
<form
|
||||||
|
onSubmit={handleSignInWithMicrosoft}
|
||||||
|
className={cn('my-4', formClassName)}
|
||||||
|
{...formProps}
|
||||||
|
>
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
disabled={isLoading || loading}
|
disabled={isLoading || loading}
|
||||||
pendingText='Signing in...'
|
pendingText='Signing in...'
|
||||||
pendingTextClassName={textProps.className}
|
className={cn('w-full', submitButtonProps?.className)}
|
||||||
{...buttonProps}
|
{...submitButtonProps}
|
||||||
>
|
>
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
<FaMicrosoft {...iconProps} />
|
<FaMicrosoft className={cn('size-5', iconClassName)} {...iconProps} />
|
||||||
<p className={textProps.className} {...textProps}>Sign In with Microsoft</p>
|
<p className={cn('text-[1.0rem]', textClassName)} {...textProps}>
|
||||||
|
Sign In with Microsoft
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</SubmitButton>
|
</SubmitButton>
|
||||||
{statusMessage && <StatusMessage message={{ error: statusMessage }} />}
|
{statusMessage && <StatusMessage message={{ error: statusMessage }} />}
|
||||||
|
@@ -1,12 +1,11 @@
|
|||||||
'use server';
|
'use server';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { Button, type buttonVariants } from '@/components/ui';
|
import { Button } from '@/components/ui';
|
||||||
import { type ComponentProps } from 'react';
|
import { type ComponentProps } from 'react';
|
||||||
import { type VariantProps } from 'class-variance-authority';
|
|
||||||
|
|
||||||
type SignInProps = ComponentProps<'button'> & VariantProps<typeof buttonVariants>;
|
type SignInLinkButtonProps = Omit<ComponentProps<typeof Button>, 'asChild'>;
|
||||||
|
|
||||||
export const SignInLinkButton = async (props: SignInProps) => {
|
export const SignInLinkButton = async (props: SignInLinkButtonProps) => {
|
||||||
return (
|
return (
|
||||||
<Button asChild {...props}>
|
<Button asChild {...props}>
|
||||||
<Link href='/sign-in'>Sign In</Link>
|
<Link href='/sign-in'>Sign In</Link>
|
||||||
|
@@ -1,47 +1,32 @@
|
|||||||
'use server';
|
'use server';
|
||||||
import { signInWithApple } from '@/lib/queries';
|
import { signInWithApple } from '@/lib/queries';
|
||||||
import { SubmitButton } from '@/components/default/forms';
|
import {
|
||||||
import Image from 'next/image';
|
SubmitButton,
|
||||||
import { type buttonVariants } from '@/components/ui';
|
type SubmitButtonProps,
|
||||||
import { type ComponentProps } from 'react';
|
} from '@/components/default/forms';
|
||||||
import { type VariantProps } from 'class-variance-authority';
|
|
||||||
import { SupabaseServer } from '@/utils/supabase';
|
import { SupabaseServer } from '@/utils/supabase';
|
||||||
|
import { FaApple } from 'react-icons/fa';
|
||||||
|
import { type ComponentProps } from 'react';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
type ButtonProps = ComponentProps<'button'> & VariantProps<typeof buttonVariants>;
|
export type SignInWithAppleProps = {
|
||||||
type ImageProps = {
|
submitButtonProps?: SubmitButtonProps;
|
||||||
src: string;
|
formClassName?: ComponentProps<'form'>['className'];
|
||||||
alt: string;
|
formProps?: Omit<ComponentProps<'form'>, 'className'>;
|
||||||
className?: string;
|
textClassName?: ComponentProps<'p'>['className'];
|
||||||
width?: number;
|
textProps?: Omit<ComponentProps<'p'>, 'className'>;
|
||||||
height?: number;
|
iconClassName?: ComponentProps<'svg'>['className'];
|
||||||
}
|
iconProps?: Omit<ComponentProps<'svg'>, 'className'>;
|
||||||
type FormProps = ComponentProps<'form'>;
|
|
||||||
type TextProps = ComponentProps<'p'>;
|
|
||||||
type SignInWithAppleProps = {
|
|
||||||
buttonProps?: ButtonProps;
|
|
||||||
imageProps?: ImageProps;
|
|
||||||
formProps?: FormProps;
|
|
||||||
textProps?: TextProps;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SignInWithApple = async ({
|
export const SignInWithApple = async ({
|
||||||
buttonProps = {
|
submitButtonProps,
|
||||||
className: 'w-full cursor-pointer',
|
formClassName,
|
||||||
type: 'submit',
|
formProps,
|
||||||
},
|
textClassName,
|
||||||
imageProps = {
|
textProps,
|
||||||
src: '/icons/auth/apple.svg',
|
iconClassName,
|
||||||
alt: 'Apple',
|
iconProps,
|
||||||
className: 'invert-75 dark:invert-25',
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
},
|
|
||||||
formProps = {
|
|
||||||
className: 'my-4',
|
|
||||||
},
|
|
||||||
textProps = {
|
|
||||||
className: 'text-[1.0rem]',
|
|
||||||
},
|
|
||||||
} : SignInWithAppleProps) => {
|
} : SignInWithAppleProps) => {
|
||||||
const supabase = await SupabaseServer();
|
const supabase = await SupabaseServer();
|
||||||
|
|
||||||
@@ -49,28 +34,30 @@ export const SignInWithApple = async ({
|
|||||||
try {
|
try {
|
||||||
if (!supabase) throw new Error('Supabase client not found');
|
if (!supabase) throw new Error('Supabase client not found');
|
||||||
const result = await signInWithApple(supabase);
|
const result = await signInWithApple(supabase);
|
||||||
if (result.error) throw new Error(`Error Signing in with Apple: ${result.error.message}`);
|
if (result.error)
|
||||||
if (result.data.url) window.location.href = result.data.url;
|
throw new Error(`Error signing in with Microsoft: ${result.error.message}`);
|
||||||
|
else if (result.data.url) window.location.href = result.data.url;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form action={handleSignInWithApple} {...formProps}>
|
<form
|
||||||
|
action={handleSignInWithApple}
|
||||||
|
className={cn('my-4', formClassName)}
|
||||||
|
{...formProps}
|
||||||
|
>
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
pendingText='Signing in...'
|
pendingText='Signing in...'
|
||||||
{...buttonProps}
|
className={cn('w-full', submitButtonProps?.className)}
|
||||||
|
{...submitButtonProps}
|
||||||
>
|
>
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
<Image
|
<FaApple className={cn('size-5', iconClassName)} {...iconProps} />
|
||||||
src={imageProps.src}
|
<p className={cn('text-[1.0rem]', textClassName)} {...textProps}>
|
||||||
alt={imageProps.alt}
|
Sign In with Apple
|
||||||
className={imageProps.className}
|
</p>
|
||||||
width={imageProps.width}
|
|
||||||
height={imageProps.height}
|
|
||||||
/>
|
|
||||||
<p className={textProps.className} {...textProps}>Sign In with Apple</p>
|
|
||||||
</div>
|
</div>
|
||||||
</SubmitButton>
|
</SubmitButton>
|
||||||
</form>
|
</form>
|
||||||
|
@@ -1,47 +1,32 @@
|
|||||||
'use server';
|
'use server';
|
||||||
import { signInWithMicrosoft } from '@/lib/queries';
|
import { signInWithMicrosoft } from '@/lib/queries';
|
||||||
import { SubmitButton } from '@/components/default/forms';
|
import {
|
||||||
import Image from 'next/image';
|
SubmitButton,
|
||||||
import { type buttonVariants } from '@/components/ui';
|
type SubmitButtonProps,
|
||||||
import { type ComponentProps } from 'react';
|
} from '@/components/default/forms';
|
||||||
import { type VariantProps } from 'class-variance-authority';
|
|
||||||
import { SupabaseServer } from '@/utils/supabase';
|
import { SupabaseServer } from '@/utils/supabase';
|
||||||
|
import { FaMicrosoft } from 'react-icons/fa';
|
||||||
|
import { type ComponentProps } from 'react';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
type ButtonProps = ComponentProps<'button'> & VariantProps<typeof buttonVariants>;
|
export type SignInWithMicrosoftProps = {
|
||||||
type ImageProps = {
|
submitButtonProps?: SubmitButtonProps;
|
||||||
src: string;
|
formClassName?: ComponentProps<'form'>['className'];
|
||||||
alt: string;
|
formProps?: Omit<ComponentProps<'form'>, 'className'>;
|
||||||
className?: string;
|
textClassName?: ComponentProps<'p'>['className'];
|
||||||
width?: number;
|
textProps?: Omit<ComponentProps<'p'>, 'className'>;
|
||||||
height?: number;
|
iconClassName?: ComponentProps<'svg'>['className'];
|
||||||
}
|
iconProps?: Omit<ComponentProps<'svg'>, 'className'>;
|
||||||
type FormProps = ComponentProps<'form'>;
|
|
||||||
type TextProps = ComponentProps<'p'>;
|
|
||||||
type SignInWithMicrosoftProps = {
|
|
||||||
buttonProps?: ButtonProps;
|
|
||||||
imageProps?: ImageProps;
|
|
||||||
formProps?: FormProps;
|
|
||||||
textProps?: TextProps;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SignInWithMicrosoft = async ({
|
export const SignInWithMicrosoft = async ({
|
||||||
buttonProps = {
|
submitButtonProps,
|
||||||
className: 'w-full cursor-pointer',
|
formClassName,
|
||||||
type: 'submit',
|
formProps,
|
||||||
},
|
textClassName,
|
||||||
imageProps = {
|
textProps,
|
||||||
src: '/icons/auth/microsoft.svg',
|
iconClassName,
|
||||||
alt: 'Microsoft',
|
iconProps,
|
||||||
className: 'invert-75 dark:invert-25',
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
},
|
|
||||||
formProps = {
|
|
||||||
className: 'my-4',
|
|
||||||
},
|
|
||||||
textProps = {
|
|
||||||
className: 'text-[1.0rem]',
|
|
||||||
},
|
|
||||||
} : SignInWithMicrosoftProps) => {
|
} : SignInWithMicrosoftProps) => {
|
||||||
const supabase = await SupabaseServer();
|
const supabase = await SupabaseServer();
|
||||||
|
|
||||||
@@ -49,28 +34,30 @@ export const SignInWithMicrosoft = async ({
|
|||||||
try {
|
try {
|
||||||
if (!supabase) throw new Error('Supabase client not found');
|
if (!supabase) throw new Error('Supabase client not found');
|
||||||
const result = await signInWithMicrosoft(supabase);
|
const result = await signInWithMicrosoft(supabase);
|
||||||
if (result.error) throw new Error(`Error Signing in with Microsoft: ${result.error.message}`);
|
if (result.error)
|
||||||
if (result.data.url) window.location.href = result.data.url;
|
throw new Error(`Error signing in with Microsoft: ${result.error.message}`);
|
||||||
|
else if (result.data.url) window.location.href = result.data.url;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form action={handleSignInWithMicrosoft} {...formProps}>
|
<form
|
||||||
|
action={handleSignInWithMicrosoft}
|
||||||
|
className={cn('my-4', formClassName)}
|
||||||
|
{...formProps}
|
||||||
|
>
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
pendingText='Signing in...'
|
pendingText='Signing in...'
|
||||||
{...buttonProps}
|
className={cn('w-full', submitButtonProps?.className)}
|
||||||
|
{...submitButtonProps}
|
||||||
>
|
>
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
<Image
|
<FaMicrosoft className={cn('size-5', iconClassName)} {...iconProps} />
|
||||||
src={imageProps.src}
|
<p className={cn('text-[1.0rem]', textClassName)} {...textProps}>
|
||||||
alt={imageProps.alt}
|
Sign In with Microsoft
|
||||||
className={imageProps.className}
|
</p>
|
||||||
width={imageProps.width}
|
|
||||||
height={imageProps.height}
|
|
||||||
/>
|
|
||||||
<p className={textProps.className} {...textProps}>Sign In with Microsoft</p>
|
|
||||||
</div>
|
</div>
|
||||||
</SubmitButton>
|
</SubmitButton>
|
||||||
</form>
|
</form>
|
||||||
|
@@ -13,9 +13,6 @@ import { StatusMessage, SubmitButton } from '@/components/default/forms';
|
|||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
CardDescription,
|
|
||||||
CardHeader,
|
|
||||||
CardTitle,
|
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
FormField,
|
FormField,
|
||||||
@@ -31,8 +28,12 @@ import {
|
|||||||
} from '@/components/ui';
|
} from '@/components/ui';
|
||||||
import {
|
import {
|
||||||
SignInWithApple,
|
SignInWithApple,
|
||||||
SignInWithMicrosoft
|
type SignInWithAppleProps,
|
||||||
|
SignInWithMicrosoft,
|
||||||
|
type SignInWithMicrosoftProps,
|
||||||
} from '@/components/default/auth/buttons/client';
|
} from '@/components/default/auth/buttons/client';
|
||||||
|
import { type ComponentProps } from 'react';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
const signInFormSchema = z.object({
|
const signInFormSchema = z.object({
|
||||||
email: z.string().email({
|
email: z.string().email({
|
||||||
@@ -63,7 +64,47 @@ const signUpFormSchema = z
|
|||||||
path: ['confirmPassword'],
|
path: ['confirmPassword'],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const SignInCard = () => {
|
type SignInCardProps = {
|
||||||
|
containerClassName?: ComponentProps<typeof Card>['className'];
|
||||||
|
containerProps?: Omit<ComponentProps<typeof Card>, 'className'>;
|
||||||
|
tabsClassName?: ComponentProps<typeof Tabs>['className'];
|
||||||
|
tabsProps?: Omit<ComponentProps<typeof Tabs>, 'className'>;
|
||||||
|
tabsListClassName?: ComponentProps<typeof TabsList>['className'];
|
||||||
|
tabsListProps?: Omit<ComponentProps<typeof TabsList>, 'className'>;
|
||||||
|
tabsTriggerClassName?: ComponentProps<typeof TabsTrigger>['className'];
|
||||||
|
tabsTriggerProps?: Omit<ComponentProps<typeof TabsTrigger>, 'className' | 'value'>;
|
||||||
|
cardClassName?: ComponentProps<typeof Card>['className'];
|
||||||
|
cardProps?: Omit<ComponentProps<typeof Card>, 'className'>;
|
||||||
|
formClassName?: ComponentProps<'form'>['className'];
|
||||||
|
formProps?: Omit<ComponentProps<'form'>, 'className' | 'onSubmit'>;
|
||||||
|
formLabelClassName?: ComponentProps<typeof FormLabel>['className'];
|
||||||
|
formLabelProps?: Omit<ComponentProps<typeof FormLabel>, 'className'>;
|
||||||
|
submitButtonProps?: Omit<ComponentProps<typeof SubmitButton>,
|
||||||
|
'pendingText' | 'disabled'>;
|
||||||
|
signInWithAppleProps?: SignInWithAppleProps;
|
||||||
|
signInWithMicrosoftProps?: SignInWithMicrosoftProps;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SignInCard = ({
|
||||||
|
containerClassName,
|
||||||
|
containerProps,
|
||||||
|
tabsClassName,
|
||||||
|
tabsProps = { defaultValue: 'sign-in' },
|
||||||
|
tabsListClassName,
|
||||||
|
tabsListProps,
|
||||||
|
tabsTriggerClassName,
|
||||||
|
tabsTriggerProps,
|
||||||
|
cardClassName,
|
||||||
|
cardProps,
|
||||||
|
formClassName,
|
||||||
|
formProps,
|
||||||
|
formLabelClassName,
|
||||||
|
formLabelProps,
|
||||||
|
submitButtonProps,
|
||||||
|
signInWithAppleProps,
|
||||||
|
signInWithMicrosoftProps,
|
||||||
|
}: SignInCardProps) => {
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { isAuthenticated, loading, refreshUser } = useAuth();
|
const { isAuthenticated, loading, refreshUser } = useAuth();
|
||||||
const [statusMessage, setStatusMessage] = useState('');
|
const [statusMessage, setStatusMessage] = useState('');
|
||||||
@@ -71,10 +112,7 @@ export const SignInCard = () => {
|
|||||||
|
|
||||||
const signInForm = useForm<z.infer<typeof signInFormSchema>>({
|
const signInForm = useForm<z.infer<typeof signInFormSchema>>({
|
||||||
resolver: zodResolver(signInFormSchema),
|
resolver: zodResolver(signInFormSchema),
|
||||||
defaultValues: {
|
defaultValues: { email: '', password: '' },
|
||||||
email: '',
|
|
||||||
password: '',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const signUpForm = useForm<z.infer<typeof signUpFormSchema>>({
|
const signUpForm = useForm<z.infer<typeof signUpFormSchema>>({
|
||||||
@@ -127,30 +165,64 @@ export const SignInCard = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs defaultValue='sign-in'>
|
<Card
|
||||||
<TabsList>
|
className={cn('p-4 bg-card/25 min-h-[720px]', containerClassName)}
|
||||||
<TabsTrigger value='sign-in'>Sign In</TabsTrigger>
|
{...containerProps}
|
||||||
<TabsTrigger value='sign-up'>Sign Up</TabsTrigger>
|
>
|
||||||
|
<Tabs
|
||||||
|
className={cn('items-center', tabsClassName)}
|
||||||
|
{...tabsProps}
|
||||||
|
>
|
||||||
|
<TabsList
|
||||||
|
className={cn('py-6', tabsListClassName)}
|
||||||
|
{...tabsListProps}
|
||||||
|
>
|
||||||
|
<TabsTrigger
|
||||||
|
value='sign-in'
|
||||||
|
className={cn(
|
||||||
|
'p-6 text-2xl font-bold cursor-pointer',
|
||||||
|
tabsTriggerClassName
|
||||||
|
)}
|
||||||
|
{...tabsTriggerProps}
|
||||||
|
>
|
||||||
|
Sign In
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger
|
||||||
|
value='sign-up'
|
||||||
|
className={cn(
|
||||||
|
'p-6 text-2xl font-bold cursor-pointer',
|
||||||
|
tabsTriggerClassName,
|
||||||
|
)}
|
||||||
|
{...tabsTriggerProps}
|
||||||
|
>
|
||||||
|
Sign Up
|
||||||
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<TabsContent value='sign-in'>
|
<TabsContent value='sign-in'>
|
||||||
<Card className='min-w-xs md:min-w-sm max-w-lg'>
|
<Card
|
||||||
<CardHeader>
|
className={cn(
|
||||||
<CardTitle className=''>
|
'min-w-xs sm:min-w-sm bg-card/50',
|
||||||
Sign In
|
cardClassName,
|
||||||
</CardTitle>
|
)}
|
||||||
</CardHeader>
|
{...cardProps}
|
||||||
|
>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Form {...signInForm}>
|
<Form {...signInForm}>
|
||||||
<form
|
<form
|
||||||
onSubmit={signInForm.handleSubmit(handleSignIn)}
|
onSubmit={signInForm.handleSubmit(handleSignIn)}
|
||||||
className=''
|
className={cn('flex flex-col space-y-6', formClassName)}
|
||||||
|
{...formProps}
|
||||||
>
|
>
|
||||||
<FormField
|
<FormField
|
||||||
control={signInForm.control}
|
control={signInForm.control}
|
||||||
name='email'
|
name='email'
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel className=''>Email</FormLabel>
|
<FormLabel
|
||||||
|
className={cn('text-xl', formLabelClassName)}
|
||||||
|
{...formLabelProps}>
|
||||||
|
Email
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
type='email'
|
type='email'
|
||||||
@@ -168,7 +240,12 @@ export const SignInCard = () => {
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<div className='flex justify-between'>
|
<div className='flex justify-between'>
|
||||||
<FormLabel className=''>Password</FormLabel>
|
<FormLabel
|
||||||
|
className={cn('text-xl', formLabelClassName)}
|
||||||
|
{...formLabelProps}
|
||||||
|
>
|
||||||
|
Password
|
||||||
|
</FormLabel>
|
||||||
<Link
|
<Link
|
||||||
href='/forgot-password'
|
href='/forgot-password'
|
||||||
>
|
>
|
||||||
@@ -198,40 +275,82 @@ export const SignInCard = () => {
|
|||||||
<SubmitButton
|
<SubmitButton
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
pendingText='Signing In...'
|
pendingText='Signing In...'
|
||||||
|
className={cn(
|
||||||
|
'text-lg font-semibold w-2/3 mx-auto',
|
||||||
|
submitButtonProps?.className
|
||||||
|
)}
|
||||||
|
{...submitButtonProps}
|
||||||
>
|
>
|
||||||
Sign In
|
Sign In
|
||||||
</SubmitButton>
|
</SubmitButton>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
<div className='flex items-center w-full gap-4'>
|
<div className='flex items-center w-full gap-4 mt-4'>
|
||||||
<Separator className='flex-1 bg-accent py-0.5' />
|
<Separator className='flex-1 bg-muted-foreground/50 py-0.5' />
|
||||||
<span className='text-sm text-muted-foreground'>or</span>
|
<span className='text-muted-foreground'>or</span>
|
||||||
<Separator className='flex-1 bg-accent py-0.5' />
|
<Separator className='flex-1 bg-muted-foreground/50 py-0.5' />
|
||||||
</div>
|
</div>
|
||||||
<SignInWithMicrosoft />
|
<SignInWithMicrosoft
|
||||||
<SignInWithApple />
|
submitButtonProps = {{
|
||||||
|
className: cn(
|
||||||
|
'flex w-5/6 m-auto',
|
||||||
|
signInWithMicrosoftProps?.submitButtonProps?.className),
|
||||||
|
}}
|
||||||
|
textClassName={cn(
|
||||||
|
'text-lg',
|
||||||
|
signInWithMicrosoftProps?.textClassName,
|
||||||
|
)}
|
||||||
|
iconClassName={cn(
|
||||||
|
'size-6',
|
||||||
|
signInWithMicrosoftProps?.iconClassName,
|
||||||
|
)}
|
||||||
|
{...signInWithMicrosoftProps}
|
||||||
|
/>
|
||||||
|
<SignInWithApple
|
||||||
|
submitButtonProps = {{
|
||||||
|
className: cn(
|
||||||
|
'flex w-5/6 m-auto',
|
||||||
|
signInWithAppleProps?.submitButtonProps?.className),
|
||||||
|
}}
|
||||||
|
textClassName={cn(
|
||||||
|
'text-lg',
|
||||||
|
signInWithAppleProps?.textClassName,
|
||||||
|
)}
|
||||||
|
iconClassName={cn(
|
||||||
|
'size-6',
|
||||||
|
signInWithAppleProps?.iconClassName,
|
||||||
|
)}
|
||||||
|
{...signInWithAppleProps}
|
||||||
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value='sign-up'>
|
<TabsContent value='sign-up'>
|
||||||
<Card className='min-w-xs md:min-w-sm max-w-lg'>
|
<Card
|
||||||
<CardHeader>
|
className={cn(
|
||||||
<CardTitle className=''>
|
'min-w-xs sm:min-w-sm bg-card/50',
|
||||||
Sign Up
|
cardClassName,
|
||||||
</CardTitle>
|
)}
|
||||||
</CardHeader>
|
{...cardProps}
|
||||||
|
>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Form {...signUpForm}>
|
<Form {...signUpForm}>
|
||||||
<form
|
<form
|
||||||
|
className={cn('flex flex-col space-y-6', formClassName)}
|
||||||
onSubmit={signUpForm.handleSubmit(handleSignUp)}
|
onSubmit={signUpForm.handleSubmit(handleSignUp)}
|
||||||
className=''
|
{...formProps}
|
||||||
>
|
>
|
||||||
<FormField
|
<FormField
|
||||||
control={signUpForm.control}
|
control={signUpForm.control}
|
||||||
name='name'
|
name='name'
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel className=''>Name</FormLabel>
|
<FormLabel
|
||||||
|
className={cn('text-xl', formLabelClassName)}
|
||||||
|
{...formLabelProps}
|
||||||
|
>
|
||||||
|
Name
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
type='text'
|
type='text'
|
||||||
@@ -247,7 +366,12 @@ export const SignInCard = () => {
|
|||||||
name='email'
|
name='email'
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel className=''>Email</FormLabel>
|
<FormLabel
|
||||||
|
className={cn('text-xl', formLabelClassName)}
|
||||||
|
{...formLabelProps}
|
||||||
|
>
|
||||||
|
Email
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
type='email'
|
type='email'
|
||||||
@@ -263,7 +387,12 @@ export const SignInCard = () => {
|
|||||||
name='password'
|
name='password'
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel className=''>Password</FormLabel>
|
<FormLabel
|
||||||
|
className={cn('text-xl', formLabelClassName)}
|
||||||
|
{...formLabelProps}
|
||||||
|
>
|
||||||
|
Password
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
type='password'
|
type='password'
|
||||||
@@ -279,7 +408,12 @@ export const SignInCard = () => {
|
|||||||
name='confirmPassword'
|
name='confirmPassword'
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel className=''>Confirm Passsword</FormLabel>
|
<FormLabel
|
||||||
|
className={cn('text-xl', formLabelClassName)}
|
||||||
|
{...formLabelProps}
|
||||||
|
>
|
||||||
|
Confirm Passsword
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
type='password'
|
type='password'
|
||||||
@@ -302,23 +436,58 @@ export const SignInCard = () => {
|
|||||||
<SubmitButton
|
<SubmitButton
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
pendingText='Signing Up...'
|
pendingText='Signing Up...'
|
||||||
|
className={cn(
|
||||||
|
'text-lg font-semibold w-2/3 mx-auto',
|
||||||
|
submitButtonProps?.className
|
||||||
|
)}
|
||||||
|
{...submitButtonProps}
|
||||||
>
|
>
|
||||||
Sign Up
|
Sign Up
|
||||||
</SubmitButton>
|
</SubmitButton>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
<div className='flex items-center w-full gap-4'>
|
<div className='flex items-center w-full gap-4 mt-4'>
|
||||||
<Separator className='flex-1 bg-accent py-0.5' />
|
<Separator className='flex-1 bg-accent py-0.5' />
|
||||||
<span className='text-sm text-muted-foreground'>or</span>
|
<span className='text-sm text-muted-foreground'>or</span>
|
||||||
<Separator className='flex-1 bg-accent py-0.5' />
|
<Separator className='flex-1 bg-accent py-0.5' />
|
||||||
</div>
|
</div>
|
||||||
<SignInWithMicrosoft />
|
<SignInWithMicrosoft
|
||||||
<SignInWithApple />
|
submitButtonProps = {{
|
||||||
|
className: cn(
|
||||||
|
'flex w-5/6 m-auto',
|
||||||
|
signInWithMicrosoftProps?.submitButtonProps?.className),
|
||||||
|
}}
|
||||||
|
textClassName={cn(
|
||||||
|
'text-lg',
|
||||||
|
signInWithMicrosoftProps?.textClassName,
|
||||||
|
)}
|
||||||
|
iconClassName={cn(
|
||||||
|
'size-6',
|
||||||
|
signInWithMicrosoftProps?.iconClassName,
|
||||||
|
)}
|
||||||
|
{...signInWithMicrosoftProps}
|
||||||
|
/>
|
||||||
|
<SignInWithApple
|
||||||
|
submitButtonProps = {{
|
||||||
|
className: cn(
|
||||||
|
'flex w-5/6 m-auto',
|
||||||
|
signInWithAppleProps?.submitButtonProps?.className),
|
||||||
|
}}
|
||||||
|
textClassName={cn(
|
||||||
|
'text-lg',
|
||||||
|
signInWithAppleProps?.textClassName,
|
||||||
|
)}
|
||||||
|
iconClassName={cn(
|
||||||
|
'size-6',
|
||||||
|
signInWithAppleProps?.iconClassName,
|
||||||
|
)}
|
||||||
|
{...signInWithAppleProps}
|
||||||
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
</Card>
|
||||||
);
|
);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,2 +1,2 @@
|
|||||||
export { StatusMessage } from './status-message';
|
export { StatusMessage } from './status-message';
|
||||||
export { SubmitButton } from './submit-button';
|
export { SubmitButton, type SubmitButtonProps } from './submit-button';
|
||||||
|
@@ -1,25 +1,52 @@
|
|||||||
|
import { type ComponentProps } from 'react';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
type Message =
|
type Message =
|
||||||
| { success: string}
|
| { success: string}
|
||||||
| { error: string}
|
| { error: string}
|
||||||
| { message: string}
|
| { message: string}
|
||||||
|
|
||||||
export const StatusMessage = ({message}: {message: Message}) => {
|
type StatusMessageProps = {
|
||||||
|
message: Message;
|
||||||
|
containerClassName?: ComponentProps<'div'>['className'];
|
||||||
|
containerProps?: Omit<ComponentProps<'div'>, 'className'>;
|
||||||
|
textClassName?: ComponentProps<'div'>['className'];
|
||||||
|
textProps?: Omit<ComponentProps<'div'>, 'className'>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const StatusMessage = ({
|
||||||
|
message,
|
||||||
|
containerClassName,
|
||||||
|
containerProps,
|
||||||
|
textClassName,
|
||||||
|
textProps,
|
||||||
|
}: StatusMessageProps) => {
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col gap-2 w-full
|
<div
|
||||||
text-sm bg-accent rounded-md p-2 px-4'
|
className={cn(
|
||||||
|
'flex flex-col gap-2 w-full\
|
||||||
|
text-sm bg-accent rounded-md p-2 px-4',
|
||||||
|
containerClassName,
|
||||||
|
)}
|
||||||
|
{...containerProps}
|
||||||
>
|
>
|
||||||
{'success' in message && (
|
{'success' in message && (
|
||||||
<div className='dark:text-green-500 text-green-700'>
|
<div className={cn(
|
||||||
|
'dark:text-green-500 text-green-700',
|
||||||
|
textClassName
|
||||||
|
)}
|
||||||
|
{...textProps}
|
||||||
|
>
|
||||||
{message.success}
|
{message.success}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{'error' in message && (
|
{'error' in message && (
|
||||||
<div className='text-destructive'>
|
<div className={cn('text-destructive', textClassName)}>
|
||||||
{message.error}
|
{message.error}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{'message' in message && (
|
{'message' in message && (
|
||||||
<div>
|
<div className={textClassName}>
|
||||||
{message.message}
|
{message.message}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@@ -1,36 +1,52 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import { Button, type buttonVariants } from '@/components/ui';
|
import { Button } from '@/components/ui';
|
||||||
import { type ComponentProps } from 'react';
|
import { type ComponentProps } from 'react';
|
||||||
import { type VariantProps } from 'class-variance-authority';
|
|
||||||
import { useFormStatus } from 'react-dom';
|
import { useFormStatus } from 'react-dom';
|
||||||
import { Loader2 } from 'lucide-react';
|
import { Loader2 } from 'lucide-react';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
type SubmitButtonProps = ComponentProps<'button'> &
|
export type SubmitButtonProps = Omit<
|
||||||
VariantProps<typeof buttonVariants> & {
|
ComponentProps<typeof Button>,
|
||||||
|
'type' | 'aria-disabled' | 'className',
|
||||||
|
> & {
|
||||||
|
className?: ComponentProps<typeof Button>['className'];
|
||||||
pendingText?: string;
|
pendingText?: string;
|
||||||
loaderClassName?: ComponentProps<'div'>['className'];
|
|
||||||
pendingTextClassName?: ComponentProps<'p'>['className'];
|
pendingTextClassName?: ComponentProps<'p'>['className'];
|
||||||
|
pendingTextProps?: Omit<ComponentProps<'p'>, 'className'>;
|
||||||
|
loaderClassName?: ComponentProps<typeof Loader2>['className'];
|
||||||
|
loaderProps?: Omit<ComponentProps<typeof Loader2>, 'className'>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SubmitButton = ({
|
export const SubmitButton = ({
|
||||||
children,
|
children,
|
||||||
|
className,
|
||||||
pendingText = 'Submitting...',
|
pendingText = 'Submitting...',
|
||||||
loaderClassName = 'mr-2 h-4 w-4 animate-spin',
|
pendingTextClassName,
|
||||||
pendingTextClassName = 'text-sm font-medium',
|
pendingTextProps,
|
||||||
|
loaderClassName,
|
||||||
|
loaderProps,
|
||||||
...props
|
...props
|
||||||
}: SubmitButtonProps) => {
|
}: SubmitButtonProps) => {
|
||||||
const { pending } = useFormStatus();
|
const { pending } = useFormStatus();
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
className='cursor-pointer'
|
className={cn('cursor-pointer', className)}
|
||||||
type='submit'
|
type='submit'
|
||||||
aria-disabled={pending}
|
aria-disabled={pending}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{pending || props.disabled ? (
|
{pending || props.disabled ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className={loaderClassName} />
|
<Loader2
|
||||||
<p className={pendingTextClassName}>{pendingText}</p>
|
className={cn('mr-2 h-4 w-4 animate-spin', loaderClassName)}
|
||||||
|
{...loaderProps}
|
||||||
|
/>
|
||||||
|
<p
|
||||||
|
className={cn('text-sm font-medium', pendingTextClassName)}
|
||||||
|
{...pendingTextProps}
|
||||||
|
>
|
||||||
|
{pendingText}
|
||||||
|
</p>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
children
|
children
|
||||||
|
@@ -1,40 +1,51 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import * as React from 'react';
|
import {
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
type ComponentProps,
|
||||||
|
} from 'react';
|
||||||
import { ThemeProvider as NextThemesProvider } from 'next-themes';
|
import { ThemeProvider as NextThemesProvider } from 'next-themes';
|
||||||
import { Moon, Sun } from 'lucide-react';
|
import { Moon, Sun } from 'lucide-react';
|
||||||
import { useTheme } from 'next-themes';
|
import { useTheme } from 'next-themes';
|
||||||
import { Button } from '@/components/ui';
|
import { Button } from '@/components/ui';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
const ThemeProvider = ({
|
const ThemeProvider = ({
|
||||||
children,
|
children,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof NextThemesProvider>) => {
|
}: ComponentProps<typeof NextThemesProvider>) => {
|
||||||
const [mounted, setMounted] = React.useState(false);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
const [mounted, setMounted] = useState(false);
|
||||||
setMounted(true);
|
|
||||||
}, []);
|
useEffect(() => { setMounted(true) }, []);
|
||||||
|
|
||||||
if (!mounted) return null;
|
if (!mounted) return null;
|
||||||
|
|
||||||
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
|
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ThemeToggleProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
type ThemeToggleProps = {
|
||||||
size?: number;
|
size?: number;
|
||||||
|
buttonClassName?: ComponentProps<typeof Button>['className'];
|
||||||
|
buttonProps?: Omit<ComponentProps<typeof Button>, 'className' | 'onClick'>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ThemeToggle = ({ size = 1, ...props }: ThemeToggleProps) => {
|
const ThemeToggle = ({
|
||||||
const { setTheme, resolvedTheme } = useTheme();
|
size = 1,
|
||||||
const [mounted, setMounted] = React.useState(false);
|
buttonClassName,
|
||||||
|
buttonProps = {
|
||||||
|
variant: 'outline',
|
||||||
|
size: 'icon',
|
||||||
|
},
|
||||||
|
}: ThemeToggleProps) => {
|
||||||
|
|
||||||
React.useEffect(() => {
|
const { setTheme, resolvedTheme } = useTheme();
|
||||||
setMounted(true);
|
const [mounted, setMounted] = useState(false);
|
||||||
}, []);
|
|
||||||
|
useEffect(() => { setMounted(true) }, []);
|
||||||
|
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
return (
|
return (
|
||||||
<Button variant='outline' size='icon' {...props}>
|
<Button className={buttonClassName} {...buttonProps}>
|
||||||
<span style={{ height: `${size}rem`, width: `${size}rem` }} />
|
<span style={{ height: `${size}rem`, width: `${size}rem` }} />
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
@@ -47,11 +58,9 @@ const ThemeToggle = ({ size = 1, ...props }: ThemeToggleProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
variant='outline'
|
className={cn('cursor-pointer', buttonClassName)}
|
||||||
size='icon'
|
|
||||||
className='cursor-pointer'
|
|
||||||
onClick={toggleTheme}
|
onClick={toggleTheme}
|
||||||
{...props}
|
{...buttonProps}
|
||||||
>
|
>
|
||||||
<Sun
|
<Sun
|
||||||
style={{ height: `${size}rem`, width: `${size}rem` }}
|
style={{ height: `${size}rem`, width: `${size}rem` }}
|
||||||
@@ -61,7 +70,6 @@ const ThemeToggle = ({ size = 1, ...props }: ThemeToggleProps) => {
|
|||||||
style={{ height: `${size}rem`, width: `${size}rem` }}
|
style={{ height: `${size}rem`, width: `${size}rem` }}
|
||||||
className='absolute rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100'
|
className='absolute rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100'
|
||||||
/>
|
/>
|
||||||
<span className='sr-only'>Toggle theme</span>
|
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -2,9 +2,9 @@
|
|||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import React, { createContext, useContext, useState } from 'react';
|
import React, { createContext, useContext, useState } from 'react';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import { Button, type buttonVariants } from '@/components/ui';
|
import { Button } from '@/components/ui';
|
||||||
import { type ComponentProps } from 'react';
|
import { type ComponentProps } from 'react';
|
||||||
import { type VariantProps } from 'class-variance-authority';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
type TVModeContextProps = {
|
type TVModeContextProps = {
|
||||||
tvMode: boolean;
|
tvMode: boolean;
|
||||||
@@ -12,11 +12,9 @@ type TVModeContextProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type TVToggleProps = {
|
type TVToggleProps = {
|
||||||
className?: ComponentProps<'button'>['className'];
|
buttonClassName?: ComponentProps<typeof Button>['className'];
|
||||||
buttonSize?: VariantProps<typeof buttonVariants>['size'];
|
buttonProps?: Omit<ComponentProps<typeof Button>, 'className'>;
|
||||||
buttonVariant?: VariantProps<typeof buttonVariants>['variant'];
|
size: number;
|
||||||
imageWidth?: number;
|
|
||||||
imageHeight?: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const TVModeContext = createContext<TVModeContextProps | undefined>(undefined);
|
const TVModeContext = createContext<TVModeContextProps | undefined>(undefined);
|
||||||
@@ -42,33 +40,33 @@ const useTVMode = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const TVToggle = ({
|
const TVToggle = ({
|
||||||
className = 'my-auto cursor-pointer',
|
buttonClassName,
|
||||||
buttonSize = 'default',
|
buttonProps = {
|
||||||
buttonVariant = 'link',
|
variant: 'outline',
|
||||||
imageWidth = 25,
|
size: 'default',
|
||||||
imageHeight = 25,
|
},
|
||||||
|
size = 25,
|
||||||
}: TVToggleProps) => {
|
}: TVToggleProps) => {
|
||||||
const { tvMode, toggleTVMode } = useTVMode();
|
const { tvMode, toggleTVMode } = useTVMode();
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
onClick={toggleTVMode}
|
onClick={toggleTVMode}
|
||||||
className={className}
|
className={cn('my-auto cursor-pointer', buttonClassName)}
|
||||||
size={buttonSize}
|
{...buttonProps}
|
||||||
variant={buttonVariant}
|
|
||||||
>
|
>
|
||||||
{tvMode ? (
|
{tvMode ? (
|
||||||
<Image
|
<Image
|
||||||
src='/icons/tv/exit.svg'
|
src='/icons/tv/exit.svg'
|
||||||
alt='Exit TV Mode'
|
alt='Exit TV Mode'
|
||||||
width={imageWidth}
|
width={size}
|
||||||
height={imageHeight}
|
height={size}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Image
|
<Image
|
||||||
src='/icons/tv/enter.svg'
|
src='/icons/tv/enter.svg'
|
||||||
alt='Exit TV Mode'
|
alt='Enter TV Mode'
|
||||||
width={imageWidth}
|
width={size}
|
||||||
height={imageHeight}
|
height={size}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
@@ -16,5 +16,5 @@ export const ccn = ({
|
|||||||
on: string;
|
on: string;
|
||||||
off: string;
|
off: string;
|
||||||
}) => {
|
}) => {
|
||||||
return className + ' ' + (context ? on : off);
|
return twMerge(className, context ? on : off);
|
||||||
};
|
};
|
||||||
|
@@ -9,10 +9,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
--radius-sm: calc(var(--radius) - 4px);
|
|
||||||
--radius-md: calc(var(--radius) - 2px);
|
|
||||||
--radius-lg: var(--radius);
|
|
||||||
--radius-xl: calc(var(--radius) + 4px);
|
|
||||||
--color-background: var(--background);
|
--color-background: var(--background);
|
||||||
--color-foreground: var(--foreground);
|
--color-foreground: var(--foreground);
|
||||||
--color-card: var(--card);
|
--color-card: var(--card);
|
||||||
@@ -28,6 +24,7 @@
|
|||||||
--color-accent: var(--accent);
|
--color-accent: var(--accent);
|
||||||
--color-accent-foreground: var(--accent-foreground);
|
--color-accent-foreground: var(--accent-foreground);
|
||||||
--color-destructive: var(--destructive);
|
--color-destructive: var(--destructive);
|
||||||
|
--color-destructive-foreground: var(--destructive-foreground);
|
||||||
--color-border: var(--border);
|
--color-border: var(--border);
|
||||||
--color-input: var(--input);
|
--color-input: var(--input);
|
||||||
--color-ring: var(--ring);
|
--color-ring: var(--ring);
|
||||||
@@ -44,102 +41,120 @@
|
|||||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||||
--color-sidebar-border: var(--sidebar-border);
|
--color-sidebar-border: var(--sidebar-border);
|
||||||
--color-sidebar-ring: var(--sidebar-ring);
|
--color-sidebar-ring: var(--sidebar-ring);
|
||||||
|
|
||||||
|
--font-sans: var(--font-sans);
|
||||||
|
--font-mono: var(--font-mono);
|
||||||
|
--font-serif: var(--font-serif);
|
||||||
|
|
||||||
|
--radius-sm: calc(var(--radius) - 4px);
|
||||||
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
|
--radius-lg: var(--radius);
|
||||||
|
--radius-xl: calc(var(--radius) + 4px);
|
||||||
|
|
||||||
|
--shadow-2xs: var(--shadow-2xs);
|
||||||
|
--shadow-xs: var(--shadow-xs);
|
||||||
|
--shadow-sm: var(--shadow-sm);
|
||||||
|
--shadow: var(--shadow);
|
||||||
|
--shadow-md: var(--shadow-md);
|
||||||
|
--shadow-lg: var(--shadow-lg);
|
||||||
|
--shadow-xl: var(--shadow-xl);
|
||||||
|
--shadow-2xl: var(--shadow-2xl);
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--background: oklch(0.9785 0.0045 314.8050);
|
--background: oklch(0.9227 0.0011 17.1793);
|
||||||
--foreground: oklch(0.3710 0.0333 301.6287);
|
--foreground: oklch(0.2840 0.0220 262.4967);
|
||||||
--card: oklch(0.9940 0 0);
|
--card: oklch(0.9699 0.0013 106.4238);
|
||||||
--card-foreground: oklch(0.3710 0.0333 301.6287);
|
--card-foreground: oklch(0.2840 0.0220 262.4967);
|
||||||
--popover: oklch(0.9940 0 0);
|
--popover: oklch(0.9699 0.0013 106.4238);
|
||||||
--popover-foreground: oklch(0.3710 0.0333 301.6287);
|
--popover-foreground: oklch(0.2840 0.0220 262.4967);
|
||||||
--primary: oklch(0.4868 0.1488 286.5771);
|
--primary: oklch(0.6378 0.1247 281.2150);
|
||||||
--primary-foreground: oklch(0.9785 0.0045 314.8050);
|
--primary-foreground: oklch(1.0000 0 0);
|
||||||
--secondary: oklch(0.9139 0.0448 291.0467);
|
--secondary: oklch(0.8682 0.0026 48.7143);
|
||||||
--secondary-foreground: oklch(0.3008 0.0773 288.1551);
|
--secondary-foreground: oklch(0.4507 0.0152 255.5845);
|
||||||
--muted: oklch(0.8930 0.0149 312.2335);
|
--muted: oklch(0.9227 0.0011 17.1793);
|
||||||
--muted-foreground: oklch(0.5361 0.0391 305.8579);
|
--muted-foreground: oklch(0.5551 0.0147 266.6154);
|
||||||
--accent: oklch(0.8514 0.0535 342.1042);
|
--accent: oklch(0.9409 0.0164 322.6966);
|
||||||
--accent-foreground: oklch(0.3328 0.0528 311.4628);
|
--accent-foreground: oklch(0.3774 0.0189 260.6754);
|
||||||
--destructive: oklch(0.6984 0.1170 47.0382);
|
--destructive: oklch(0.6322 0.1310 21.4751);
|
||||||
--destructive-foreground: oklch(0.9785 0.0045 314.8050);
|
--destructive-foreground: oklch(1.0000 0 0);
|
||||||
--border: oklch(0.8488 0.0244 313.1102);
|
--border: oklch(0.8682 0.0026 48.7143);
|
||||||
--input: oklch(0.9352 0.0136 314.7562);
|
--input: oklch(0.8682 0.0026 48.7143);
|
||||||
--ring: oklch(0.4868 0.1488 286.5771);
|
--ring: oklch(0.6378 0.1247 281.2150);
|
||||||
--chart-1: oklch(0.4868 0.1488 286.5771);
|
--chart-1: oklch(0.6378 0.1247 281.2150);
|
||||||
--chart-2: oklch(0.8514 0.0535 342.1042);
|
--chart-2: oklch(0.5608 0.1433 283.1275);
|
||||||
--chart-3: oklch(0.7388 0.0664 194.5709);
|
--chart-3: oklch(0.5008 0.1358 283.9499);
|
||||||
--chart-4: oklch(0.9197 0.1140 104.6226);
|
--chart-4: oklch(0.4372 0.1108 283.4322);
|
||||||
--chart-5: oklch(0.7409 0.0895 280.3986);
|
--chart-5: oklch(0.3928 0.0817 282.8932);
|
||||||
--sidebar: oklch(0.9569 0.0090 314.7812);
|
--sidebar: oklch(0.8682 0.0026 48.7143);
|
||||||
--sidebar-foreground: oklch(0.3710 0.0333 301.6287);
|
--sidebar-foreground: oklch(0.2840 0.0220 262.4967);
|
||||||
--sidebar-primary: oklch(0.4868 0.1488 286.5771);
|
--sidebar-primary: oklch(0.6378 0.1247 281.2150);
|
||||||
--sidebar-primary-foreground: oklch(0.9785 0.0045 314.8050);
|
--sidebar-primary-foreground: oklch(1.0000 0 0);
|
||||||
--sidebar-accent: oklch(0.8514 0.0535 342.1042);
|
--sidebar-accent: oklch(0.9409 0.0164 322.6966);
|
||||||
--sidebar-accent-foreground: oklch(0.3328 0.0528 311.4628);
|
--sidebar-accent-foreground: oklch(0.3774 0.0189 260.6754);
|
||||||
--sidebar-border: oklch(0.8759 0.0218 316.4501);
|
--sidebar-border: oklch(0.8682 0.0026 48.7143);
|
||||||
--sidebar-ring: oklch(0.4868 0.1488 286.5771);
|
--sidebar-ring: oklch(0.6378 0.1247 281.2150);
|
||||||
--font-sans: Inter, sans-serif;
|
--font-sans: Inter, sans-serif;
|
||||||
--font-serif: "Lora", Georgia, serif;
|
--font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
|
||||||
--font-mono: "Fira Code", "Courier New", monospace;
|
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||||
--radius: 0.5rem;
|
--radius: 1.0rem;
|
||||||
--shadow-2xs: 1px 2px 5px 1px hsl(0 0% 10.1961% / 0.03);
|
--shadow-2xs: 2px 2px 10px 4px hsl(240 1.9608% 60% / 0.09);
|
||||||
--shadow-xs: 1px 2px 5px 1px hsl(0 0% 10.1961% / 0.03);
|
--shadow-xs: 2px 2px 10px 4px hsl(240 1.9608% 60% / 0.09);
|
||||||
--shadow-sm: 1px 2px 5px 1px hsl(0 0% 10.1961% / 0.06), 1px 1px 2px 0px hsl(0 0% 10.1961% / 0.06);
|
--shadow-sm: 2px 2px 10px 4px hsl(240 1.9608% 60% / 0.18), 2px 1px 2px 3px hsl(240 1.9608% 60% / 0.18);
|
||||||
--shadow: 1px 2px 5px 1px hsl(0 0% 10.1961% / 0.06), 1px 1px 2px 0px hsl(0 0% 10.1961% / 0.06);
|
--shadow: 2px 2px 10px 4px hsl(240 1.9608% 60% / 0.18), 2px 1px 2px 3px hsl(240 1.9608% 60% / 0.18);
|
||||||
--shadow-md: 1px 2px 5px 1px hsl(0 0% 10.1961% / 0.06), 1px 2px 4px 0px hsl(0 0% 10.1961% / 0.06);
|
--shadow-md: 2px 2px 10px 4px hsl(240 1.9608% 60% / 0.18), 2px 2px 4px 3px hsl(240 1.9608% 60% / 0.18);
|
||||||
--shadow-lg: 1px 2px 5px 1px hsl(0 0% 10.1961% / 0.06), 1px 4px 6px 0px hsl(0 0% 10.1961% / 0.06);
|
--shadow-lg: 2px 2px 10px 4px hsl(240 1.9608% 60% / 0.18), 2px 4px 6px 3px hsl(240 1.9608% 60% / 0.18);
|
||||||
--shadow-xl: 1px 2px 5px 1px hsl(0 0% 10.1961% / 0.06), 1px 8px 10px 0px hsl(0 0% 10.1961% / 0.06);
|
--shadow-xl: 2px 2px 10px 4px hsl(240 1.9608% 60% / 0.18), 2px 8px 10px 3px hsl(240 1.9608% 60% / 0.18);
|
||||||
--shadow-2xl: 1px 2px 5px 1px hsl(0 0% 10.1961% / 0.15);
|
--shadow-2xl: 2px 2px 10px 4px hsl(240 1.9608% 60% / 0.45);
|
||||||
--tracking-normal: 0em;
|
--tracking-normal: 0em;
|
||||||
--spacing: 0.25rem;
|
--spacing: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--background: oklch(0.2213 0.0228 309.2819);
|
--background: oklch(0.2236 0.0049 67.5717);
|
||||||
--foreground: oklch(0.9100 0.0260 308.1435);
|
--foreground: oklch(0.9301 0.0075 260.7315);
|
||||||
--card: oklch(0.2671 0.0372 295.2445);
|
--card: oklch(0.2793 0.0057 56.1503);
|
||||||
--card-foreground: oklch(0.9100 0.0260 308.1435);
|
--card-foreground: oklch(0.9301 0.0075 260.7315);
|
||||||
--popover: oklch(0.2671 0.0372 295.2445);
|
--popover: oklch(0.2793 0.0057 56.1503);
|
||||||
--popover-foreground: oklch(0.9100 0.0260 308.1435);
|
--popover-foreground: oklch(0.9301 0.0075 260.7315);
|
||||||
--primary: oklch(0.6405 0.1338 286.4998);
|
--primary: oklch(0.7223 0.0946 279.6746);
|
||||||
--primary-foreground: oklch(0.2213 0.0228 309.2819);
|
--primary-foreground: oklch(0.2236 0.0049 67.5717);
|
||||||
--secondary: oklch(0.4071 0.0776 288.3025);
|
--secondary: oklch(0.3352 0.0055 56.2080);
|
||||||
--secondary-foreground: oklch(0.9100 0.0260 308.1435);
|
--secondary-foreground: oklch(0.8726 0.0059 264.5296);
|
||||||
--muted: oklch(0.2630 0.0340 310.8818);
|
--muted: oklch(0.2793 0.0057 56.1503);
|
||||||
--muted-foreground: oklch(0.7026 0.0304 313.2720);
|
--muted-foreground: oklch(0.7176 0.0111 261.7826);
|
||||||
--accent: oklch(0.3328 0.0528 311.4628);
|
--accent: oklch(0.3889 0.0053 56.2463);
|
||||||
--accent-foreground: oklch(0.8076 0.0881 341.1289);
|
--accent-foreground: oklch(0.8726 0.0059 264.5296);
|
||||||
--destructive: oklch(0.7501 0.1053 47.2117);
|
--destructive: oklch(0.6322 0.1310 21.4751);
|
||||||
--destructive-foreground: oklch(0.2213 0.0228 309.2819);
|
--destructive-foreground: oklch(0.2236 0.0049 67.5717);
|
||||||
--border: oklch(0.3139 0.0379 309.3053);
|
--border: oklch(0.3352 0.0055 56.2080);
|
||||||
--input: oklch(0.2913 0.0360 305.8978);
|
--input: oklch(0.3352 0.0055 56.2080);
|
||||||
--ring: oklch(0.6405 0.1338 286.4998);
|
--ring: oklch(0.7223 0.0946 279.6746);
|
||||||
--chart-1: oklch(0.6405 0.1338 286.4998);
|
--chart-1: oklch(0.7223 0.0946 279.6746);
|
||||||
--chart-2: oklch(0.8076 0.0881 341.1289);
|
--chart-2: oklch(0.6378 0.1247 281.2150);
|
||||||
--chart-3: oklch(0.7388 0.0664 194.5709);
|
--chart-3: oklch(0.5608 0.1433 283.1275);
|
||||||
--chart-4: oklch(0.9197 0.1140 104.6226);
|
--chart-4: oklch(0.5008 0.1358 283.9499);
|
||||||
--chart-5: oklch(0.4868 0.1488 286.5771);
|
--chart-5: oklch(0.4372 0.1108 283.4322);
|
||||||
--sidebar: oklch(0.2039 0.0232 309.1750);
|
--sidebar: oklch(0.3352 0.0055 56.2080);
|
||||||
--sidebar-foreground: oklch(0.9100 0.0260 308.1435);
|
--sidebar-foreground: oklch(0.9301 0.0075 260.7315);
|
||||||
--sidebar-primary: oklch(0.6405 0.1338 286.4998);
|
--sidebar-primary: oklch(0.7223 0.0946 279.6746);
|
||||||
--sidebar-primary-foreground: oklch(0.2213 0.0228 309.2819);
|
--sidebar-primary-foreground: oklch(0.2236 0.0049 67.5717);
|
||||||
--sidebar-accent: oklch(0.3328 0.0528 311.4628);
|
--sidebar-accent: oklch(0.3889 0.0053 56.2463);
|
||||||
--sidebar-accent-foreground: oklch(0.8076 0.0881 341.1289);
|
--sidebar-accent-foreground: oklch(0.8726 0.0059 264.5296);
|
||||||
--sidebar-border: oklch(0.2913 0.0360 305.8978);
|
--sidebar-border: oklch(0.3352 0.0055 56.2080);
|
||||||
--sidebar-ring: oklch(0.6405 0.1338 286.4998);
|
--sidebar-ring: oklch(0.7223 0.0946 279.6746);
|
||||||
--font-sans: Inter, sans-serif;
|
--font-sans: Inter, sans-serif;
|
||||||
--font-serif: "Lora", Georgia, serif;
|
--font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
|
||||||
--font-mono: "Fira Code", "Courier New", monospace;
|
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||||
--radius: 0.5rem;
|
--radius: 1.0rem;
|
||||||
--shadow-2xs: 1px 2px 5px 1px hsl(0 0% 10.1961% / 0.03);
|
--shadow-2xs: 2px 2px 10px 4px hsl(0 0% 10.1961% / 0.09);
|
||||||
--shadow-xs: 1px 2px 5px 1px hsl(0 0% 10.1961% / 0.03);
|
--shadow-xs: 2px 2px 10px 4px hsl(0 0% 10.1961% / 0.09);
|
||||||
--shadow-sm: 1px 2px 5px 1px hsl(0 0% 10.1961% / 0.06), 1px 1px 2px 0px hsl(0 0% 10.1961% / 0.06);
|
--shadow-sm: 2px 2px 10px 4px hsl(0 0% 10.1961% / 0.18), 2px 1px 2px 3px hsl(0 0% 10.1961% / 0.18);
|
||||||
--shadow: 1px 2px 5px 1px hsl(0 0% 10.1961% / 0.06), 1px 1px 2px 0px hsl(0 0% 10.1961% / 0.06);
|
--shadow: 2px 2px 10px 4px hsl(0 0% 10.1961% / 0.18), 2px 1px 2px 3px hsl(0 0% 10.1961% / 0.18);
|
||||||
--shadow-md: 1px 2px 5px 1px hsl(0 0% 10.1961% / 0.06), 1px 2px 4px 0px hsl(0 0% 10.1961% / 0.06);
|
--shadow-md: 2px 2px 10px 4px hsl(0 0% 10.1961% / 0.18), 2px 2px 4px 3px hsl(0 0% 10.1961% / 0.18);
|
||||||
--shadow-lg: 1px 2px 5px 1px hsl(0 0% 10.1961% / 0.06), 1px 4px 6px 0px hsl(0 0% 10.1961% / 0.06);
|
--shadow-lg: 2px 2px 10px 4px hsl(0 0% 10.1961% / 0.18), 2px 4px 6px 3px hsl(0 0% 10.1961% / 0.18);
|
||||||
--shadow-xl: 1px 2px 5px 1px hsl(0 0% 10.1961% / 0.06), 1px 8px 10px 0px hsl(0 0% 10.1961% / 0.06);
|
--shadow-xl: 2px 2px 10px 4px hsl(0 0% 10.1961% / 0.18), 2px 8px 10px 3px hsl(0 0% 10.1961% / 0.18);
|
||||||
--shadow-2xl: 1px 2px 5px 1px hsl(0 0% 10.1961% / 0.15);
|
--shadow-2xl: 2px 2px 10px 4px hsl(0 0% 10.1961% / 0.45);
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
|
Reference in New Issue
Block a user