import { Accessibility } from '@fluentui/accessibility'; import { ComponentWithAs, AutoFocusZone, AutoFocusZoneProps, FocusTrapZone, FocusTrapZoneProps, getElementType, useAccessibility, useFluentContext, useStyles, useTelemetry, useUnhandledProps, } from '@fluentui/react-bindings'; import * as customPropTypes from '@fluentui/react-proptypes'; import * as PopperJs from '@popperjs/core'; import cx from 'classnames'; import * as _ from 'lodash'; import * as PropTypes from 'prop-types'; import * as React from 'react'; import { ComponentEventHandler, FluentComponentStaticProps } from '../../types'; import { childrenExist, createShorthandFactory, UIComponentProps, ChildrenComponentProps, ContentComponentProps, commonPropTypes, rtlTextContainer, } from '../../utils'; import { getBasePlacement, PopperChildrenProps } from '../../utils/positioner'; export interface PopupContentSlotClassNames { content: string; } export interface PopupContentProps extends UIComponentProps, ChildrenComponentProps, ContentComponentProps { /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility; /** * Called after user's mouse enter. * @param event - React's original SyntheticEvent. * @param data - All props. */ onMouseEnter?: ComponentEventHandler; /** * Called after user's mouse leave. * @param event - React's original SyntheticEvent. * @param data - All props. */ onMouseLeave?: ComponentEventHandler; /** An actual placement value from Popper. */ placement?: PopperChildrenProps['placement']; /** A popup can show a pointer to trigger. */ pointing?: boolean; /** A ref to a pointer element. */ pointerRef?: React.Ref; /** Controls whether or not focus trap should be applied, using boolean or FocusTrapZoneProps type value. */ trapFocus?: boolean | FocusTrapZoneProps; /** Controls whether or not auto focus should be applied, using boolean or AutoFocusZoneProps type value. */ autoFocus?: boolean | AutoFocusZoneProps; } export type PopupContentStylesProps = Required> & { basePlacement: PopperJs.BasePlacement; }; export const popupContentClassName = 'ui-popup__content'; export const popupContentSlotClassNames: PopupContentSlotClassNames = { content: `${popupContentClassName}__content`, }; /** * A PopupContent displays the content of a Popup component. */ export const PopupContent: ComponentWithAs<'div', PopupContentProps> & FluentComponentStaticProps = props => { const context = useFluentContext(); const { setStart, setEnd } = useTelemetry(PopupContent.displayName, context.telemetry); setStart(); const { accessibility, autoFocus, children, className, content, design, placement, pointing, pointerRef, styles, trapFocus, variables, } = props; const getA11yProps = useAccessibility(accessibility, { debugName: PopupContent.displayName, rtl: context.rtl, }); const { classes } = useStyles(PopupContent.displayName, { className: popupContentClassName, mapPropsToStyles: () => ({ basePlacement: getBasePlacement(placement, context.rtl), pointing, }), mapPropsToInlineStyles: () => ({ className, design, styles, variables }), rtl: context.rtl, }); const ElementType = getElementType(props); const unhandledProps = useUnhandledProps(PopupContent.handledProps, props); const handleMouseEnter = e => { _.invoke(props, 'onMouseEnter', e, props); }; const handleMouseLeave = e => { _.invoke(props, 'onMouseLeave', e, props); }; const popupContentProps: PopupContentProps = getA11yProps('root', { className: classes.root, ...rtlTextContainer.getAttributes({ forElements: [children, content] }), ...unhandledProps, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, }); const popupContent = ( <> {pointing &&
}
{childrenExist(children) ? children : content}
); let element: React.ReactElement; if (trapFocus) { const focusTrapZoneProps = { ...popupContentProps, ...((_.keys(trapFocus).length && trapFocus) as FocusTrapZoneProps), as: ElementType, }; element = {popupContent}; } else if (autoFocus) { const autoFocusZoneProps = { ...popupContentProps, ...((_.keys(autoFocus).length && autoFocus) as AutoFocusZoneProps), as: ElementType, }; element = {popupContent}; } else { element = {popupContent}; } setEnd(); return element; }; PopupContent.displayName = 'PopupContent'; PopupContent.propTypes = { ...commonPropTypes.createCommon(), placement: PropTypes.oneOf([ 'auto-start', 'auto', 'auto-end', 'top-start', 'top', 'top-end', 'right-start', 'right', 'right-end', 'bottom-end', 'bottom', 'bottom-start', 'left-end', 'left', 'left-start', ]), pointing: PropTypes.bool, onMouseEnter: PropTypes.func, onMouseLeave: PropTypes.func, pointerRef: customPropTypes.ref, trapFocus: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]), autoFocus: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]), }; PopupContent.handledProps = Object.keys(PopupContent.propTypes) as any; PopupContent.create = createShorthandFactory({ Component: PopupContent, mappedProp: 'content' });