admin/components/heroui/InputOtp.tsx
alisaza e1eaf5eff5 feat: initial ghabilee-admin backoffice app
Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js
app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
2026-09-05 13:12:59 +03:30

76 lines
2.3 KiB
TypeScript

'use client'
import type { ComponentProps, ReactNode } from 'react'
import { Description, FieldError, InputOTP } from '@heroui/react'
import { cn } from '@/lib/cn'
type InputOtpProps = Omit<
ComponentProps<typeof InputOTP>,
'children' | 'className' | 'fullWidth' | 'maxLength' | 'radius' | 'size' | 'variant'
> & {
className?: string
classNames?: { segmentWrapper?: string; segment?: string }
description?: ReactNode
descriptionClassName?: string
errorMessage?: ReactNode
fullWidth?: boolean
isReadOnly?: boolean
isRequired?: boolean
length: number
radius?: string
size?: string
validationBehavior?: 'native' | 'aria'
variant?: 'flat' | 'bordered' | 'faded' | 'underlined'
}
export function InputOtp({
className,
classNames,
description,
descriptionClassName,
errorMessage,
fullWidth: _fullWidth,
isInvalid,
isReadOnly,
isRequired,
length,
radius: _radius,
size: _size,
validationBehavior: _validationBehavior,
variant,
...props
}: InputOtpProps) {
return (
<div className={cn('flex w-full min-w-0 flex-col gap-1 overflow-x-clip px-5', className)}>
<InputOTP
{...props}
autoComplete={props.autoComplete ?? 'one-time-code'}
isInvalid={isInvalid}
maxLength={length}
readOnly={isReadOnly}
required={isRequired}
variant={variant === 'bordered' || variant === 'underlined' ? 'secondary' : 'primary'}
>
<InputOTP.Group className={cn('flex w-full min-w-0 justify-center gap-2', classNames?.segmentWrapper)}>
{Array.from({ length }, (_, index) => (
<InputOTP.Slot
key={index}
className={cn(
'shrink !outline-none !ring-0 !shadow-none',
'data-[active=true]:!border-primary data-[active=true]:!outline-none data-[active=true]:!ring-0',
'data-[invalid=true]:!border data-[invalid=true]:!border-fourth data-[invalid=true]:!outline-none data-[invalid=true]:!ring-0',
classNames?.segment
)}
index={index}
/>
))}
</InputOTP.Group>
</InputOTP>
{description ? <Description className={descriptionClassName}>{description}</Description> : null}
{isInvalid && errorMessage ? <FieldError className="text-center text-sm">{errorMessage}</FieldError> : null}
</div>
)
}