@gluestack-ui-nightly/core
Version:
Universal UI components for React Native, Expo, and Next.js
164 lines • 7.5 kB
JSX
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
import React, { forwardRef, useState } from 'react';
import { useKeyboardDismissable } from '@gluestack-ui-nightly/utils/aria';
import { usePopover } from './PopoverContext';
import { Platform, AccessibilityInfo, Keyboard, View } from 'react-native';
import { findNodeHandle, mergeRefs } from '@gluestack-ui-nightly/utils/common';
import { useOverlayPosition } from '../../overlay/aria';
import { OverlayAnimatePresence } from './OverlayAnimatePresence';
import { FocusScope as FocusScopeAria } from '@gluestack-ui-nightly/utils/aria';
import { useDialog } from '@gluestack-ui-nightly/utils/aria';
import { PopoverContentProvider } from './PopoverContext';
import { getContainerStyle } from './utils';
const DEFAULT_ARROW_HEIGHT = 14, DEFAULT_ARROW_WIDTH = 14;
const PopoverContent = (StyledPopoverContent, AnimatePresence) => forwardRef((_a, ref) => {
var { children, style } = _a, props = __rest(_a, ["children", "style"]);
const { value } = usePopover('PopoverContext');
const [arrowHeight, setArrowHeight] = useState(DEFAULT_ARROW_HEIGHT);
const [arrowWidth, setArrowWidth] = useState(DEFAULT_ARROW_WIDTH);
const [arrowElement, setArrowElement] = useState(null);
const { targetRef, initialFocusRef, finalFocusRef, popoverContentId, headerMounted, bodyMounted, bodyId, headerId, isOpen, placement, shouldOverlapWithTrigger, crossOffset, offset, trapFocus, handleClose, shouldFlip, focusScope, } = value;
const contentRef = React.useRef(null);
React.useEffect(() => {
if (contentRef) {
const reactTag = findNodeHandle(contentRef.current);
if (reactTag) {
AccessibilityInfo.setAccessibilityFocus(reactTag);
}
}
}, [isOpen, contentRef]);
const { dialogProps } = useDialog(Object.assign({ initialFocusRef }, props), contentRef);
React.useEffect(() => {
var _a, _b, _c, _d;
if (isOpen) {
if (focusScope) {
Keyboard.dismiss();
}
if (initialFocusRef &&
(initialFocusRef === null || initialFocusRef === void 0 ? void 0 : initialFocusRef.current) &&
((_a = initialFocusRef === null || initialFocusRef === void 0 ? void 0 : initialFocusRef.current) === null || _a === void 0 ? void 0 : _a.focus)) {
(_b = initialFocusRef === null || initialFocusRef === void 0 ? void 0 : initialFocusRef.current) === null || _b === void 0 ? void 0 : _b.focus();
}
}
else {
if (finalFocusRef &&
(finalFocusRef === null || finalFocusRef === void 0 ? void 0 : finalFocusRef.current) &&
((_c = finalFocusRef === null || finalFocusRef === void 0 ? void 0 : finalFocusRef.current) === null || _c === void 0 ? void 0 : _c.focus)) {
(_d = finalFocusRef === null || finalFocusRef === void 0 ? void 0 : finalFocusRef.current) === null || _d === void 0 ? void 0 : _d.focus();
}
}
}, [initialFocusRef, finalFocusRef, isOpen, focusScope]);
useKeyboardDismissable({
enabled: true,
callback: handleClose,
});
const accessibilityProps = Platform.OS === 'web'
? {
'role': 'dialog',
'aria-labelledby': headerMounted ? headerId : undefined,
'aria-describedby': bodyMounted ? bodyId : undefined,
}
: {};
const overlayRef = React.useRef(null);
const { overlayProps, arrowProps, placement: calculatedPlacement, isFlipped, } = useOverlayPosition({
placement: placement,
targetRef,
overlayRef,
crossOffset,
offset,
shouldOverlapWithTrigger,
shouldFlip,
});
if (Object.keys(overlayProps.style).length === 0) {
overlayProps.style = {
top: -1000,
left: -1000,
};
}
const mergedRef = mergeRefs([ref, contentRef]);
const updateArrowSize = ({ height, width, }) => {
setArrowHeight(height);
setArrowWidth(width);
};
const updateArrowElement = (element) => {
setArrowElement(element);
};
const providerValues = React.useMemo(() => {
return {
arrowProps: arrowProps,
arrowHeight,
arrowWidth,
updateArrowSize,
updateArrowElement,
actualPlacement: calculatedPlacement,
};
}, [calculatedPlacement, arrowProps, arrowHeight, arrowWidth]);
const popoverContentStyle = React.useMemo(() => {
const arrayConvertedStyles = Array.isArray(style) ? style : [style];
const containerStyle = arrowElement
? getContainerStyle({
placement: calculatedPlacement,
arrowHeight: arrowHeight,
})
: {};
return [containerStyle, arrayConvertedStyles];
}, [calculatedPlacement, arrowHeight, style, arrowElement]);
const initialAnimatedStyles = {
opacity: 0,
y: calculatedPlacement === 'top'
? 6
: calculatedPlacement === 'bottom'
? -6
: 0,
x: calculatedPlacement === 'right'
? -6
: calculatedPlacement === 'left'
? 6
: 0,
};
const animatedStyles = {
opacity: 1,
y: 0,
x: 0,
};
const exitAnimatedStyles = {
opacity: 0,
};
return (<PopoverContentProvider value={Object.assign(Object.assign(Object.assign({}, value), providerValues), { isFlipped })}>
<OverlayAnimatePresence visible={isOpen} AnimatePresence={AnimatePresence}>
<View style={Object.assign({ position: 'absolute', alignItems: calculatedPlacement === 'right'
? 'flex-start'
: calculatedPlacement === 'left'
? 'flex-end'
: 'center' }, overlayProps === null || overlayProps === void 0 ? void 0 : overlayProps.style)} ref={overlayRef} collapsable={false}>
{arrowElement}
<FocusScopeComponent contain={trapFocus} restoreFocus autoFocus>
<StyledPopoverContent id={popoverContentId} {...accessibilityProps} {...props} isOpen={isOpen} collapsable={false} {...dialogProps} tabIndex={Platform.OS === 'web' ? -1 : undefined} initial={initialAnimatedStyles} animate={animatedStyles} exit={exitAnimatedStyles} style={popoverContentStyle} ref={mergedRef} dataSet={{ flip: isFlipped }} states={{
flip: isFlipped,
}}>
{children}
</StyledPopoverContent>
</FocusScopeComponent>
</View>
</OverlayAnimatePresence>
</PopoverContentProvider>);
});
const FocusScopeComponent = ({ trapFocus, focusScope, children }) => {
if (focusScope)
return (<FocusScopeAria contain={trapFocus} restoreFocus autoFocus>
{children}
</FocusScopeAria>);
return children;
};
export default PopoverContent;
//# sourceMappingURL=PopoverContent.jsx.map