Skip to content

Commit

Permalink
Added: Input OTP component added.
Browse files Browse the repository at this point in the history
  • Loading branch information
Arifulislam5577 committed Aug 21, 2024
1 parent 9b14e53 commit 1f41185
Show file tree
Hide file tree
Showing 18 changed files with 354 additions and 3 deletions.
2 changes: 1 addition & 1 deletion app/docs/components/input/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const page: NextPage = () => {
return (
<DocsContentLayout description={`${metadata.description}`} title={`${metadata.title}`}>
<InputDocs />
<EditPage pageLink="/docs/components/input" nextPageLink="/docs/components/timeline" nextPageName="Timeline" />
<EditPage pageLink="/docs/components/input" nextPageLink="/docs/components/inputOtp" nextPageName="Input OTP" />
</DocsContentLayout>
)
}
Expand Down
44 changes: 44 additions & 0 deletions app/docs/components/inputOtp/InputOTP.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import CodeHighlightPreview from '../../../components/CodeHighlightPreview'
import { DefaultInputOTP, DefaultInputOTPCode } from './variant/DefaultInputOTP'
import { GroupInputOTP, GroupInputOTPCode } from './variant/GroupInputOTP'
import { InputOTPCodeValue, InputOTPValue } from './variant/InputOTPValue'
import { inputOTPApiData } from './InputOTPApi'
import ComponentApi from '../../../components/ComponentApi'

## Table of Contents

The OTP (One-Time Password) Inputs component is a secure and intuitive interface designed to capture verification codes typically sent via SMS or email. It consists of multiple input fields that allow users to enter each digit of the OTP separately, ensuring accurate and quick entry. The component often includes features like automatic focus shift, clear functionality, and input validation to enhance user experience. Ideal for authentication processes, account verification, and secure transactions, the OTP Inputs component plays a crucial role in reinforcing security while maintaining ease of use.

## Default Input OTP

A basic OTP input component designed for straightforward entry of one-time passwords, with automatic focus shift and input validation for a smooth user experience.

<CodeHighlightPreview code={DefaultInputOTPCode}>
<DefaultInputOTP />
</CodeHighlightPreview>

## Group Input OTP

An OTP input component that groups input fields together, providing a more organized and compact layout for entering one-time passwords efficiently.

<CodeHighlightPreview code={GroupInputOTPCode}>
<GroupInputOTP />
</CodeHighlightPreview>

## Control Input OTP

A more advanced OTP input component with value handling.

<CodeHighlightPreview code={InputOTPCodeValue}>
<InputOTPValue />
</CodeHighlightPreview>

## API Reference

Here is a list of the props that you can pass to the input OTP component:

<ComponentApi data={inputOTPApiData} />

## Reference

