Remove multiple components related to multi-factor authentication setup
Removed personal account related multi-factor authentication setup modal and otp-input. Adjusted dependencies, exports, and imports to reflect the deletion. Various adjustments in other areas of the codebase were made to account for these deletions, including moving necessary components and adding the 'input-otp' library in the package.json under 'ui' directory.
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
"@radix-ui/react-tooltip": "1.0.7",
|
||||
"clsx": "^2.1.0",
|
||||
"cmdk": "^0.2.0",
|
||||
"input-otp": "1.2.3",
|
||||
"react-top-loading-bar": "2.3.1",
|
||||
"tailwind-merge": "^2.2.0"
|
||||
},
|
||||
@@ -75,6 +76,7 @@
|
||||
"prettier": "@kit/prettier-config",
|
||||
"exports": {
|
||||
"./accordion": "./src/shadcn/accordion.tsx",
|
||||
"./alert-dialog": "./src/shadcn/alert-dialog.tsx",
|
||||
"./avatar": "./src/shadcn/avatar.tsx",
|
||||
"./button": "./src/shadcn/button.tsx",
|
||||
"./calendar": "./src/shadcn/calendar.tsx",
|
||||
@@ -101,6 +103,7 @@
|
||||
"./badge": "./src/shadcn/badge.tsx",
|
||||
"./radio-group": "./src/shadcn/radio-group.tsx",
|
||||
"./separator": "./src/shadcn/separator.tsx",
|
||||
"./input-otp": "./src/shadcn/input-otp.tsx",
|
||||
"./utils": "./src/utils/index.ts",
|
||||
"./if": "./src/makerkit/if.tsx",
|
||||
"./trans": "./src/makerkit/trans.tsx",
|
||||
@@ -113,7 +116,6 @@
|
||||
"./global-loader": "./src/makerkit/global-loader.tsx",
|
||||
"./error-boundary": "./src/makerkit/error-boundary.tsx",
|
||||
"./auth-change-listener": "./src/makerkit/auth-change-listener.tsx",
|
||||
"./otp-input": "./src/makerkit/otp-input.tsx",
|
||||
"./loading-overlay": "./src/makerkit/loading-overlay.tsx",
|
||||
"./profile-avatar": "./src/makerkit/profile-avatar.tsx",
|
||||
"./mdx": "./src/makerkit/mdx/mdx-renderer.tsx"
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
import type { FormEventHandler } from 'react';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
|
||||
import { useFieldArray, useForm } from 'react-hook-form';
|
||||
|
||||
import { Input } from '../shadcn/input';
|
||||
|
||||
const DIGITS = 6;
|
||||
|
||||
export function OtpInput({
|
||||
onValid,
|
||||
onInvalid,
|
||||
}: React.PropsWithChildren<{
|
||||
onValid: (code: string) => void;
|
||||
onInvalid: () => void;
|
||||
}>) {
|
||||
const digitsArray = useMemo(
|
||||
() => Array.from({ length: DIGITS }, (_, i) => i),
|
||||
[],
|
||||
);
|
||||
|
||||
const { control, register, watch, setFocus, formState, setValue } = useForm({
|
||||
mode: 'onChange',
|
||||
reValidateMode: 'onChange',
|
||||
defaultValues: {
|
||||
values: digitsArray.map(() => ({ value: '' })),
|
||||
},
|
||||
});
|
||||
|
||||
useFieldArray({
|
||||
control,
|
||||
name: 'values',
|
||||
shouldUnregister: true,
|
||||
});
|
||||
|
||||
const { values } = watch();
|
||||
const isFormValid = formState.isValid;
|
||||
const code = (values ?? []).map(({ value }) => value).join('');
|
||||
|
||||
useEffect(() => {
|
||||
if (!isFormValid) {
|
||||
onInvalid();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (code.length === DIGITS) {
|
||||
onValid(code);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
onInvalid();
|
||||
}, [onInvalid, onValid, code, isFormValid]);
|
||||
|
||||
useEffect(() => {
|
||||
setFocus('values.0.value');
|
||||
}, [setFocus]);
|
||||
|
||||
const onInput: FormEventHandler<HTMLInputElement> = useCallback(
|
||||
(target) => {
|
||||
const element = target.currentTarget;
|
||||
const isValid = element.reportValidity();
|
||||
|
||||
if (isValid) {
|
||||
const nextIndex = Number(element.dataset.index) + 1;
|
||||
|
||||
if (nextIndex >= DIGITS) {
|
||||
return;
|
||||
}
|
||||
|
||||
setFocus(`values.${nextIndex}.value`);
|
||||
}
|
||||
},
|
||||
[setFocus],
|
||||
);
|
||||
|
||||
const onPaste = useCallback(
|
||||
(event: React.ClipboardEvent<HTMLInputElement>) => {
|
||||
const pasted = event.clipboardData.getData('text/plain');
|
||||
|
||||
// check if value is numeric
|
||||
if (isNumeric(pasted)) {
|
||||
const digits = getDigits(pasted, digitsArray);
|
||||
|
||||
digits.forEach((value, index) => {
|
||||
setValue(`values.${index}.value`, value);
|
||||
setFocus(`values.${index + 1}.value`);
|
||||
});
|
||||
}
|
||||
},
|
||||
[digitsArray, setFocus, setValue],
|
||||
);
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (event.key === 'Backspace') {
|
||||
event.preventDefault();
|
||||
|
||||
const index = Number(event.currentTarget.dataset.inputIndex);
|
||||
|
||||
setValue(`values.${index}.value`, '');
|
||||
setFocus(`values.${index - 1}.value`);
|
||||
}
|
||||
},
|
||||
[setFocus, setValue],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={'flex justify-center space-x-2'}>
|
||||
{digitsArray.map((digit, index) => {
|
||||
const control = { ...register(`values.${digit}.value`) };
|
||||
|
||||
return (
|
||||
<Input
|
||||
autoComplete={'off'}
|
||||
className={'w-10 text-center'}
|
||||
data-index={digit}
|
||||
pattern="[0-9]"
|
||||
required
|
||||
key={digit}
|
||||
maxLength={1}
|
||||
onInput={onInput}
|
||||
onPaste={onPaste}
|
||||
onKeyDown={handleKeyDown}
|
||||
data-input-index={index}
|
||||
{...control}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function isNumeric(pasted: string) {
|
||||
const isNumericRegExp = /^-?\d+$/;
|
||||
|
||||
return isNumericRegExp.test(pasted);
|
||||
}
|
||||
|
||||
function getDigits(pasted: string, digitsArray: number[]) {
|
||||
return pasted.split('').slice(0, digitsArray.length);
|
||||
}
|
||||
@@ -4,9 +4,8 @@ import * as React from 'react';
|
||||
|
||||
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
|
||||
|
||||
import { buttonVariants } from '@kit/ui/button';
|
||||
|
||||
import { cn } from '../utils/cn';
|
||||
import { buttonVariants } from './button';
|
||||
|
||||
const AlertDialog = AlertDialogPrimitive.Root;
|
||||
|
||||
|
||||
72
packages/ui/src/shadcn/input-otp.tsx
Normal file
72
packages/ui/src/shadcn/input-otp.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
|
||||
import { DashIcon } from '@radix-ui/react-icons';
|
||||
import { OTPInput, OTPInputContext } from 'input-otp';
|
||||
|
||||
import { cn } from '../utils';
|
||||
|
||||
const InputOTP = React.forwardRef<
|
||||
React.ElementRef<typeof OTPInput>,
|
||||
React.ComponentPropsWithoutRef<typeof OTPInput>
|
||||
>(({ className, containerClassName, ...props }, ref) => (
|
||||
<OTPInput
|
||||
ref={ref}
|
||||
containerClassName={cn(
|
||||
'flex items-center gap-2 has-[:disabled]:opacity-50',
|
||||
containerClassName,
|
||||
)}
|
||||
className={cn('disabled:cursor-not-allowed', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
InputOTP.displayName = 'InputOTP';
|
||||
|
||||
const InputOTPGroup = React.forwardRef<
|
||||
React.ElementRef<'div'>,
|
||||
React.ComponentPropsWithoutRef<'div'>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn('flex items-center', className)} {...props} />
|
||||
));
|
||||
InputOTPGroup.displayName = 'InputOTPGroup';
|
||||
|
||||
const InputOTPSlot = React.forwardRef<
|
||||
React.ElementRef<'div'>,
|
||||
React.ComponentPropsWithoutRef<'div'> & { index: number }
|
||||
>(({ index, className, ...props }, ref) => {
|
||||
const inputOTPContext = React.useContext(OTPInputContext);
|
||||
const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index];
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative flex h-9 w-9 items-center justify-center border-y border-r border-input text-sm shadow-sm transition-all first:rounded-l-md first:border-l last:rounded-r-md',
|
||||
isActive && 'z-10 ring-1 ring-ring',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{char}
|
||||
{hasFakeCaret && (
|
||||
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
|
||||
<div className="animate-caret-blink h-4 w-px bg-foreground duration-1000" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
InputOTPSlot.displayName = 'InputOTPSlot';
|
||||
|
||||
const InputOTPSeparator = React.forwardRef<
|
||||
React.ElementRef<'div'>,
|
||||
React.ComponentPropsWithoutRef<'div'>
|
||||
>(({ ...props }, ref) => (
|
||||
<div ref={ref} role="separator" {...props}>
|
||||
<DashIcon />
|
||||
</div>
|
||||
));
|
||||
InputOTPSeparator.displayName = 'InputOTPSeparator';
|
||||
|
||||
export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator };
|
||||
Reference in New Issue
Block a user