128 lines
5.4 KiB
JavaScript
128 lines
5.4 KiB
JavaScript
'use client';
|
|
import { Escape } from '@fluentui/keyboard-keys';
|
|
import { presenceMotionSlot } from '@fluentui/react-motion';
|
|
import { useEventCallback, useMergedRefs, isResolvedShorthand, slot, getIntrinsicElementProps, useIsomorphicLayoutEffect } from '@fluentui/react-utilities';
|
|
import * as React from 'react';
|
|
import { useDialogContext_unstable, useDialogBackdropContext_unstable } from '../../contexts';
|
|
import { useDisableBodyScroll } from '../../utils/useDisableBodyScroll';
|
|
import { DialogBackdropMotion } from '../DialogBackdropMotion';
|
|
import { useMotionForwardedRef } from '@fluentui/react-motion';
|
|
/**
|
|
* Create the state required to render DialogSurface.
|
|
*
|
|
* The returned state can be modified with hooks such as useDialogSurfaceStyles_unstable,
|
|
* before being passed to renderDialogSurface_unstable.
|
|
*
|
|
* @param props - props from this instance of DialogSurface
|
|
* @param ref - reference to root HTMLElement of DialogSurface
|
|
*/ export const useDialogSurface_unstable = (props, ref)=>{
|
|
const contextRef = useMotionForwardedRef();
|
|
const modalType = useDialogContext_unstable((ctx)=>ctx.modalType);
|
|
const isNestedDialog = useDialogContext_unstable((ctx)=>ctx.isNestedDialog);
|
|
const backdropOverride = useDialogBackdropContext_unstable();
|
|
const treatBackdropAsNested = backdropOverride !== null && backdropOverride !== void 0 ? backdropOverride : isNestedDialog;
|
|
const modalAttributes = useDialogContext_unstable((ctx)=>ctx.modalAttributes);
|
|
const dialogRef = useDialogContext_unstable((ctx)=>ctx.dialogRef);
|
|
const requestOpenChange = useDialogContext_unstable((ctx)=>ctx.requestOpenChange);
|
|
const dialogTitleID = useDialogContext_unstable((ctx)=>ctx.dialogTitleId);
|
|
const open = useDialogContext_unstable((ctx)=>ctx.open);
|
|
const unmountOnClose = useDialogContext_unstable((ctx)=>ctx.unmountOnClose);
|
|
const handledBackdropClick = useEventCallback((event)=>{
|
|
if (isResolvedShorthand(props.backdrop)) {
|
|
var _props_backdrop_onClick, _props_backdrop;
|
|
(_props_backdrop_onClick = (_props_backdrop = props.backdrop).onClick) === null || _props_backdrop_onClick === void 0 ? void 0 : _props_backdrop_onClick.call(_props_backdrop, event);
|
|
}
|
|
if (modalType === 'modal' && !event.isDefaultPrevented()) {
|
|
requestOpenChange({
|
|
event,
|
|
open: false,
|
|
type: 'backdropClick'
|
|
});
|
|
}
|
|
});
|
|
const handleKeyDown = useEventCallback((event)=>{
|
|
var _props_onKeyDown;
|
|
(_props_onKeyDown = props.onKeyDown) === null || _props_onKeyDown === void 0 ? void 0 : _props_onKeyDown.call(props, event);
|
|
if (event.key === Escape && !event.isDefaultPrevented()) {
|
|
requestOpenChange({
|
|
event,
|
|
open: false,
|
|
type: 'escapeKeyDown'
|
|
});
|
|
// stop propagation to avoid conflicting with other elements that listen for `Escape`
|
|
// e,g: nested Dialog, Popover, Menu and Tooltip
|
|
event.preventDefault();
|
|
}
|
|
});
|
|
const backdrop = slot.optional(props.backdrop, {
|
|
renderByDefault: modalType !== 'non-modal',
|
|
defaultProps: {
|
|
'aria-hidden': 'true'
|
|
},
|
|
elementType: 'div'
|
|
});
|
|
const backdropAppearance = backdrop === null || backdrop === void 0 ? void 0 : backdrop.appearance;
|
|
if (backdrop) {
|
|
backdrop.onClick = handledBackdropClick;
|
|
// remove backdrop.appearance so it is not passed to the DOM
|
|
delete backdrop.appearance;
|
|
}
|
|
const { disableBodyScroll, enableBodyScroll } = useDisableBodyScroll();
|
|
useIsomorphicLayoutEffect(()=>{
|
|
if (!open) {
|
|
enableBodyScroll();
|
|
return;
|
|
}
|
|
if (isNestedDialog || modalType === 'non-modal') {
|
|
return;
|
|
}
|
|
disableBodyScroll();
|
|
return ()=>enableBodyScroll();
|
|
}, [
|
|
open,
|
|
modalType,
|
|
isNestedDialog,
|
|
disableBodyScroll,
|
|
enableBodyScroll
|
|
]);
|
|
return {
|
|
components: {
|
|
backdrop: 'div',
|
|
root: 'div',
|
|
backdropMotion: DialogBackdropMotion
|
|
},
|
|
open,
|
|
backdrop,
|
|
isNestedDialog,
|
|
treatBackdropAsNested,
|
|
backdropAppearance,
|
|
unmountOnClose,
|
|
mountNode: props.mountNode,
|
|
root: slot.always(getIntrinsicElementProps('div', {
|
|
tabIndex: -1,
|
|
role: modalType === 'alert' ? 'alertdialog' : 'dialog',
|
|
'aria-modal': modalType !== 'non-modal',
|
|
'aria-labelledby': props['aria-label'] ? undefined : dialogTitleID,
|
|
'aria-hidden': !unmountOnClose && !open ? true : undefined,
|
|
...props,
|
|
...modalAttributes,
|
|
onKeyDown: handleKeyDown,
|
|
// FIXME:
|
|
// `DialogSurfaceElement` is wrongly assigned to be `HTMLElement` instead of `HTMLDivElement`
|
|
// but since it would be a breaking change to fix it, we are casting ref to it's proper type
|
|
ref: useMergedRefs(ref, contextRef, dialogRef)
|
|
}), {
|
|
elementType: 'div'
|
|
}),
|
|
backdropMotion: presenceMotionSlot(props.backdropMotion, {
|
|
elementType: DialogBackdropMotion,
|
|
defaultProps: {
|
|
appear: unmountOnClose,
|
|
visible: open
|
|
}
|
|
}),
|
|
// Deprecated properties
|
|
transitionStatus: undefined
|
|
};
|
|
};
|