Персист модалок: черновики, восстановление после F5, предупреждение при выходе #30
@@ -1,6 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import styles from './Modal.module.scss';
|
||||
import { Button } from './Button';
|
||||
import { ModalOverlay } from './ModalOverlay';
|
||||
import { texts } from '../../texts';
|
||||
|
||||
export interface IConfirmDialogProps {
|
||||
@@ -20,7 +21,7 @@ export const ConfirmDialog: React.FC<IConfirmDialogProps> = ({
|
||||
onCancel,
|
||||
busy
|
||||
}) => (
|
||||
<div className={styles.overlay} onClick={e => { if (e.target === e.currentTarget) onCancel(); }}>
|
||||
<ModalOverlay onBackdropClick={onCancel}>
|
||||
<div className={`${styles.box} ${styles.narrow}`}>
|
||||
<div className={styles.title}>{title}</div>
|
||||
<p className={styles.text}>{text}</p>
|
||||
@@ -29,5 +30,5 @@ export const ConfirmDialog: React.FC<IConfirmDialogProps> = ({
|
||||
<Button variant="danger" icon="trash" onClick={onConfirm} disabled={busy}>{confirmLabel}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ModalOverlay>
|
||||
);
|
||||
|
||||
@@ -7,9 +7,17 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 300;
|
||||
// Выше шапки SharePoint, но ниже слоёв Fluent UI (1000000). Оверлей рендерится
|
||||
// порталом в body (см. ModalOverlay), поэтому сравнивается с шапкой напрямую.
|
||||
z-index: 100000;
|
||||
backdrop-filter: blur(6px);
|
||||
padding: 20px;
|
||||
// Портал вне корня веб-части — шрифт и метрики .shell (AdminApp.module.scss)
|
||||
// сюда не наследуются, задаём заново.
|
||||
font-family: $font-sans;
|
||||
font-size: $fs-body;
|
||||
line-height: 1.55;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.box {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import styles from './Modal.module.scss';
|
||||
import { IconButton } from './IconButton';
|
||||
import { ModalOverlay } from './ModalOverlay';
|
||||
import { texts } from '../../texts';
|
||||
|
||||
export interface IModalProps {
|
||||
@@ -11,8 +12,7 @@ export interface IModalProps {
|
||||
}
|
||||
|
||||
export const Modal: React.FC<IModalProps> = ({ title, onClose, footer, children }) => (
|
||||
// Клик по подложке закрывает окно, клик внутри — нет (проверяем сам currentTarget).
|
||||
<div className={styles.overlay} onClick={e => { if (e.target === e.currentTarget) onClose(); }}>
|
||||
<ModalOverlay onBackdropClick={onClose}>
|
||||
<div className={styles.box}>
|
||||
<div className={styles.header}>
|
||||
<div className={styles.title}>{title}</div>
|
||||
@@ -21,5 +21,5 @@ export const Modal: React.FC<IModalProps> = ({ title, onClose, footer, children
|
||||
<div className={styles.body}>{children}</div>
|
||||
{footer && <div className={styles.footer}>{footer}</div>}
|
||||
</div>
|
||||
</div>
|
||||
</ModalOverlay>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import styles from './Modal.module.scss';
|
||||
|
||||
export interface IModalOverlayProps {
|
||||
/** Клик по подложке (не по содержимому окна). */
|
||||
onBackdropClick: () => void;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Затемнение под модалкой — порталом в document.body, а не внутри веб-части.
|
||||
* На живой странице SharePoint канва веб-части лежит в своём контексте наложения
|
||||
* ниже шапки (suite bar), и position: fixed-оверлей изнутри неё оказывался ПОД
|
||||
* шапкой: верх окна, центрированного по высоте экрана, уезжал за неё. В workbench
|
||||
* шапки нет — там было незаметно.
|
||||
*/
|
||||
export const ModalOverlay: React.FC<IModalOverlayProps> = ({ onBackdropClick, children }) =>
|
||||
ReactDOM.createPortal(
|
||||
// Клик по подложке закрывает окно, клик внутри — нет (проверяем сам currentTarget).
|
||||
<div className={styles.overlay} onClick={e => { if (e.target === e.currentTarget) onBackdropClick(); }}>
|
||||
{children}
|
||||
</div>,
|
||||
document.body
|
||||
);
|
||||
@@ -1,6 +1,6 @@
|
||||
@import '../../../../styles/design-tokens';
|
||||
|
||||
// Ниже Modal.module.scss/.overlay (z-index 300) — диалоги, которые может
|
||||
// Ниже Modal.module.scss/.overlay (z-index 100000) — диалоги, которые может
|
||||
// открыть сама длительная операция (например, разрешение конфликта), должны
|
||||
// быть ПОВЕРХ этой подложки, а не под ней.
|
||||
.overlay {
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
box-shadow: $sh-modal;
|
||||
border: 1px solid $gray-200;
|
||||
padding: 6px;
|
||||
z-index: 400;
|
||||
z-index: 100001; // над оверлеем модалки (Modal.module.scss)
|
||||
max-height: 320px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -17,6 +17,7 @@ export * from './PrimaryCell';
|
||||
export * from './ActionsCell';
|
||||
export * from './CellNote';
|
||||
export * from './Modal';
|
||||
export * from './ModalOverlay';
|
||||
export * from './ConfirmDialog';
|
||||
export * from './FormModal';
|
||||
export * from './FormGrid';
|
||||
|
||||
Reference in New Issue
Block a user