feat(mobile): disable swipe back gesture when there is no back in header (#8876)

close AF-1663, AF-1756

- new global `ModalConfigContext`
- new logic to judge whether inside modal
- render `✕` for PageHeader back if inside modal
- only enable `NavigationGesture` when there is `<` in PageHeader
This commit is contained in:
CatsJuice
2024-11-25 03:12:21 +00:00
parent 922db5ced4
commit b369ee0cca
23 changed files with 260 additions and 26 deletions

View File

@@ -0,0 +1,11 @@
import { createContext } from 'react';
export interface ModalConfig {
/**
* add global callback for modal open/close
*/
onOpenChange?: (open: boolean) => void;
}
export const ModalConfigContext = createContext<ModalConfig>({});
export const InsideModalContext = createContext<number>(0);

View File

@@ -1,4 +1,5 @@
export * from './confirm-modal';
export * from './context';
export * from './modal';
export * from './overlay-modal';
export * from './prompt-modal';

View File

@@ -10,12 +10,19 @@ import * as VisuallyHidden from '@radix-ui/react-visually-hidden';
import { assignInlineVars } from '@vanilla-extract/dynamic';
import clsx from 'clsx';
import type { CSSProperties } from 'react';
import { forwardRef, useCallback, useEffect, useState } from 'react';
import {
forwardRef,
useCallback,
useContext,
useEffect,
useState,
} from 'react';
import { startScopedViewTransition } from '../../utils';
import type { IconButtonProps } from '../button';
import { IconButton } from '../button';
import { SafeArea } from '../safe-area';
import { InsideModalContext, ModalConfigContext } from './context';
import * as styles from './styles.css';
export interface ModalProps extends DialogProps {
@@ -123,6 +130,7 @@ function createContainer() {
export const ModalInner = forwardRef<HTMLDivElement, ModalProps>(
(props, ref) => {
const modalConfig = useContext(ModalConfigContext);
const {
modal,
portalOptions,
@@ -164,6 +172,10 @@ export const ModalInner = forwardRef<HTMLDivElement, ModalProps>(
null
);
useEffect(() => {
modalConfig.onOpenChange?.(open ?? false);
}, [modalConfig, open]);
useEffect(() => {
if (open) {
const container = createContainer();
@@ -304,10 +316,20 @@ export const ModalInner = forwardRef<HTMLDivElement, ModalProps>(
ModalInner.displayName = 'ModalInner';
export const Modal = forwardRef<HTMLDivElement, ModalProps>((props, ref) => {
const insideModal = useContext(InsideModalContext);
if (!props.open) {
return;
}
return <ModalInner {...props} ref={ref} />;
return (
<InsideModalContext.Provider value={insideModal + 1}>
<ModalInner {...props} ref={ref} />
</InsideModalContext.Provider>
);
});
Modal.displayName = 'Modal';
export const useIsInsideModal = () => {
const context = useContext(InsideModalContext);
return context > 0;
};