diff --git a/app/components/Button.tsx b/app/components/Button.tsx index 0fcf295..3f1c337 100644 --- a/app/components/Button.tsx +++ b/app/components/Button.tsx @@ -1,12 +1,12 @@ import type { Dispatch, SetStateAction } from 'react'; import React, { useRef } from 'react'; -import { useButton, type AriaButtonOptions } from 'react-aria'; +import { type AriaButtonOptions, useButton } from 'react-aria'; import { cn } from '~/utils/cn'; export interface ButtonProps extends AriaButtonOptions<'button'> { - variant?: 'heavy' | 'light' - className?: string - children?: React.ReactNode + variant?: 'heavy' | 'light' | 'danger'; + className?: string; + children?: React.ReactNode; } export default function Button({ variant = 'light', ...props }: ButtonProps) { @@ -23,17 +23,20 @@ export default function Button({ variant = 'light', ...props }: ButtonProps) { props.isDisabled && 'opacity-60 cursor-not-allowed', ...(variant === 'heavy' ? [ - 'bg-headplane-900 dark:bg-headplane-50 font-semibold', - 'hover:bg-headplane-900/90 dark:hover:bg-headplane-50/90', - 'text-headplane-200 dark:text-headplane-800' - ] : [ - 'bg-headplane-100 dark:bg-headplane-700/30 font-medium', - 'hover:bg-headplane-200/90 dark:hover:bg-headplane-800/30', - ]), + 'bg-headplane-900 dark:bg-headplane-50 font-semibold', + 'hover:bg-headplane-900/90 dark:hover:bg-headplane-50/90', + 'text-headplane-200 dark:text-headplane-800', + ] + : variant === 'danger' + ? ['bg-red-500 text-white font-semibold', 'hover:bg-red-500/90'] + : [ + 'bg-headplane-100 dark:bg-headplane-700/30 font-medium', + 'hover:bg-headplane-200/90 dark:hover:bg-headplane-800/30', + ]), props.className, )} > {props.children} - ) + ); } diff --git a/app/components/Dialog.tsx b/app/components/Dialog.tsx index 3566dd9..7f8f2f3 100644 --- a/app/components/Dialog.tsx +++ b/app/components/Dialog.tsx @@ -1,97 +1,156 @@ -import React, { Dispatch, ReactNode, SetStateAction } from 'react'; -import Button, { ButtonProps } from '~/components/Button'; -import Title from '~/components/Title'; -import Text from '~/components/Text'; -import Card from '~/components/Card'; +import React, { cloneElement, useRef } from 'react'; import { - Dialog as AriaDialog, - DialogTrigger, - Modal, - ModalOverlay, -} from 'react-aria-components'; + type AriaDialogProps, + type AriaModalOverlayProps, + Overlay, + useDialog, + useModalOverlay, + useOverlayTrigger, +} from 'react-aria'; +import { Form, type HTMLFormMethod } from 'react-router'; +import { + type OverlayTriggerProps, + type OverlayTriggerState, + useOverlayTriggerState, +} from 'react-stately'; +import Button, { ButtonProps } from '~/components/Button'; +import Card from '~/components/Card'; +import IconButton, { IconButtonProps } from '~/components/IconButton'; +import Text from '~/components/Text'; +import Title from '~/components/Title'; import { cn } from '~/utils/cn'; -interface ActionProps extends Omit { - variant: 'cancel' | 'confirm'; +export interface DialogProps extends OverlayTriggerProps { + children: [ + React.ReactElement | React.ReactElement, + React.ReactElement, + ]; } -function Action(props: ActionProps) { - return ( - + + + + + ); +} + +interface DModalProps extends AriaModalOverlayProps { + children: React.ReactNode; + state: OverlayTriggerState; +} + +function DModal(props: DModalProps) { + const { children, state } = props; + const ref = useRef(null); + const { modalProps, underlayProps } = useModalOverlay(props, state, ref); + + if (!state.isOpen) { + return null; } - return {children}; + return ( + +