Pārlūkot izejas kodu

feat: create new dialog component using radix-ui primitives

Nicolas Meienberger 2 gadi atpakaļ
vecāks
revīzija
67b9c43ae1

+ 36 - 0
src/client/components/ui/Dialog/Dialog.module.scss

@@ -0,0 +1,36 @@
+@keyframes zoomIn {
+  0% {
+    transform: scale(0.8);
+  }
+  50% {
+    transform: scale(1.05);
+  }
+  100% {
+    transform: scale(1);
+  }
+}
+
+@keyframes dimmedBackground {
+  from {
+    background-color: rgba(0, 0, 0, 0);
+  }
+  to {
+    background-color: rgba(0, 0, 0, 0.5);
+  }
+}
+
+.dimmedBackground {
+  animation-name: dimmedBackground;
+  animation-duration: 0.2s;
+  animation-iteration-count: 1;
+  animation-timing-function: ease-in-out;
+  animation-fill-mode: forwards;
+}
+
+.zoomIn {
+  animation-name: zoomIn;
+  animation-duration: 0.25s;
+  animation-iteration-count: 1;
+  animation-timing-function: spring;
+  animation-fill-mode: forwards;
+}

+ 68 - 0
src/client/components/ui/Dialog/Dialog.tsx

@@ -0,0 +1,68 @@
+'use client';
+
+import * as React from 'react';
+import * as DialogPrimitive from '@radix-ui/react-dialog';
+import clsx from 'clsx';
+import styles from './Dialog.module.scss';
+
+type Sizes = 'sm' | 'md' | 'lg' | 'xl';
+type ModalType = 'default' | 'primary' | 'success' | 'info' | 'warning' | 'danger';
+
+type ModalProps = {
+  size?: Sizes;
+  type?: ModalType;
+};
+
+const Dialog = DialogPrimitive.Root;
+const DialogTrigger = DialogPrimitive.Trigger;
+
+const DialogPortal = ({ className, children, ...props }: DialogPrimitive.DialogPortalProps & ModalProps) => (
+  <DialogPrimitive.Portal className={clsx(className)} {...props}>
+    <div className={clsx('modal modal-sm d-block', styles.dimmedBackground)}>
+      <div className={clsx(`modal-dialog modal-dialog-centered modal-${props.size || 'lg'}`, styles.zoomIn)}>
+        <div className="shadow modal-content">
+          <DialogPrimitive.Close className="btn-close mt-1">
+            <button data-testid="modal-close-button" type="button" className="btn-close" aria-label="Close" />
+          </DialogPrimitive.Close>
+          <div data-testid="modal-status" className={clsx('modal-status', { [`bg-${props.type}`]: Boolean(props.type), 'd-none': !props.type })} />
+          {children}
+        </div>
+      </div>
+    </div>
+  </DialogPrimitive.Portal>
+);
+DialogPortal.displayName = DialogPrimitive.Portal.displayName;
+
+const DialogOverlay = React.forwardRef<React.ElementRef<typeof DialogPrimitive.Overlay>, React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>>(({ className, children, ...props }, ref) => (
+  <DialogPrimitive.Overlay className={clsx('', className)} {...props} ref={ref} />
+));
+DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
+
+const DialogContent = React.forwardRef<React.ElementRef<typeof DialogPrimitive.Content>, React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & ModalProps>(
+  ({ className, children, ...props }, ref) => (
+    <DialogPortal type={props.type} size={props.size}>
+      <DialogPrimitive.Content ref={ref} className={clsx('modal-content mt-1', className)} {...props}>
+        {children}
+      </DialogPrimitive.Content>
+    </DialogPortal>
+  ),
+);
+DialogContent.displayName = DialogPrimitive.Content.displayName;
+
+const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => <div data-testid="modal-header" className={clsx('modal-header', className)} {...props} />;
+DialogHeader.displayName = 'DialogHeader';
+
+const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => <div className={clsx('modal-footer', className)} {...props} />;
+DialogFooter.displayName = 'DialogFooter';
+
+const DialogTitle = React.forwardRef<React.ElementRef<typeof DialogPrimitive.Title>, React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>>(({ className, ...props }, ref) => (
+  <DialogPrimitive.Title ref={ref} className={clsx('modal-title', className)} {...props} />
+));
+DialogTitle.displayName = DialogPrimitive.Title.displayName;
+
+const DialogDescription = React.forwardRef<React.ElementRef<typeof DialogPrimitive.Description>, React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>>(({ className, ...props }, ref) => (
+  <DialogPrimitive.Description ref={ref} className={clsx('modal-body', className)} {...props} />
+));
+DialogDescription.displayName = DialogPrimitive.Description.displayName;
+
+export { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription };

+ 1 - 0
src/client/components/ui/Dialog/index.ts

@@ -0,0 +1 @@
+export { Dialog, DialogTitle, DialogFooter, DialogHeader, DialogContent, DialogTrigger, DialogDescription } from './Dialog';