1 | import { __rest } from "tslib";
|
2 | import * as React from 'react';
|
3 | import * as ReactDOM from 'react-dom';
|
4 | import { canUseDOM, KEY_CODES } from '../../helpers';
|
5 | import { css } from '@patternfly/react-styles';
|
6 | import styles from '@patternfly/react-styles/css/components/Backdrop/backdrop';
|
7 | import { ModalContent } from './ModalContent';
|
8 | import { getDefaultOUIAId } from '../../helpers';
|
9 | export var ModalVariant;
|
10 | (function (ModalVariant) {
|
11 | ModalVariant["small"] = "small";
|
12 | ModalVariant["medium"] = "medium";
|
13 | ModalVariant["large"] = "large";
|
14 | ModalVariant["default"] = "default";
|
15 | })(ModalVariant || (ModalVariant = {}));
|
16 | export class Modal extends React.Component {
|
17 | constructor(props) {
|
18 | super(props);
|
19 | this.boxId = '';
|
20 | this.labelId = '';
|
21 | this.descriptorId = '';
|
22 | this.handleEscKeyClick = (event) => {
|
23 | const { onEscapePress } = this.props;
|
24 | if (event.keyCode === KEY_CODES.ESCAPE_KEY && this.props.isOpen) {
|
25 | onEscapePress ? onEscapePress(event) : this.props.onClose();
|
26 | }
|
27 | };
|
28 | this.getElement = (appendTo) => {
|
29 | if (typeof appendTo === 'function') {
|
30 | return appendTo();
|
31 | }
|
32 | return appendTo || document.body;
|
33 | };
|
34 | this.toggleSiblingsFromScreenReaders = (hide) => {
|
35 | const { appendTo } = this.props;
|
36 | const target = this.getElement(appendTo);
|
37 | const bodyChildren = target.children;
|
38 | for (const child of Array.from(bodyChildren)) {
|
39 | if (child !== this.state.container) {
|
40 | hide ? child.setAttribute('aria-hidden', '' + hide) : child.removeAttribute('aria-hidden');
|
41 | }
|
42 | }
|
43 | };
|
44 | this.isEmpty = (value) => value === null || value === undefined || value === '';
|
45 | const boxIdNum = Modal.currentId++;
|
46 | const labelIdNum = boxIdNum + 1;
|
47 | const descriptorIdNum = boxIdNum + 2;
|
48 | this.boxId = props.id || `pf-modal-part-${boxIdNum}`;
|
49 | this.labelId = `pf-modal-part-${labelIdNum}`;
|
50 | this.descriptorId = `pf-modal-part-${descriptorIdNum}`;
|
51 | this.state = {
|
52 | container: undefined,
|
53 | ouiaStateId: getDefaultOUIAId(Modal.displayName, props.variant)
|
54 | };
|
55 | }
|
56 | componentDidMount() {
|
57 | const { appendTo, title, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, hasNoBodyWrapper, header } = this.props;
|
58 | const target = this.getElement(appendTo);
|
59 | const container = document.createElement('div');
|
60 | this.setState({ container });
|
61 | target.appendChild(container);
|
62 | target.addEventListener('keydown', this.handleEscKeyClick, false);
|
63 | if (this.props.isOpen) {
|
64 | target.classList.add(css(styles.backdropOpen));
|
65 | }
|
66 | else {
|
67 | target.classList.remove(css(styles.backdropOpen));
|
68 | }
|
69 | if (this.isEmpty(title) && this.isEmpty(ariaLabel) && this.isEmpty(ariaLabelledby)) {
|
70 |
|
71 | console.error('Modal: Specify at least one of: title, aria-label, aria-labelledby.');
|
72 | }
|
73 | if (this.isEmpty(ariaLabel) && this.isEmpty(ariaLabelledby) && (hasNoBodyWrapper || header)) {
|
74 |
|
75 | console.error('Modal: When using hasNoBodyWrapper or setting a custom header, ensure you assign an accessible name to the the modal container with aria-label or aria-labelledby.');
|
76 | }
|
77 | }
|
78 | componentDidUpdate() {
|
79 | const { appendTo } = this.props;
|
80 | const target = this.getElement(appendTo);
|
81 | if (this.props.isOpen) {
|
82 | target.classList.add(css(styles.backdropOpen));
|
83 | this.toggleSiblingsFromScreenReaders(true);
|
84 | }
|
85 | else {
|
86 | target.classList.remove(css(styles.backdropOpen));
|
87 | this.toggleSiblingsFromScreenReaders(false);
|
88 | }
|
89 | }
|
90 | componentWillUnmount() {
|
91 | const { appendTo } = this.props;
|
92 | const target = this.getElement(appendTo);
|
93 | if (this.state.container) {
|
94 | target.removeChild(this.state.container);
|
95 | }
|
96 | target.removeEventListener('keydown', this.handleEscKeyClick, false);
|
97 | target.classList.remove(css(styles.backdropOpen));
|
98 | }
|
99 | render() {
|
100 | const _a = this.props, {
|
101 |
|
102 | appendTo,
|
103 |
|
104 | onEscapePress, 'aria-labelledby': ariaLabelledby, 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedby, bodyAriaLabel, bodyAriaRole, title, titleIconVariant, titleLabel, ouiaId, ouiaSafe } = _a, props = __rest(_a, ["appendTo", "onEscapePress", 'aria-labelledby', 'aria-label', 'aria-describedby', "bodyAriaLabel", "bodyAriaRole", "title", "titleIconVariant", "titleLabel", "ouiaId", "ouiaSafe"]);
|
105 | const { container } = this.state;
|
106 | if (!canUseDOM || !container) {
|
107 | return null;
|
108 | }
|
109 | return ReactDOM.createPortal(React.createElement(ModalContent, Object.assign({}, props, { boxId: this.boxId, labelId: this.labelId, descriptorId: this.descriptorId, title: title, titleIconVariant: titleIconVariant, titleLabel: titleLabel, "aria-label": ariaLabel, "aria-describedby": ariaDescribedby, "aria-labelledby": ariaLabelledby, bodyAriaLabel: bodyAriaLabel, bodyAriaRole: bodyAriaRole, ouiaId: ouiaId !== undefined ? ouiaId : this.state.ouiaStateId, ouiaSafe: ouiaSafe })), container);
|
110 | }
|
111 | }
|
112 | Modal.displayName = 'Modal';
|
113 | Modal.currentId = 0;
|
114 | Modal.defaultProps = {
|
115 | className: '',
|
116 | isOpen: false,
|
117 | title: '',
|
118 | titleIconVariant: null,
|
119 | titleLabel: '',
|
120 | 'aria-label': '',
|
121 | showClose: true,
|
122 | 'aria-describedby': '',
|
123 | 'aria-labelledby': '',
|
124 | id: undefined,
|
125 | actions: [],
|
126 | onClose: () => undefined,
|
127 | variant: 'default',
|
128 | hasNoBodyWrapper: false,
|
129 | appendTo: () => document.body,
|
130 | ouiaSafe: true
|
131 | };
|
132 |
|
\ | No newline at end of file |