134 lines
3.9 KiB
JavaScript
134 lines
3.9 KiB
JavaScript
import { Component } from 'react';
|
|
import Dialog from '@mui/material/Dialog';
|
|
import { DIALOG_FOG } from '../colors';
|
|
|
|
/**
|
|
* Shared dialog shell — drop-in replacement for MUI `Dialog`.
|
|
*
|
|
* Guarantees across the whole app:
|
|
* - instant open/close (no Fade/scale animation): `transitionDuration={0}`
|
|
* - darker, bluish fog: backdrop `backgroundColor` = `DIALOG_FOG`
|
|
* - scrollbar-gutter matches the fog: toggles `df-dialog-open` on
|
|
* `<html>` while any `AppDialog` is open (see `src/index.css`).
|
|
*
|
|
* Class component on purpose (PureComponent-free: open state is driven
|
|
* by props, state lives in the wrapped content).
|
|
*
|
|
* Usage:
|
|
* import AppDialog from '../AppDialog';
|
|
* <AppDialog open={open} onClose={onClose} maxWidth="xs" fullWidth>
|
|
*/
|
|
class AppDialog extends Component {
|
|
static openCount = 0;
|
|
|
|
static updateFogClass() {
|
|
if (typeof document === 'undefined') return;
|
|
const html = document.documentElement;
|
|
if (!html) return;
|
|
if (AppDialog.openCount > 0) {
|
|
html.classList.add('df-dialog-open');
|
|
} else {
|
|
html.classList.remove('df-dialog-open');
|
|
}
|
|
}
|
|
|
|
static enter() {
|
|
AppDialog.openCount += 1;
|
|
AppDialog.updateFogClass();
|
|
}
|
|
|
|
static exit() {
|
|
AppDialog.openCount = Math.max(0, AppDialog.openCount - 1);
|
|
AppDialog.updateFogClass();
|
|
}
|
|
|
|
componentDidMount() {
|
|
if (this.props.open) AppDialog.enter();
|
|
}
|
|
|
|
componentDidUpdate(prevProps) {
|
|
if (this.props.open && !prevProps.open) AppDialog.enter();
|
|
else if (!this.props.open && prevProps.open) AppDialog.exit();
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
if (this.props.open) AppDialog.exit();
|
|
}
|
|
|
|
render() {
|
|
const {
|
|
transitionDuration: _transitionDuration,
|
|
TransitionProps: _transitionProps,
|
|
transitionProps: _lowerTransitionProps,
|
|
TransitionComponent: _transitionComponent,
|
|
// Legacy MUI prop (removed in v9) — merge into slotProps.paper instead
|
|
// of spreading onto Dialog/DOM where it triggers
|
|
// "React does not recognize the `PaperProps` prop" warnings.
|
|
PaperProps: legacyPaperProps,
|
|
disableScrollLock = true,
|
|
slotProps: userSlotProps,
|
|
slots: userSlots,
|
|
...rest
|
|
} = this.props;
|
|
void _transitionDuration;
|
|
void _transitionProps;
|
|
void _lowerTransitionProps;
|
|
void _transitionComponent;
|
|
|
|
const userPaper = userSlotProps?.paper ?? {};
|
|
const { sx: userPaperSx, ...restPaper } = userPaper;
|
|
const { sx: legacyPaperSx, ...restLegacyPaper } = legacyPaperProps ?? {};
|
|
const toSxArray = (sx) =>
|
|
// eslint-disable-next-line no-nested-ternary
|
|
Array.isArray(sx) ? sx : sx ? [sx] : [];
|
|
const mergedPaperSx = [...toSxArray(legacyPaperSx), ...toSxArray(userPaperSx)];
|
|
const mergedPaper =
|
|
mergedPaperSx.length > 0
|
|
? { ...restLegacyPaper, ...restPaper, sx: mergedPaperSx }
|
|
: { ...restLegacyPaper, ...restPaper };
|
|
|
|
const userBackdrop = userSlotProps?.backdrop ?? {};
|
|
const { sx: userBackdropSx, ...restBackdrop } = userBackdrop;
|
|
const userTransition = userSlotProps?.transition ?? {};
|
|
const { transition: _userTransitionSlot, ...restSlots } = userSlots ?? {};
|
|
void _userTransitionSlot;
|
|
|
|
const slotProps = {
|
|
...userSlotProps,
|
|
paper: mergedPaper,
|
|
backdrop: {
|
|
...restBackdrop,
|
|
sx: [
|
|
{ backgroundColor: DIALOG_FOG },
|
|
...(Array.isArray(userBackdropSx)
|
|
? userBackdropSx
|
|
: userBackdropSx
|
|
? [userBackdropSx]
|
|
: []),
|
|
],
|
|
},
|
|
transition: {
|
|
...userTransition,
|
|
timeout: 0,
|
|
},
|
|
};
|
|
|
|
return (
|
|
<Dialog
|
|
disableScrollLock={disableScrollLock}
|
|
disableEnforceFocus
|
|
{...rest}
|
|
onWheel={(e) => {
|
|
e.stopPropagation();
|
|
rest.onWheel?.(e);
|
|
}}
|
|
slots={restSlots}
|
|
slotProps={slotProps}
|
|
transitionDuration={0}
|
|
/>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default AppDialog;
|