diff --git a/package-lock.json b/package-lock.json index 3a4211e6..a61c7cd0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "tailwindcss": "^4.0.0", "tailwindcss-animate": "^1.0.7", "typescript": "^5.7.2", + "vaul": "^1.1.2", "vite": "^6.0" }, "devDependencies": { @@ -1301,7 +1302,6 @@ "version": "1.1.6", "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.6.tgz", "integrity": "sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw==", - "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", @@ -7037,6 +7037,18 @@ } } }, + "node_modules/vaul": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vaul/-/vaul-1.1.2.tgz", + "integrity": "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==", + "dependencies": { + "@radix-ui/react-dialog": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/vite": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/vite/-/vite-6.1.1.tgz", diff --git a/package.json b/package.json index 8621dbe2..f1dfa5c4 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "tailwindcss": "^4.0.0", "tailwindcss-animate": "^1.0.7", "typescript": "^5.7.2", + "vaul": "^1.1.2", "vite": "^6.0" }, "optionalDependencies": { diff --git a/resources/js/components/delete-user.tsx b/resources/js/components/delete-user.tsx index e1f8788b..788078cc 100644 --- a/resources/js/components/delete-user.tsx +++ b/resources/js/components/delete-user.tsx @@ -1,35 +1,46 @@ import { useForm } from '@inertiajs/react'; -import { FormEventHandler, useRef } from 'react'; +import { FormEventHandler, useCallback, useRef, useState } from 'react'; import InputError from '@/components/input-error'; +import { ResponsiveModal } from '@/components/responsive-modal'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import HeadingSmall from '@/components/heading-small'; -import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; +import { DialogDescription, DialogFooter, DialogTitle } from '@/components/ui/dialog'; export default function DeleteUser() { const passwordInput = useRef(null); + const { data, setData, delete: destroy, processing, reset, errors, clearErrors } = useForm>({ password: '' }); + const [isOpen, setIsOpen] = useState(false); + + const handleModalClose = useCallback(() => { + setIsOpen(false); + clearErrors(); + reset(); + }, [clearErrors, reset]); + + const handleOpenChange = useCallback( + (open: boolean) => { + if (!open) handleModalClose(); + else setIsOpen(true); + }, + [handleModalClose], + ); const deleteUser: FormEventHandler = (e) => { e.preventDefault(); destroy(route('profile.destroy'), { preserveScroll: true, - onSuccess: () => closeModal(), + onSuccess: () => handleModalClose(), onError: () => passwordInput.current?.focus(), onFinish: () => reset(), }); }; - - const closeModal = () => { - clearErrors(); - reset(); - }; - return (
@@ -39,13 +50,14 @@ export default function DeleteUser() {

Please proceed with caution, this cannot be undone.

- - - - - + + + +
Are you sure you want to delete your account? - + Once your account is deleted, all of its resources and data will also be permanently deleted. Please enter your password to confirm you would like to permanently delete your account. @@ -70,19 +82,17 @@ export default function DeleteUser() {
- - - + - + -
-
+ + ); diff --git a/resources/js/components/responsive-modal.tsx b/resources/js/components/responsive-modal.tsx new file mode 100644 index 00000000..0e496568 --- /dev/null +++ b/resources/js/components/responsive-modal.tsx @@ -0,0 +1,31 @@ +import { useIsMobile } from '@/hooks/use-mobile'; + +import { Dialog, DialogContent } from '@/components/ui/dialog'; +import { Drawer, DrawerContent } from '@/components/ui/drawer'; +import React from 'react'; + +interface ResponsiveModalProps { + children: React.ReactNode; + open: boolean; + onOpenChange: (open: boolean) => void; +} + +export const ResponsiveModal = ({ children, onOpenChange, open }: ResponsiveModalProps) => { + const isMobile = useIsMobile(); + + if (isMobile) { + return ( + + +
{children}
+
+
+ ); + } + + return ( + + {children} + + ); +}; diff --git a/resources/js/components/ui/drawer.tsx b/resources/js/components/ui/drawer.tsx new file mode 100644 index 00000000..94f4329e --- /dev/null +++ b/resources/js/components/ui/drawer.tsx @@ -0,0 +1,130 @@ +import * as React from "react" +import { Drawer as DrawerPrimitive } from "vaul" + +import { cn } from "@/lib/utils" + +function Drawer({ + ...props +}: React.ComponentProps) { + return +} + +function DrawerTrigger({ + ...props +}: React.ComponentProps) { + return +} + +function DrawerPortal({ + ...props +}: React.ComponentProps) { + return +} + +function DrawerClose({ + ...props +}: React.ComponentProps) { + return +} + +function DrawerOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DrawerContent({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + + +
+ {children} + + + ) +} + +function DrawerHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function DrawerFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function DrawerTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DrawerDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + Drawer, + DrawerPortal, + DrawerOverlay, + DrawerTrigger, + DrawerClose, + DrawerContent, + DrawerHeader, + DrawerFooter, + DrawerTitle, + DrawerDescription, +}