From b52a742925cff4429885e94815d61f3f7cfb5a66 Mon Sep 17 00:00:00 2001
From: Eric Bailey <git@esb.lol>
Date: Mon, 19 Feb 2024 18:18:13 -0600
Subject: Improve dialogs (#2933)

* Improve dialogs

* Remove comment, revert storybook

* Hacky fix

* Comments
---
 src/components/Dialog/index.tsx | 121 +++++++++++++++++++++++-----------------
 1 file changed, 70 insertions(+), 51 deletions(-)

(limited to 'src/components/Dialog/index.tsx')

diff --git a/src/components/Dialog/index.tsx b/src/components/Dialog/index.tsx
index 9132e68de..f30d24e6a 100644
--- a/src/components/Dialog/index.tsx
+++ b/src/components/Dialog/index.tsx
@@ -8,7 +8,7 @@ import BottomSheet, {
 } from '@gorhom/bottom-sheet'
 import {useSafeAreaInsets} from 'react-native-safe-area-context'
 
-import {useTheme, atoms as a} from '#/alf'
+import {useTheme, atoms as a, flatten} from '#/alf'
 import {Portal} from '#/components/Portal'
 import {createInput} from '#/components/forms/TextField'
 
@@ -36,9 +36,23 @@ export function Outer({
   const hasSnapPoints = !!sheetOptions.snapPoints
   const insets = useSafeAreaInsets()
 
-  const open = React.useCallback<DialogControlProps['open']>((i = 0) => {
-    sheet.current?.snapToIndex(i)
-  }, [])
+  /*
+   * Used to manage open/closed, but index is otherwise handled internally by `BottomSheet`
+   */
+  const [openIndex, setOpenIndex] = React.useState(-1)
+
+  /*
+   * `openIndex` is the index of the snap point to open the bottom sheet to. If >0, the bottom sheet is open.
+   */
+  const isOpen = openIndex > -1
+
+  const open = React.useCallback<DialogControlProps['open']>(
+    ({index} = {}) => {
+      // can be set to any index of `snapPoints`, but `0` is the first i.e. "open"
+      setOpenIndex(index || 0)
+    },
+    [setOpenIndex],
+  )
 
   const close = React.useCallback(() => {
     sheet.current?.close()
@@ -57,77 +71,80 @@ export function Outer({
     (index: number) => {
       if (index === -1) {
         onClose?.()
+        setOpenIndex(-1)
       }
     },
-    [onClose],
+    [onClose, setOpenIndex],
   )
 
   const context = React.useMemo(() => ({close}), [close])
 
   return (
-    <Portal>
-      <BottomSheet
-        enableDynamicSizing={!hasSnapPoints}
-        enablePanDownToClose
-        keyboardBehavior="interactive"
-        android_keyboardInputMode="adjustResize"
-        keyboardBlurBehavior="restore"
-        topInset={insets.top}
-        {...sheetOptions}
-        ref={sheet}
-        index={-1}
-        backgroundStyle={{backgroundColor: 'transparent'}}
-        backdropComponent={props => (
-          <BottomSheetBackdrop
-            opacity={0.4}
-            appearsOnIndex={0}
-            disappearsOnIndex={-1}
-            {...props}
-          />
-        )}
-        handleIndicatorStyle={{backgroundColor: t.palette.primary_500}}
-        handleStyle={{display: 'none'}}
-        onChange={onChange}>
-        <Context.Provider value={context}>
-          <View
-            style={[
-              a.absolute,
-              a.inset_0,
-              t.atoms.bg,
-              {
-                borderTopLeftRadius: 40,
-                borderTopRightRadius: 40,
-                height: Dimensions.get('window').height * 2,
-              },
-            ]}
-          />
-          {children}
-        </Context.Provider>
-      </BottomSheet>
-    </Portal>
+    isOpen && (
+      <Portal>
+        <BottomSheet
+          enableDynamicSizing={!hasSnapPoints}
+          enablePanDownToClose
+          keyboardBehavior="interactive"
+          android_keyboardInputMode="adjustResize"
+          keyboardBlurBehavior="restore"
+          topInset={insets.top}
+          {...sheetOptions}
+          ref={sheet}
+          index={openIndex}
+          backgroundStyle={{backgroundColor: 'transparent'}}
+          backdropComponent={props => (
+            <BottomSheetBackdrop
+              opacity={0.4}
+              appearsOnIndex={0}
+              disappearsOnIndex={-1}
+              {...props}
+            />
+          )}
+          handleIndicatorStyle={{backgroundColor: t.palette.primary_500}}
+          handleStyle={{display: 'none'}}
+          onChange={onChange}>
+          <Context.Provider value={context}>
+            <View
+              style={[
+                a.absolute,
+                a.inset_0,
+                t.atoms.bg,
+                {
+                  borderTopLeftRadius: 40,
+                  borderTopRightRadius: 40,
+                  height: Dimensions.get('window').height * 2,
+                },
+              ]}
+            />
+            {hasSnapPoints ? children : <View>{children}</View>}
+          </Context.Provider>
+        </BottomSheet>
+      </Portal>
+    )
   )
 }
 
-// TODO a11y props here, or is that handled by the sheet?
-export function Inner(props: DialogInnerProps) {
+export function Inner({children, style}: DialogInnerProps) {
   const insets = useSafeAreaInsets()
   return (
     <BottomSheetView
       style={[
-        a.p_lg,
+        a.p_xl,
         {
           paddingTop: 40,
           borderTopLeftRadius: 40,
           borderTopRightRadius: 40,
           paddingBottom: insets.bottom + a.pb_5xl.paddingBottom,
         },
+        flatten(style),
       ]}>
-      {props.children}
+      {children}
     </BottomSheetView>
   )
 }
 
-export function ScrollableInner(props: DialogInnerProps) {
+export function ScrollableInner({children, style}: DialogInnerProps) {
   const insets = useSafeAreaInsets()
   return (
     <BottomSheetScrollView
@@ -136,13 +153,15 @@ export function ScrollableInner(props: DialogInnerProps) {
       style={[
         a.flex_1, // main diff is this
         a.p_xl,
+        a.h_full,
         {
           paddingTop: 40,
           borderTopLeftRadius: 40,
           borderTopRightRadius: 40,
         },
+        flatten(style),
       ]}>
-      {props.children}
+      {children}
       <View style={{height: insets.bottom + a.pt_5xl.paddingTop}} />
     </BottomSheetScrollView>
   )
-- 
cgit 1.4.1


From 8a169dc6a14d422f65ea4afb5af7fe30efacf2bb Mon Sep 17 00:00:00 2001
From: Eric Bailey <git@esb.lol>
Date: Tue, 20 Feb 2024 10:04:07 -0600
Subject: Improve dialogs issue (#2941)

* Fix collapse, update backdrop color

* Remove test prop

* Remove debug code
---
 src/components/Dialog/index.tsx        |  4 +++-
 src/view/screens/Storybook/Dialogs.tsx | 43 ++++++++++++++++++++++++++++++----
 2 files changed, 41 insertions(+), 6 deletions(-)

(limited to 'src/components/Dialog/index.tsx')

diff --git a/src/components/Dialog/index.tsx b/src/components/Dialog/index.tsx
index f30d24e6a..529535051 100644
--- a/src/components/Dialog/index.tsx
+++ b/src/components/Dialog/index.tsx
@@ -90,6 +90,7 @@ export function Outer({
           keyboardBlurBehavior="restore"
           topInset={insets.top}
           {...sheetOptions}
+          snapPoints={sheetOptions.snapPoints || ['100%']}
           ref={sheet}
           index={openIndex}
           backgroundStyle={{backgroundColor: 'transparent'}}
@@ -99,6 +100,7 @@ export function Outer({
               appearsOnIndex={0}
               disappearsOnIndex={-1}
               {...props}
+              style={[flatten(props.style), t.atoms.bg_contrast_300]}
             />
           )}
           handleIndicatorStyle={{backgroundColor: t.palette.primary_500}}
@@ -117,7 +119,7 @@ export function Outer({
                 },
               ]}
             />
-            {hasSnapPoints ? children : <View>{children}</View>}
+            {children}
           </Context.Provider>
         </BottomSheet>
       </Portal>
diff --git a/src/view/screens/Storybook/Dialogs.tsx b/src/view/screens/Storybook/Dialogs.tsx
index a18b3c62f..821ac3c88 100644
--- a/src/view/screens/Storybook/Dialogs.tsx
+++ b/src/view/screens/Storybook/Dialogs.tsx
@@ -9,7 +9,8 @@ import * as Prompt from '#/components/Prompt'
 import {useDialogStateControlContext} from '#/state/dialogs'
 
 export function Dialogs() {
-  const control = Dialog.useDialogControl()
+  const scrollable = Dialog.useDialogControl()
+  const basic = Dialog.useDialogControl()
   const prompt = Prompt.usePromptControl()
   const {closeAllDialogs} = useDialogStateControlContext()
 
@@ -20,8 +21,31 @@ export function Dialogs() {
         color="secondary"
         size="small"
         onPress={() => {
-          control.open()
+          scrollable.open()
           prompt.open()
+          basic.open()
+        }}
+        label="Open basic dialog">
+        Open all dialogs
+      </Button>
+
+      <Button
+        variant="outline"
+        color="secondary"
+        size="small"
+        onPress={() => {
+          scrollable.open()
+        }}
+        label="Open basic dialog">
+        Open scrollable dialog
+      </Button>
+
+      <Button
+        variant="outline"
+        color="secondary"
+        size="small"
+        onPress={() => {
+          basic.open()
         }}
         label="Open basic dialog">
         Open basic dialog
@@ -48,8 +72,17 @@ export function Dialogs() {
         </Prompt.Actions>
       </Prompt.Outer>
 
+      <Dialog.Outer control={basic}>
+        <Dialog.Handle />
+
+        <Dialog.Inner label="test">
+          <H3 nativeID="dialog-title">Dialog</H3>
+          <P nativeID="dialog-description">A basic dialog</P>
+        </Dialog.Inner>
+      </Dialog.Outer>
+
       <Dialog.Outer
-        control={control}
+        control={scrollable}
         nativeOptions={{sheet: {snapPoints: ['100%']}}}>
         <Dialog.Handle />
 
@@ -77,9 +110,9 @@ export function Dialogs() {
                 variant="outline"
                 color="primary"
                 size="small"
-                onPress={() => control.close()}
+                onPress={() => scrollable.close()}
                 label="Open basic dialog">
-                Close basic dialog
+                Close dialog
               </Button>
             </View>
           </View>
-- 
cgit 1.4.1


From f88b16525498584f81ea7f594a63623fc5dc7ce9 Mon Sep 17 00:00:00 2001
From: Eric Bailey <git@esb.lol>
Date: Tue, 20 Feb 2024 18:20:59 -0600
Subject: Add optional close callback to Dialog (#2947)

* Add optional close callback

* No emitter
---
 src/components/Dialog/context.ts       | 10 +++++++---
 src/components/Dialog/index.tsx        |  8 +++++++-
 src/components/Dialog/types.ts         |  6 ++----
 src/view/screens/Storybook/Dialogs.tsx |  6 +++++-
 4 files changed, 21 insertions(+), 9 deletions(-)

(limited to 'src/components/Dialog/index.tsx')

diff --git a/src/components/Dialog/context.ts b/src/components/Dialog/context.ts
index b28b9f5a2..f0c7c983a 100644
--- a/src/components/Dialog/context.ts
+++ b/src/components/Dialog/context.ts
@@ -1,7 +1,11 @@
 import React from 'react'
 
 import {useDialogStateContext} from '#/state/dialogs'
-import {DialogContextProps, DialogControlProps} from '#/components/Dialog/types'
+import {
+  DialogContextProps,
+  DialogControlProps,
+  DialogOuterProps,
+} from '#/components/Dialog/types'
 
 export const Context = React.createContext<DialogContextProps>({
   close: () => {},
@@ -11,7 +15,7 @@ export function useDialogContext() {
   return React.useContext(Context)
 }
 
-export function useDialogControl() {
+export function useDialogControl(): DialogOuterProps['control'] {
   const id = React.useId()
   const control = React.useRef<DialogControlProps>({
     open: () => {},
@@ -30,6 +34,6 @@ export function useDialogControl() {
   return {
     ref: control,
     open: () => control.current.open(),
-    close: () => control.current.close(),
+    close: cb => control.current.close(cb),
   }
 }
diff --git a/src/components/Dialog/index.tsx b/src/components/Dialog/index.tsx
index 529535051..27f43afd3 100644
--- a/src/components/Dialog/index.tsx
+++ b/src/components/Dialog/index.tsx
@@ -35,6 +35,7 @@ export function Outer({
   const sheetOptions = nativeOptions?.sheet || {}
   const hasSnapPoints = !!sheetOptions.snapPoints
   const insets = useSafeAreaInsets()
+  const closeCallback = React.useRef<() => void>()
 
   /*
    * Used to manage open/closed, but index is otherwise handled internally by `BottomSheet`
@@ -54,7 +55,10 @@ export function Outer({
     [setOpenIndex],
   )
 
-  const close = React.useCallback(() => {
+  const close = React.useCallback<DialogControlProps['close']>(cb => {
+    if (cb) {
+      closeCallback.current = cb
+    }
     sheet.current?.close()
   }, [])
 
@@ -70,6 +74,8 @@ export function Outer({
   const onChange = React.useCallback(
     (index: number) => {
       if (index === -1) {
+        closeCallback.current?.()
+        closeCallback.current = undefined
         onClose?.()
         setOpenIndex(-1)
       }
diff --git a/src/components/Dialog/types.ts b/src/components/Dialog/types.ts
index 00178926a..75ba825ac 100644
--- a/src/components/Dialog/types.ts
+++ b/src/components/Dialog/types.ts
@@ -22,15 +22,13 @@ export type DialogControlOpenOptions = {
 
 export type DialogControlProps = {
   open: (options?: DialogControlOpenOptions) => void
-  close: () => void
+  close: (callback?: () => void) => void
 }
 
 export type DialogOuterProps = {
   control: {
     ref: React.RefObject<DialogControlProps>
-    open: (index?: number) => void
-    close: () => void
-  }
+  } & DialogControlProps
   onClose?: () => void
   nativeOptions?: {
     sheet?: Omit<BottomSheetProps, 'children'>
diff --git a/src/view/screens/Storybook/Dialogs.tsx b/src/view/screens/Storybook/Dialogs.tsx
index 821ac3c88..09be124db 100644
--- a/src/view/screens/Storybook/Dialogs.tsx
+++ b/src/view/screens/Storybook/Dialogs.tsx
@@ -110,7 +110,11 @@ export function Dialogs() {
                 variant="outline"
                 color="primary"
                 size="small"
-                onPress={() => scrollable.close()}
+                onPress={() =>
+                  scrollable.close(() => {
+                    console.log('CLOSED')
+                  })
+                }
                 label="Open basic dialog">
                 Close dialog
               </Button>
-- 
cgit 1.4.1

