첫 커밋

This commit is contained in:
focp212@naver.com
2025-09-05 15:36:48 +09:00
commit 05238b04c1
825 changed files with 176358 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
export interface DialogButtonsProps {
buttonLabel: string[];
onConfirm: () => void;
onCancel: () => void;
}
export const DialogButtons = ({ buttonLabel, onConfirm, onCancel }: DialogButtonsProps) => {
if (buttonLabel.length === 1) {
return (
<button className="btn-40 btn-darkgray" onClick={onConfirm}>
{buttonLabel[0]}
</button>
);
}
return (
<div className="popup-btn-group">
{buttonLabel[0] && (
<button className="btn-40 btn-darkgray" onClick={onCancel}>
{buttonLabel[0]}
</button>
)}
{buttonLabel[1] && (
<button className="btn-40 btn-blue" onClick={onConfirm}>
{buttonLabel[1]}
</button>
)}
</div>
);
};

View File

@@ -0,0 +1,95 @@
import { Description, Dialog as _Dialog, DialogPanel, DialogTitle, Transition } from '@headlessui/react';
import { useBlocker } from '@/shared/lib/hooks/use-blocker';
import { useBridge } from '@webview-bridge/react';
import { Fragment, ReactNode, useCallback } from 'react';
import { DialogButtons } from '@/shared/ui/dialogs/dialog-buttons';
import { bridge } from '@/bridge';
export interface DialogProps {
open: boolean;
onConfirmClick?: () => void | Promise<void>;
onCancelClick?: () => void | Promise<void>;
children?: ReactNode;
title?: string | ReactNode;
message?: string | ReactNode;
shouldCloseOnBackdropClick?: boolean;
buttonLabel?: string[];
onClose: () => void;
fullScreenMode?: boolean;
afterLeave: () => void;
}
export const Dialog = ({
open = false,
onConfirmClick,
onCancelClick,
children,
title,
message,
shouldCloseOnBackdropClick = false,
buttonLabel = ['확인'],
onClose,
afterLeave,
fullScreenMode = false,
}: DialogProps) => {
const { nativeDialog, resetNativeDialog } = useBridge(bridge.store, (state) => state);
const whenToBlock = useCallback(() => open, [open]);
// 다이얼로그가 열려있을때 뒤로가기 방지
const blocker = useBlocker(whenToBlock);
const nativeMessage = nativeDialog?.message;
const displayMessage = nativeMessage || message;
const handleClose = useCallback(() => {
resetNativeDialog?.();
onClose();
}, []);
const handleConfirm = useCallback(() => {
blocker?.proceed?.();
onConfirmClick?.();
handleClose();
}, []);
const handleCancel = useCallback(() => {
blocker?.reset?.();
onCancelClick?.();
handleClose();
}, []);
return (
<Transition
afterLeave={afterLeave}
show={open}
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<_Dialog onClose={shouldCloseOnBackdropClick ? onClose : () => null} static={!shouldCloseOnBackdropClick}>
{/* The backdrop, rendered as a fixed sibling to the panel container */}
<div
id="cancelConfirmPopup"
className="popup-overlay"
>
<div className="popup-bg-dim"></div>
<DialogPanel
as="div"
className="popup-container"
>
{displayMessage && (
<Description as="div" className={'popup-title'}>
<p>{displayMessage}</p>
</Description>
)}
{/* render buttonLabel */}
{!fullScreenMode && (
<DialogButtons buttonLabel={buttonLabel} onCancel={handleCancel} onConfirm={handleConfirm} />
)}
</DialogPanel>
</div>
</_Dialog>
</Transition>
);
};

View File

@@ -0,0 +1,9 @@
import { overlay } from 'overlay-kit';
import { delay } from '~/shared/lib/delay';
export const DIALOG_LEAVE_ANIMATION_DURATION = 100;
export const closeDialog = (callback: () => void) => {
overlay.closeAll();
delay(DIALOG_LEAVE_ANIMATION_DURATION).then(callback);
};