Files
NapCatQQ/.pnpm-store/v3/files/00/db5838c83122dcdbaacc5750eded673feb657dd7d3180aeadd10417103bb9bcb5d5ea4264cbb7fcf8d9be45462171381e930f578784fd457e61bde740d9428
T
TanTan e311ca3275
Build NapCat Artifacts / Type Check & Test (push) Waiting to run
docs: 仓库链接指向本镜像仓库
2026-11-13 09:25:58 +08:00

91 lines
3.1 KiB
Plaintext

/*
* Copyright 2022 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
import {ClearPressResponder} from '@react-aria/interactions';
import {FocusScope} from '@react-aria/focus';
import React, {ReactNode, useContext, useMemo, useState} from 'react';
import ReactDOM from 'react-dom';
import {useIsSSR} from '@react-aria/ssr';
import {useLayoutEffect} from '@react-aria/utils';
import {useUNSTABLE_PortalContext} from './PortalProvider';
export interface OverlayProps {
/**
* The container element in which the overlay portal will be placed.
* @default document.body
*/
portalContainer?: Element,
/** The overlay to render in the portal. */
children: ReactNode,
/**
* Disables default focus management for the overlay, including containment and restoration.
* This option should be used very carefully. When focus management is disabled, you must
* implement focus containment and restoration to ensure the overlay is keyboard accessible.
*/
disableFocusManagement?: boolean,
/**
* Whether the overlay is currently performing an exit animation. When true,
* focus is allowed to move outside.
*/
isExiting?: boolean
}
export const OverlayContext = React.createContext<{contain: boolean, setContain: React.Dispatch<React.SetStateAction<boolean>>} | null>(null);
/**
* A container which renders an overlay such as a popover or modal in a portal,
* and provides a focus scope for the child elements.
*/
export function Overlay(props: OverlayProps) {
let isSSR = useIsSSR();
let {portalContainer = isSSR ? null : document.body, isExiting} = props;
let [contain, setContain] = useState(false);
let contextValue = useMemo(() => ({contain, setContain}), [contain, setContain]);
let {getContainer} = useUNSTABLE_PortalContext();
if (!props.portalContainer && getContainer) {
portalContainer = getContainer();
}
if (!portalContainer) {
return null;
}
let contents = props.children;
if (!props.disableFocusManagement) {
contents = (
<FocusScope restoreFocus contain={contain && !isExiting}>
{contents}
</FocusScope>
);
}
contents = (
<OverlayContext.Provider value={contextValue}>
<ClearPressResponder>
{contents}
</ClearPressResponder>
</OverlayContext.Provider>
);
return ReactDOM.createPortal(contents, portalContainer);
}
/** @private */
export function useOverlayFocusContain() {
let ctx = useContext(OverlayContext);
let setContain = ctx?.setContain;
useLayoutEffect(() => {
setContain?.(true);
}, [setContain]);
}