diff --git a/apps/web/vibes/soul/examples/sections/forgot-password/index.tsx b/apps/web/vibes/soul/examples/sections/forgot-password/index.tsx
index e5de9fc37..bd18f2dd5 100644
--- a/apps/web/vibes/soul/examples/sections/forgot-password/index.tsx
+++ b/apps/web/vibes/soul/examples/sections/forgot-password/index.tsx
@@ -1,8 +1,9 @@
-import { ForgotPassword } from '@/vibes/soul/sections/forgot-password';
-import { schema } from '@/vibes/soul/sections/forgot-password/schema';
import { SubmissionResult } from '@conform-to/react';
import { parseWithZod } from '@conform-to/zod';
+import { ForgotPassword } from '@/vibes/soul/sections/forgot-password';
+import { schema } from '@/vibes/soul/sections/forgot-password/schema';
+
export default function Preview() {
return
;
}
diff --git a/apps/web/vibes/soul/examples/sections/reset-password/index.tsx b/apps/web/vibes/soul/examples/sections/reset-password/index.tsx
index 7b5938fa7..ba23e8a7e 100644
--- a/apps/web/vibes/soul/examples/sections/reset-password/index.tsx
+++ b/apps/web/vibes/soul/examples/sections/reset-password/index.tsx
@@ -1,7 +1,7 @@
-import { ResetPassword } from '@/vibes/soul/sections/reset-password';
-
import { SubmissionResult } from '@conform-to/react';
import { parseWithZod } from '@conform-to/zod';
+
+import { ResetPassword } from '@/vibes/soul/sections/reset-password';
import { schema } from '@/vibes/soul/sections/reset-password/schema';
export default function Preview() {
diff --git a/apps/web/vibes/soul/examples/sections/sign-in/index.tsx b/apps/web/vibes/soul/examples/sections/sign-in/index.tsx
index fc67effa2..afa78b12c 100644
--- a/apps/web/vibes/soul/examples/sections/sign-in/index.tsx
+++ b/apps/web/vibes/soul/examples/sections/sign-in/index.tsx
@@ -1,8 +1,9 @@
-import { SignIn } from '@/vibes/soul/sections/sign-in';
-import { schema } from '@/vibes/soul/sections/sign-in/schema';
import { SubmissionResult } from '@conform-to/react';
import { parseWithZod } from '@conform-to/zod';
+import { SignIn } from '@/vibes/soul/sections/sign-in';
+import { schema } from '@/vibes/soul/sections/sign-in/schema';
+
export default function Preview() {
return
;
}
diff --git a/apps/web/vibes/soul/primitives/modal/index.tsx b/apps/web/vibes/soul/primitives/modal/index.tsx
index 5985798ae..8f97bd793 100644
--- a/apps/web/vibes/soul/primitives/modal/index.tsx
+++ b/apps/web/vibes/soul/primitives/modal/index.tsx
@@ -1,13 +1,21 @@
import * as Dialog from '@radix-ui/react-dialog';
import { clsx } from 'clsx';
-import { ReactNode } from 'react';
+import { XIcon } from 'lucide-react';
-export interface ModalProps {
- isOpen: boolean;
- setOpen: (open: boolean) => void;
+import { Button } from '@/vibes/soul/primitives/button';
+
+export interface ModalProps extends React.PropsWithChildren {
+ className?: string;
+ isOpen?: boolean;
+ setOpen?: (open: boolean) => void;
+ /** Title should always be given for screen reader support. */
title: string;
- trigger: ReactNode;
- children: ReactNode;
+ /** Element to trigger the modal. Not required if the modal is being controlled manually. */
+ trigger?: React.ReactNode;
+ /** If `true`, a user will be required to make a choice by clicking on one of the provided actions. Defaults to `false`. */
+ required?: boolean;
+ /** Hides the header / top of the modal. */
+ hideHeader?: boolean;
}
/**
@@ -21,23 +29,55 @@ export interface ModalProps {
* }
* ```
*/
-export const Modal = function Modal({ isOpen, setOpen, title, trigger, children }: ModalProps) {
+export const Modal = ({
+ className = '',
+ isOpen,
+ setOpen,
+ title,
+ trigger,
+ children,
+ required = false,
+ hideHeader = false,
+}: ModalProps) => {
return (
- {trigger}
+ {trigger != null && {trigger}}
e.preventDefault()}
+ onEscapeKeyDown={required ? (event) => event.preventDefault() : undefined}
+ onInteractOutside={required ? (event) => event.preventDefault() : undefined}
+ onPointerDownOutside={required ? (event) => event.preventDefault() : undefined}
>
- {title}
- {children}
+
+
+
+ {title}
+
+ {!(required || hideHeader) && (
+
+
+
+
+
+ )}
+
+
{children}
+
diff --git a/apps/web/vibes/soul/sections/address-list-section/index.tsx b/apps/web/vibes/soul/sections/address-list-section/index.tsx
index ebec6e7cd..b61dc139a 100644
--- a/apps/web/vibes/soul/sections/address-list-section/index.tsx
+++ b/apps/web/vibes/soul/sections/address-list-section/index.tsx
@@ -14,10 +14,10 @@ import {
import { useFormStatus } from 'react-dom';
import { z } from 'zod';
-import { Badge } from '@/vibes/soul/primitives/badge';
-import { Button } from '@/vibes/soul/primitives/button';
import { DynamicForm } from '@/vibes/soul/form/dynamic-form';
import { Field, FieldGroup } from '@/vibes/soul/form/dynamic-form/schema';
+import { Badge } from '@/vibes/soul/primitives/badge';
+import { Button } from '@/vibes/soul/primitives/button';
import { Spinner } from '@/vibes/soul/primitives/spinner';
import { toast } from '@/vibes/soul/primitives/toaster';
diff --git a/apps/web/vibes/soul/sections/reviews/index.tsx b/apps/web/vibes/soul/sections/reviews/index.tsx
index c53bb6285..8a4cbf13f 100644
--- a/apps/web/vibes/soul/sections/reviews/index.tsx
+++ b/apps/web/vibes/soul/sections/reviews/index.tsx
@@ -1,8 +1,8 @@
import { Stream, Streamable } from '@/vibes/soul/lib/streamable';
import { CursorPagination, CursorPaginationInfo } from '@/vibes/soul/primitives/cursor-pagination';
import { Rating } from '@/vibes/soul/primitives/rating';
-import { StickySidebarLayout } from '@/vibes/soul/sections/sticky-sidebar-layout';
import * as Skeleton from '@/vibes/soul/primitives/skeleton';
+import { StickySidebarLayout } from '@/vibes/soul/sections/sticky-sidebar-layout';
interface Review {
id: string;
@@ -209,9 +209,9 @@ function TotalCountSkeleton() {
return (
);
@@ -235,7 +235,7 @@ function AverageRatingSkeleton() {
function RatingSkeleton() {
return (
-
+
);
}