[Sheets] [Pt. 14] Portaling behaviors for nested dialogs (#5588)

Co-authored-by: Eric Bailey <git@esb.lol>
Co-authored-by: Samuel Newman <mozzius@protonmail.com>
This commit is contained in:
Hailey
2024-10-02 22:03:52 -07:00
committed by GitHub
parent 52a8e4d616
commit 058ddb0c53
5 changed files with 376 additions and 349 deletions
+4 -21
View File
@@ -16,7 +16,7 @@ import {
DialogOuterProps,
} from '#/components/Dialog/types'
import {createInput} from '#/components/forms/TextField'
import {Portal} from '#/components/Portal'
import {Portal as DefaultPortal} from '#/components/Portal'
import {
BottomSheetSnapPoint,
BottomSheetView,
@@ -34,26 +34,7 @@ export function Outer({
onClose,
nativeOptions,
testID,
}: React.PropsWithChildren<DialogOuterProps>) {
return (
<Portal>
<OuterWithoutPortal
control={control}
onClose={onClose}
nativeOptions={nativeOptions}
testID={testID}>
{children}
</OuterWithoutPortal>
</Portal>
)
}
export function OuterWithoutPortal({
children,
control,
onClose,
nativeOptions,
testID,
Portal = DefaultPortal,
}: React.PropsWithChildren<DialogOuterProps>) {
const t = useTheme()
const ref = React.useRef<BottomSheetView>(null)
@@ -119,6 +100,7 @@ export function OuterWithoutPortal({
const Wrapper = isIOS ? View : GestureHandlerRootView
return (
<Portal>
<Context.Provider value={context}>
<BottomSheetView
ref={ref}
@@ -139,6 +121,7 @@ export function OuterWithoutPortal({
</Wrapper>
</BottomSheetView>
</Context.Provider>
</Portal>
)
}
+2
View File
@@ -6,6 +6,7 @@ import type {
} from 'react-native'
import {ViewStyleProp} from '#/alf'
import {PortalComponent} from '#/components/Portal'
import {BottomSheetViewProps} from '../../../modules/bottom-sheet'
import {BottomSheetSnapPoint} from '../../../modules/bottom-sheet/src/BottomSheet.types'
@@ -58,6 +59,7 @@ export type DialogOuterProps = {
nativeOptions?: Omit<BottomSheetViewProps, 'children'>
webOptions?: {}
testID?: string
Portal?: PortalComponent
}
type DialogInnerPropsBase<T> = React.PropsWithChildren<ViewStyleProp> & T
+2
View File
@@ -12,6 +12,8 @@ type ComponentMap = {
[id: string]: Component
}
export type PortalComponent = ({children}: {children?: React.ReactNode}) => null
export function createPortalGroup() {
const Context = React.createContext<ContextType>({
outlet: null,
+8 -14
View File
@@ -3,10 +3,10 @@ import {GestureResponderEvent, View} from 'react-native'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {isNative} from '#/platform/detection'
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {Button, ButtonColor, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {PortalComponent} from '#/components/Portal'
import {Text} from '#/components/Typography'
export {
@@ -26,11 +26,11 @@ export function Outer({
children,
control,
testID,
withoutPortal,
Portal,
}: React.PropsWithChildren<{
control: Dialog.DialogControlProps
testID?: string
withoutPortal?: boolean
Portal?: PortalComponent
}>) {
const {gtMobile} = useBreakpoints()
const titleId = React.useId()
@@ -41,11 +41,8 @@ export function Outer({
[titleId, descriptionId],
)
const Wrapper =
withoutPortal && isNative ? Dialog.OuterWithoutPortal : Dialog.Outer
return (
<Wrapper control={control} testID={testID}>
<Dialog.Outer control={control} testID={testID} Portal={Portal}>
<Context.Provider value={context}>
<Dialog.ScrollableInner
accessibilityLabelledBy={titleId}
@@ -56,7 +53,7 @@ export function Outer({
{children}
</Dialog.ScrollableInner>
</Context.Provider>
</Wrapper>
</Dialog.Outer>
)
}
@@ -185,7 +182,7 @@ export function Basic({
onConfirm,
confirmButtonColor,
showCancel = true,
withoutPortal,
Portal,
}: React.PropsWithChildren<{
control: Dialog.DialogOuterProps['control']
title: string
@@ -202,13 +199,10 @@ export function Basic({
onConfirm: (e: GestureResponderEvent) => void
confirmButtonColor?: ButtonColor
showCancel?: boolean
withoutPortal?: boolean
Portal?: PortalComponent
}>) {
return (
<Outer
control={control}
testID="confirmModal"
withoutPortal={withoutPortal}>
<Outer control={control} testID="confirmModal" Portal={Portal}>
<TitleText>{title}</TitleText>
<DescriptionText>{description}</DescriptionText>
<Actions>
+57 -11
View File
@@ -31,11 +31,14 @@ import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
import {Loader} from '#/components/Loader'
import {NormalizedRNGHPressable} from '#/components/NormalizedRNGHPressable'
import {createPortalGroup} from '#/components/Portal'
import * as Prompt from '#/components/Prompt'
import {Text} from '#/components/Typography'
const ONE_DAY = 24 * 60 * 60 * 1000
const Portal = createPortalGroup()
export function MutedWordsDialog() {
const {mutedWordsDialogControl: control} = useGlobalDialogsControlContext()
return (
@@ -105,17 +108,23 @@ function MutedWordsInner() {
}, [_, field, targets, addMutedWord, setField, durations, excludeFollowing])
return (
<Portal.Provider>
<Dialog.ScrollableInner label={_(msg`Manage your muted words and tags`)}>
<View>
<Text
style={[a.text_md, a.font_bold, a.pb_sm, t.atoms.text_contrast_high]}>
style={[
a.text_md,
a.font_bold,
a.pb_sm,
t.atoms.text_contrast_high,
]}>
<Trans>Add muted words and tags</Trans>
</Text>
<Text style={[a.pb_lg, a.leading_snug, t.atoms.text_contrast_medium]}>
<Trans>
Posts can be muted based on their text, their tags, or both. We
recommend avoiding common words that appear in many posts, since it
can result in no posts being shown.
recommend avoiding common words that appear in many posts, since
it can result in no posts being shown.
</Trans>
</Text>
@@ -173,7 +182,12 @@ function MutedWordsInner() {
PressableComponent={NormalizedRNGHPressable}>
<TargetToggle>
<View
style={[a.flex_1, a.flex_row, a.align_center, a.gap_sm]}>
style={[
a.flex_1,
a.flex_row,
a.align_center,
a.gap_sm,
]}>
<Toggle.Radio />
<Toggle.LabelText style={[a.flex_1, a.leading_tight]}>
<Trans>Forever</Trans>
@@ -189,7 +203,12 @@ function MutedWordsInner() {
PressableComponent={NormalizedRNGHPressable}>
<TargetToggle>
<View
style={[a.flex_1, a.flex_row, a.align_center, a.gap_sm]}>
style={[
a.flex_1,
a.flex_row,
a.align_center,
a.gap_sm,
]}>
<Toggle.Radio />
<Toggle.LabelText style={[a.flex_1, a.leading_tight]}>
<Trans>24 hours</Trans>
@@ -214,7 +233,12 @@ function MutedWordsInner() {
PressableComponent={NormalizedRNGHPressable}>
<TargetToggle>
<View
style={[a.flex_1, a.flex_row, a.align_center, a.gap_sm]}>
style={[
a.flex_1,
a.flex_row,
a.align_center,
a.gap_sm,
]}>
<Toggle.Radio />
<Toggle.LabelText style={[a.flex_1, a.leading_tight]}>
<Trans>7 days</Trans>
@@ -230,7 +254,12 @@ function MutedWordsInner() {
PressableComponent={NormalizedRNGHPressable}>
<TargetToggle>
<View
style={[a.flex_1, a.flex_row, a.align_center, a.gap_sm]}>
style={[
a.flex_1,
a.flex_row,
a.align_center,
a.gap_sm,
]}>
<Toggle.Radio />
<Toggle.LabelText style={[a.flex_1, a.leading_tight]}>
<Trans>30 days</Trans>
@@ -243,7 +272,9 @@ function MutedWordsInner() {
</Toggle.Group>
<Toggle.Group
label={_(msg`Select what content this mute word should apply to.`)}
label={_(
msg`Select what content this mute word should apply to.`,
)}
type="radio"
values={targets}
onChange={setTargets}>
@@ -312,7 +343,8 @@ function MutedWordsInner() {
onChange={setExcludeFollowing}
PressableComponent={NormalizedRNGHPressable}>
<TargetToggle>
<View style={[a.flex_1, a.flex_row, a.align_center, a.gap_sm]}>
<View
style={[a.flex_1, a.flex_row, a.align_center, a.gap_sm]}>
<Toggle.Checkbox />
<Toggle.LabelText style={[a.flex_1, a.leading_tight]}>
<Trans>Exclude users you follow</Trans>
@@ -380,7 +412,12 @@ function MutedWordsInner() {
<Loader />
) : preferencesError || !preferences ? (
<View
style={[a.py_md, a.px_lg, a.rounded_md, t.atoms.bg_contrast_25]}>
style={[
a.py_md,
a.px_lg,
a.rounded_md,
t.atoms.bg_contrast_25,
]}>
<Text style={[a.italic, t.atoms.text_contrast_high]}>
<Trans>
We're sorry, but we weren't able to load your muted words at
@@ -400,7 +437,12 @@ function MutedWordsInner() {
))
) : (
<View
style={[a.py_md, a.px_lg, a.rounded_md, t.atoms.bg_contrast_25]}>
style={[
a.py_md,
a.px_lg,
a.rounded_md,
t.atoms.bg_contrast_25,
]}>
<Text style={[a.italic, t.atoms.text_contrast_high]}>
<Trans>You haven't muted any words or tags yet</Trans>
</Text>
@@ -413,6 +455,9 @@ function MutedWordsInner() {
<Dialog.Close />
</Dialog.ScrollableInner>
<Portal.Outlet />
</Portal.Provider>
)
}
@@ -444,6 +489,7 @@ function MutedWordRow({
onConfirm={remove}
confirmButtonCta={_(msg`Remove`)}
confirmButtonColor="negative"
Portal={Portal.Portal}
/>
<View