Our OTP component is built using the `input-otp` package. For more information , follow the documentation [input-otp](https://input-otp.rodz.dev).
23 changes: 23 additions & 0 deletions app/docs/components/inputOtp/InputOTPApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export const inputOTPApiData = [
{
id: 1,
propsName: 'containerClassName',
propsType: 'string',
propsDescription: 'containerClassName style',
default: '',
},
{
id: 2,
propsName: 'maxLength',
propsType: 'number',
propsDescription: 'Input maxLength',
default: '6',
},
{
id: 3,
propsName: 'pattern',
propsType: 'regex',
propsDescription: 'Input pattern',
default: '^\\d+$',
},
]
7 changes: 7 additions & 0 deletions app/docs/components/inputOtp/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use client'
import type { FC } from 'react'
import InputOTPDocsContent from './InputOTP.mdx'

const InputOTPDocs: FC = () => <InputOTPDocsContent />

export default InputOTPDocs
21 changes: 21 additions & 0 deletions app/docs/components/inputOtp/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { Metadata, NextPage } from 'next'
import InputOTPDocs from '.'
import { DocsContentLayout } from '../../../components/DocsContentLayout'
import EditPage from '../../../components/EditPage'

export const metadata: Metadata = {
description:
'The OTP (One-Time Password) Inputs component is a secure and intuitive interface designed to capture verification codes typically sent via SMS or email. It consists of multiple input fields that allow users to enter each digit of the OTP separately, ensuring accurate and quick entry.',
title: 'OTP Input - Keep React',
}

const page: NextPage = () => {
return (
<DocsContentLayout description={`${metadata.description}`} title={`${metadata.title}`}>
<InputOTPDocs />
<EditPage pageLink="/docs/components/inputOtp" nextPageLink="/docs/components/timeline" nextPageName="Timeline" />
</DocsContentLayout>
)
}

export default page
41 changes: 41 additions & 0 deletions app/docs/components/inputOtp/variant/DefaultInputOTP.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { InputOTP, InputOTPGroup, InputOTPItem } from '../../../../src'

const DefaultInputOTP = () => {
return (
<div className="flex items-center justify-center px-5 py-3">
<InputOTP maxLength={6}>
<InputOTPGroup>
<InputOTPItem index={0} />
<InputOTPItem index={1} />
<InputOTPItem index={2} />
<InputOTPItem index={3} />
<InputOTPItem index={4} />
<InputOTPItem index={5} />
</InputOTPGroup>
</InputOTP>
</div>
)
}

const DefaultInputOTPCode = {
'InputOTPComponent.tsx': `
import { InputOTP, InputOTPGroup, InputOTPItem } from 'keep-react'

export const InputOTPComponent = () => {
return (
<InputOTP maxLength={6}>
<InputOTPGroup>
<InputOTPItem index={0} />
<InputOTPItem index={1} />
<InputOTPItem index={2} />
<InputOTPItem index={3} />
<InputOTPItem index={4} />
<InputOTPItem index={5} />
</InputOTPGroup>
</InputOTP>
)
}
`,
}

export { DefaultInputOTP, DefaultInputOTPCode }
53 changes: 53 additions & 0 deletions app/docs/components/inputOtp/variant/GroupInputOTP.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { InputOTP, InputOTPDivider, InputOTPGroup, InputOTPItem } from '../../../../src'

const GroupInputOTP = () => {
return (
<div className="flex items-center justify-center px-5 py-3">
<InputOTP maxLength={6}>
<InputOTPGroup>
<InputOTPItem index={0} />
<InputOTPItem index={1} />
</InputOTPGroup>
<InputOTPDivider />
<InputOTPGroup>
<InputOTPItem index={2} />
<InputOTPItem index={3} />
</InputOTPGroup>
<InputOTPDivider />
<InputOTPGroup>
<InputOTPItem index={4} />
<InputOTPItem index={5} />
</InputOTPGroup>
</InputOTP>
</div>
)
}

const GroupInputOTPCode = {
'InputOTPComponent.tsx': `
import { InputOTP, InputOTPDivider, InputOTPGroup, InputOTPItem } from 'keep-react'

export const InputOTPComponent = () => {
return (
<InputOTP maxLength={6}>
<InputOTPGroup>
<InputOTPItem index={0} />
<InputOTPItem index={1} />
</InputOTPGroup>
<InputOTPDivider />
<InputOTPGroup>
<InputOTPItem index={2} />
<InputOTPItem index={3} />
</InputOTPGroup>
<InputOTPDivider />
<InputOTPGroup>
<InputOTPItem index={4} />
<InputOTPItem index={5} />
</InputOTPGroup>
</InputOTP>
)
}
`,
}

export { GroupInputOTP, GroupInputOTPCode }
53 changes: 53 additions & 0 deletions app/docs/components/inputOtp/variant/InputOTPValue.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
'use client'
import { useState } from 'react'
import { InputOTP, InputOTPDivider, InputOTPGroup, InputOTPItem } from '../../../../src'

const InputOTPValue = () => {
const [value, setValue] = useState('')
return (
<div className="flex items-center justify-center space-y-3 px-5 py-3">
<InputOTP value={value} onChange={(value) => setValue(value)} maxLength={6}>
<InputOTPGroup>
<InputOTPItem index={0} />
<InputOTPItem index={1} />
<InputOTPItem index={2} />
</InputOTPGroup>
<InputOTPDivider />
<InputOTPGroup>
<InputOTPItem index={3} />
<InputOTPItem index={4} />
<InputOTPItem index={5} />
</InputOTPGroup>
</InputOTP>
</div>
)
}

const InputOTPCodeValue = {
'InputOTPComponent.tsx': `
'use client'
import { useState } from 'react'
import { InputOTP, InputOTPGroup, InputOTPItem, InputOTPDivider } from 'keep-react'
export const InputOTPComponent = () => {
const [value, setValue] = useState('')
return (
<InputOTP value={value} onChange={(value) => setValue(value)} maxLength={6}>
<InputOTPGroup>
<InputOTPItem index={0} />
<InputOTPItem index={1} />
<InputOTPItem index={2} />
</InputOTPGroup>
<InputOTPDivider />
<InputOTPGroup>
<InputOTPItem index={3} />
<InputOTPItem index={4} />
<InputOTPItem index={5} />
</InputOTPGroup>
</InputOTP>
)
}
`,
}

export { InputOTPCodeValue, InputOTPValue }
4 changes: 4 additions & 0 deletions app/docs/components/select/Select.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ A select component where each item includes an icon, providing a more visual rep
<CodeHighlightPreview code={SelectWithItemIconCode}>
<SelectWithItemIcon />
</CodeHighlightPreview>

## Reference

Our select component is built using the `@radix-ui/react-select` package. For more information , follow the documentation [@radix-ui/react-select](https://www.radix-ui.com/primitives/docs/components/select#api-reference).
19 changes: 19 additions & 0 deletions app/src/components/InputOTP/InputOTP.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
'use client'
import { OTPInput } from 'input-otp'
import { ComponentPropsWithoutRef, ElementRef, forwardRef } from 'react'
import { cn } from '../../utils/cn'

const InputOTP = forwardRef<ElementRef<typeof OTPInput>, ComponentPropsWithoutRef<typeof OTPInput>>(
({ className, containerClassName, pattern, ...props }, ref) => (
<OTPInput
pattern={pattern}
ref={ref}
containerClassName={cn('flex items-center gap-2.5 has-[:disabled]:opacity-50', containerClassName)}
className={cn('disabled:cursor-not-allowed', className)}
{...props}
/>
),
)
InputOTP.displayName = 'InputOTP'

export { InputOTP }
25 changes: 25 additions & 0 deletions app/src/components/InputOTP/InputOTPDivider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use client'
import { cloneElement, ComponentPropsWithoutRef, ElementRef, forwardRef, isValidElement } from 'react'

type InputOTPDividerProps = ComponentPropsWithoutRef<'div'> & {
asChild?: boolean
}

const InputOTPDivider = forwardRef<ElementRef<'div'>, InputOTPDividerProps>(({ asChild, children, ...props }, ref) => {
if (asChild && isValidElement(children)) {
return cloneElement(children, {
itemRef: ref,
...props,
})
}
return (
<div ref={ref} {...props} className="px-2.5">
<svg xmlns="http://www.w3.org/2000/svg" width={20} height={14} fill="#455468" viewBox="0 0 256 256">
<path d="M224,128a8,8,0,0,1-8,8H40a8,8,0,0,1,0-16H216A8,8,0,0,1,224,128Z"></path>
</svg>
</div>
)
})
InputOTPDivider.displayName = 'InputOTPDivider'

export { InputOTPDivider }
10 changes: 10 additions & 0 deletions app/src/components/InputOTP/InputOTPGroup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use client'
import { ComponentPropsWithoutRef, ElementRef, forwardRef } from 'react'
import { cn } from '../../utils/cn'

const InputOTPGroup = forwardRef<ElementRef<'div'>, ComponentPropsWithoutRef<'div'>>(({ className, ...props }, ref) => (
<div ref={ref} className={cn('flex items-center gap-2.5', className)} {...props} />
))
InputOTPGroup.displayName = 'InputOTPGroup'

export { InputOTPGroup }
33 changes: 33 additions & 0 deletions app/src/components/InputOTP/InputOTPItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use client'
import { OTPInputContext } from 'input-otp'
import { ComponentPropsWithoutRef, ElementRef, forwardRef, useContext } from 'react'
import { cn } from '../../utils/cn'

const InputOTPItem = forwardRef<ElementRef<'div'>, ComponentPropsWithoutRef<'div'> & { index: number }>(
({ index, className, ...props }, ref) => {
const inputOTPContext = useContext(OTPInputContext)

const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]

return (
<div
ref={ref}
className={cn(
'relative flex h-16 w-[52px] items-center justify-center rounded-[10px] border border-metal-100 text-heading-6 font-medium text-metal-900 transition-all dark:border-metal-800 dark:text-white',
isActive && 'z-10 ring-2 ring-metal-600 ring-offset-metal-200 dark:ring-metal-800',
className,
)}
{...props}>
{char}
{hasFakeCaret && (
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
<div className="h-[22px] w-px animate-blinkIn bg-metal-300 duration-1000 dark:bg-white" />
</div>
)}
</div>
)
},
)
InputOTPItem.displayName = 'InputOTPItem'

export { InputOTPItem }
4 changes: 4 additions & 0 deletions app/src/components/InputOTP/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { InputOTP } from './InputOTP'
export { InputOTPDivider } from './InputOTPDivider'
export { InputOTPGroup } from './InputOTPGroup'
export { InputOTPItem } from './InputOTPItem'
1 change: 1 addition & 0 deletions app/src/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export * from './Empty'
export * from './Helpers'
export * from './Input'
export * from './Input/Icon'
export * from './InputOTP'
export * from './Label'
export * from './Modal'
export * from './Navbar'
Expand Down
5 changes: 5 additions & 0 deletions app/src/theme/keepTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,15 @@ const presetFn = (color: ColorThemeType) => {
transform: 'translateY(auto)',
},
},
blinkIn: {
'0%,70%,100%': { opacity: '1' },
'20%,50%': { opacity: '0' },
},
},
animation: {
zoomIn: 'zoomIn 0.3s ease-in-out',
fadeInUp: 'fadeInUp 0.3s ease-out',
blinkIn: 'blinkIn 1.25s ease-out infinite',
},
backgroundImage: {
sun: "url('https://staticmania.cdn.prismic.io/staticmania/Zqc9bB5LeNNTxjUr_sun.svg')",
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"embla-carousel": "^8.0.2",
"embla-carousel-react": "^8.0.2",
"framer-motion": "^11.3.2",
"input-otp": "^1.2.4",
"react": "^18.3.1",
"react-collapse": "^5.1.1",
"react-day-picker": "^8.10.0",
Expand Down
Loading

0 comments on commit 1f41185

Please sign in to comment.