Files
dashfront/src/components/AppDialog.jsx
2026-10-14 01:13:09 +02:00

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;