solved merged conflicts
This commit is contained in:
Generated
+10
@@ -3185,6 +3185,16 @@
|
||||
"node": ">=0.8.19"
|
||||
}
|
||||
},
|
||||
"node_modules/input-otp": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/input-otp/-/input-otp-1.4.2.tgz",
|
||||
"integrity": "sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/internmap": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import { OTPInput, OTPInputContext } from "input-otp";
|
||||
import { MinusIcon } from "lucide-react";
|
||||
|
||||
import { cn } from "../../lib/utils";
|
||||
|
||||
function InputOTP({
|
||||
className,
|
||||
containerClassName,
|
||||
...props
|
||||
}: React.ComponentProps<typeof OTPInput> & {
|
||||
containerClassName?: string;
|
||||
}) {
|
||||
return (
|
||||
<OTPInput
|
||||
data-slot="input-otp"
|
||||
containerClassName={cn(
|
||||
"flex items-center gap-2 has-disabled:opacity-50",
|
||||
containerClassName
|
||||
)}
|
||||
className={cn("disabled:cursor-not-allowed", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function InputOTPGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="input-otp-group"
|
||||
className={cn("flex items-center gap-2", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function InputOTPSlot({
|
||||
index,
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div"> & {
|
||||
index: number;
|
||||
}) {
|
||||
const inputOTPContext = React.useContext(OTPInputContext);
|
||||
const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {};
|
||||
|
||||
return (
|
||||
<div
|
||||
data-slot="input-otp-slot"
|
||||
data-active={isActive}
|
||||
className={cn(
|
||||
"relative flex h-10 w-10 items-center justify-center border border-input bg-background text-sm transition-all",
|
||||
"first:rounded-l-md first:border-l last:rounded-r-md",
|
||||
"data-[active=true]:border-primary data-[active=true]:ring-2 data-[active=true]:ring-primary/20",
|
||||
"aria-invalid:border-destructive aria-invalid:ring-destructive/20",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{char}
|
||||
{hasFakeCaret && (
|
||||
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
|
||||
<div className="animate-caret-blink bg-foreground h-4 w-px duration-1000" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function InputOTPSeparator({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="input-otp-separator"
|
||||
className={cn("text-muted-foreground", className)}
|
||||
role="separator"
|
||||
{...props}
|
||||
>
|
||||
<MinusIcon className="h-4 w-4" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
InputOTP,
|
||||
InputOTPGroup,
|
||||
InputOTPSlot,
|
||||
InputOTPSeparator,
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
import React from 'react';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
|
||||
interface LoaderProps {
|
||||
size?: 'sm' | 'md' | 'lg' | 'xl';
|
||||
text?: string;
|
||||
className?: string;
|
||||
fullScreen?: boolean; // New prop for full page loader
|
||||
}
|
||||
|
||||
/**
|
||||
* Size mapping for consistent loader dimensions
|
||||
*/
|
||||
const sizeClasses = {
|
||||
sm: 'w-4 h-4',
|
||||
md: 'w-8 h-8',
|
||||
lg: 'w-12 h-12',
|
||||
xl: 'w-16 h-16',
|
||||
};
|
||||
|
||||
/**
|
||||
* Inline Loader - Used inside components, cards, buttons, etc.
|
||||
*/
|
||||
export const CustomLoader: React.FC<LoaderProps> = ({
|
||||
size = 'md',
|
||||
text,
|
||||
className = '',
|
||||
}) => {
|
||||
return (
|
||||
<div className={`flex flex-col items-center justify-center p-4 ${className}`}>
|
||||
<Loader2 className={`${sizeClasses[size]} animate-spin text-primary`} />
|
||||
{text && (
|
||||
<p className="mt-2 text-sm text-gray-500 font-medium">{text}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Page Loader - Full screen overlay for initial loading or route transitions
|
||||
*/
|
||||
export const PageLoader: React.FC<LoaderProps> = ({
|
||||
size = 'lg',
|
||||
text = 'Loading...',
|
||||
className = '',
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={`fixed inset-0 z-[9999] flex flex-col items-center justify-center
|
||||
bg-white/90 backdrop-blur-sm ${className}`}
|
||||
>
|
||||
<div className="relative flex flex-col items-center">
|
||||
<Loader2 className={`${sizeClasses[size]} animate-spin text-primary`} />
|
||||
|
||||
{/* Optional subtle ring effect */}
|
||||
<div className={`absolute inset-0 ${sizeClasses[size]} border-4 border-gray-200 rounded-full`} />
|
||||
</div>
|
||||
|
||||
{text && (
|
||||
<p className="mt-6 text-sm font-medium text-gray-600 tracking-wide animate-pulse">
|
||||
{text}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Default export (most commonly used)
|
||||
export default CustomLoader;
|
||||
Reference in New Issue
Block a user