Просмотр исходного кода

feat(ui): add optional transition animations to dialog

David Hill 4 недель назад
Родитель
Сommit
92229b44f8

+ 1 - 1
packages/app/src/components/dialog-settings.tsx

@@ -14,7 +14,7 @@ export const DialogSettings: Component = () => {
   const platform = usePlatform()
 
   return (
-    <Dialog size="x-large">
+    <Dialog size="x-large" transition>
       <Tabs orientation="vertical" variant="settings" defaultValue="general" class="h-full settings-dialog">
         <Tabs.List>
           <div class="flex flex-col justify-between h-full w-full">

+ 10 - 14
packages/ui/src/components/dialog.css

@@ -5,12 +5,6 @@
   inset: 0;
   z-index: 50;
   background-color: hsl(from var(--background-base) h s l / 0.2);
-
-  /* animation: overlayHide 250ms ease 100ms forwards; */
-  /**/
-  /* &[data-expanded] { */
-  /*   animation: overlayShow 250ms ease; */
-  /* } */
 }
 
 [data-component="dialog"] {
@@ -58,12 +52,6 @@
       background-clip: padding-box;
       box-shadow: var(--shadow-lg-border-base);
 
-      /* animation: contentHide 300ms ease-in forwards; */
-      /**/
-      /* &[data-expanded] { */
-      /*   animation: contentShow 300ms ease-out; */
-      /* } */
-
       [data-slot="dialog-header"] {
         display: flex;
         padding: 20px;
@@ -147,6 +135,14 @@
   }
 }
 
+[data-component="dialog"][data-transition] [data-slot="dialog-content"] {
+  animation: contentHide 100ms ease-in forwards;
+
+  &[data-expanded] {
+    animation: contentShow 200ms ease-out;
+  }
+}
+
 @keyframes overlayShow {
   from {
     opacity: 0;
@@ -166,7 +162,7 @@
 @keyframes contentShow {
   from {
     opacity: 0;
-    transform: scale(0.96);
+    transform: scale(0.98);
   }
   to {
     opacity: 1;
@@ -180,6 +176,6 @@
   }
   to {
     opacity: 0;
-    transform: scale(0.96);
+    transform: scale(0.98);
   }
 }

+ 7 - 1
packages/ui/src/components/dialog.tsx

@@ -11,12 +11,18 @@ export interface DialogProps extends ParentProps {
   class?: ComponentProps<"div">["class"]
   classList?: ComponentProps<"div">["classList"]
   fit?: boolean
+  transition?: boolean
 }
 
 export function Dialog(props: DialogProps) {
   const i18n = useI18n()
   return (
-    <div data-component="dialog" data-fit={props.fit ? true : undefined} data-size={props.size || "normal"}>
+    <div
+      data-component="dialog"
+      data-fit={props.fit ? true : undefined}
+      data-size={props.size || "normal"}
+      data-transition={props.transition ? true : undefined}
+    >
       <div data-slot="dialog-container">
         <Kobalte.Content
           data-slot="dialog-content"

+ 12 - 5
packages/ui/src/context/dialog.tsx

@@ -21,6 +21,7 @@ type Active = {
   dispose: () => void
   owner: Owner
   onClose?: () => void
+  setClosing: (closing: boolean) => void
 }
 
 const Context = createContext<ReturnType<typeof init>>()
@@ -32,8 +33,11 @@ function init() {
     const current = active()
     if (!current) return
     current.onClose?.()
-    current.dispose()
-    setActive(undefined)
+    current.setClosing(true)
+    setTimeout(() => {
+      current.dispose()
+      setActive(undefined)
+    }, 100)
   }
 
   createEffect(() => {
@@ -55,14 +59,17 @@ function init() {
 
     const id = Math.random().toString(36).slice(2)
     let dispose: (() => void) | undefined
+    let setClosing: ((closing: boolean) => void) | undefined
 
     const node = runWithOwner(owner, () =>
       createRoot((d: () => void) => {
         dispose = d
+        const [closing, setClosingSignal] = createSignal(false)
+        setClosing = setClosingSignal
         return (
           <Kobalte
             modal
-            open={true}
+            open={!closing()}
             onOpenChange={(open: boolean) => {
               if (open) return
               close()
@@ -77,9 +84,9 @@ function init() {
       }),
     )
 
-    if (!dispose) return
+    if (!dispose || !setClosing) return
 
-    setActive({ id, node, dispose, owner, onClose })
+    setActive({ id, node, dispose, owner, onClose, setClosing })
   }
 
   return {