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:
giancarlo
2024-03-28 01:30:43 +08:00
parent 500fea4bf8
commit 6048cc4759
21 changed files with 1078 additions and 630 deletions

View File

@@ -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"

View File

@@ -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);
}

View File

@@ -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;

View 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 };