Files
lti-web-client/src/components/modal/ConfirmationModal.tsx
T
2026-01-26 22:20:03 +07:00

237 lines
6.4 KiB
TypeScript

'use client';
import { MouseEventHandler, RefObject, useState } from 'react';
import { Icon } from '@iconify/react';
import Modal from '@/components/Modal';
import Button, { ButtonProps } from '@/components/Button';
import { cn } from '@/lib/helper';
export type IconPosition = 'left' | 'center' | 'right';
export interface ConfirmationModalProps {
ref: RefObject<HTMLDialogElement | null>;
type?: 'info' | 'success' | 'error';
text?: string;
subtitleText?: string;
closeOnBackdrop?: boolean;
primaryButton?: ButtonProps & {
text?: string;
};
secondaryButton?: ButtonProps & {
text?: string;
};
className?: {
modal?: string;
modalBox?: string;
};
children?: React.ReactNode;
iconSize?: number;
iconPosition?: IconPosition;
}
const iconConfig = {
info: {
icon: 'material-symbols:info-outline-rounded',
iconClassName: 'text-info-content',
innerRingClassName: 'bg-info',
middleRingClassName: 'bg-info/12',
outerRingClassName: 'border-info/12 bg-info/8',
},
success: {
icon: 'heroicons:check',
iconClassName: 'text-white',
innerRingClassName: 'bg-success',
middleRingClassName: 'bg-success/12',
outerRingClassName: 'border-success/12 bg-success/8',
},
error: {
icon: 'heroicons:exclamation-triangle',
iconClassName: 'text-error-content',
innerRingClassName: 'bg-error',
middleRingClassName: 'bg-error/12',
outerRingClassName: 'border-error/12 bg-error/8',
},
} as const;
const ConfirmationModalIcon = ({
type,
size = 16,
}: {
type: 'info' | 'success' | 'error';
size?: number;
}) => {
const config = iconConfig[type];
return (
<div
className={cn('rounded-full border p-[5px]', config.outerRingClassName)}
>
<div className={cn('rounded-full p-2', config.middleRingClassName)}>
<div
className={cn(
'rounded-full p-1 flex items-center justify-center',
config.innerRingClassName
)}
>
<Icon
icon={config.icon}
width={size}
height={size}
className={config.iconClassName}
/>
</div>
</div>
</div>
);
};
const ConfirmationModal = ({
ref,
type = 'info',
text,
subtitleText,
closeOnBackdrop,
primaryButton,
secondaryButton,
className,
children,
iconSize = 16,
iconPosition = 'center',
}: ConfirmationModalProps) => {
const [isPrimaryButtonLoading, setIsPrimaryButtonLoading] = useState(false);
const closeModalHandler = () => {
ref.current?.close();
};
const primaryButtonClickHandler: MouseEventHandler<
HTMLButtonElement
> = async (event) => {
setIsPrimaryButtonLoading(true);
await primaryButton?.onClick?.(event);
setIsPrimaryButtonLoading(false);
};
return (
<Modal
ref={ref}
closeOnBackdrop={closeOnBackdrop}
className={{
...className,
modalBox: cn('rounded-xl p-4', className?.modalBox),
}}
>
<div className='w-full flex flex-col gap-4'>
{iconPosition === 'center' ? (
<>
<div className='w-fit mx-auto'>
<ConfirmationModalIcon type={type} size={iconSize} />
</div>
<p className='text-center font-medium'>
{text ?? 'Apakah anda yakin ingin melakukan hal ini?'}
</p>
{subtitleText && (
<p className='text-center text-sm text-gray-400'>
{subtitleText}
</p>
)}
</>
) : (
<div
className={cn('flex flex-row items-center gap-3', {
'flex-row': iconPosition === 'left',
'flex-row-reverse': iconPosition === 'right',
})}
>
<div className='w-fit'>
<ConfirmationModalIcon type={type} size={iconSize} />
</div>
<div className='flex flex-col gap-1'>
<p className='text-sm font-semibold'>
{text ?? 'Apakah anda yakin ingin melakukan hal ini?'}
</p>
{subtitleText && (
<p className='text-xs text-base-content/50'>{subtitleText}</p>
)}
</div>
</div>
)}
{children && <div className='w-full'>{children}</div>}
{(secondaryButton || primaryButton) && (
<div
className={cn('w-full grid gap-3', {
'grid-cols-2': secondaryButton && primaryButton,
'grid-cols-1':
(secondaryButton && !primaryButton) ||
(!secondaryButton && primaryButton),
})}
>
{secondaryButton && secondaryButton.text && (
<Button
{...secondaryButton}
variant='outline'
color={secondaryButton?.color}
isLoading={secondaryButton?.isLoading}
disabled={
secondaryButton?.isLoading !== undefined
? secondaryButton?.isLoading
: isPrimaryButtonLoading
}
onClick={(e) => {
if (secondaryButton?.onClick) {
secondaryButton.onClick(e);
} else {
closeModalHandler();
}
}}
className={cn(
'p-2 rounded-xl text-sm',
secondaryButton?.className
)}
>
{secondaryButton?.text ?? 'Tidak'}
</Button>
)}
{primaryButton && primaryButton.text && (
<Button
{...primaryButton}
color={primaryButton?.color ?? 'info'}
onClick={primaryButtonClickHandler}
isLoading={
primaryButton?.isLoading !== undefined
? primaryButton?.isLoading
: isPrimaryButtonLoading
}
disabled={
primaryButton?.isLoading !== undefined
? primaryButton?.isLoading
: isPrimaryButtonLoading
}
className={cn(
'p-2 rounded-xl text-sm',
primaryButton?.className
)}
>
{primaryButton?.text ?? 'Ya'}
</Button>
)}
</div>
)}
</div>
</Modal>
);
};
export default ConfirmationModal;