@scm-manager/ui-components 2.29.2-20220021-101653 → 2.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -40,6 +40,7 @@ export type ButtonProps = {
40
40
  reducedMobile?: boolean;
41
41
  children?: ReactNode;
42
42
  testId?: string;
43
+ ref?: React.ForwardedRef<HTMLButtonElement>;
43
44
  };
44
45
 
45
46
  type Props = ButtonProps & {
@@ -47,7 +48,11 @@ type Props = ButtonProps & {
47
48
  color?: string;
48
49
  };
49
50
 
50
- const Button: FC<Props> = ({
51
+ type InnerProps = Props & {
52
+ innerRef: React.Ref<HTMLButtonElement>;
53
+ };
54
+
55
+ const Button: FC<InnerProps> = ({
51
56
  link,
52
57
  className,
53
58
  icon,
@@ -61,7 +66,8 @@ const Button: FC<Props> = ({
61
66
  loading,
62
67
  disabled,
63
68
  action,
64
- color = "default"
69
+ color = "default",
70
+ innerRef
65
71
  }) => {
66
72
  const renderIcon = () => {
67
73
  return <>{icon ? <Icon name={icon} color="inherit" className="is-medium pr-1" /> : null}</>;
@@ -109,6 +115,7 @@ const Button: FC<Props> = ({
109
115
  disabled={disabled}
110
116
  onClick={event => action && action(event)}
111
117
  className={classes}
118
+ ref={innerRef}
112
119
  {...createAttributesForTesting(testId)}
113
120
  >
114
121
  {content}
@@ -116,4 +123,4 @@ const Button: FC<Props> = ({
116
123
  );
117
124
  };
118
125
 
119
- export default Button;
126
+ export default React.forwardRef<HTMLButtonElement, Props>((props, ref) => <Button {...props} innerRef={ref} />);
@@ -21,112 +21,78 @@
21
21
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
22
  * SOFTWARE.
23
23
  */
24
- import React from "react";
25
- import { WithTranslation, withTranslation } from "react-i18next";
24
+ import React, { FC, useEffect, useState } from "react";
25
+ import { useTranslation } from "react-i18next";
26
26
  import InputField from "./InputField";
27
27
 
28
- type State = {
29
- password: string;
30
- confirmedPassword: string;
31
- passwordValid: boolean;
32
- passwordConfirmationFailed: boolean;
33
- };
34
- type Props = WithTranslation & {
28
+ type BaseProps = {
35
29
  passwordChanged: (p1: string, p2: boolean) => void;
36
30
  passwordValidator?: (p: string) => boolean;
37
31
  onReturnPressed?: () => void;
38
32
  };
39
33
 
40
- class PasswordConfirmation extends React.Component<Props, State> {
41
- constructor(props: Props) {
42
- super(props);
43
- this.state = {
44
- password: "",
45
- confirmedPassword: "",
46
- passwordValid: true,
47
- passwordConfirmationFailed: false,
48
- };
49
- }
34
+ type InnerProps = BaseProps & {
35
+ innerRef: React.Ref<HTMLInputElement>;
36
+ };
50
37
 
51
- componentDidMount() {
52
- this.setState({
53
- password: "",
54
- confirmedPassword: "",
55
- passwordValid: true,
56
- passwordConfirmationFailed: false,
57
- });
58
- }
38
+ const PasswordConfirmation: FC<InnerProps> = ({ passwordChanged, passwordValidator, onReturnPressed, innerRef }) => {
39
+ const [t] = useTranslation("commons");
40
+ const [password, setPassword] = useState("");
41
+ const [confirmedPassword, setConfirmedPassword] = useState("");
42
+ const [passwordValid, setPasswordValid] = useState(true);
43
+ const [passwordConfirmationFailed, setPasswordConfirmationFailed] = useState(false);
44
+ const isValid = passwordValid && !passwordConfirmationFailed;
59
45
 
60
- render() {
61
- const { t, onReturnPressed } = this.props;
62
- return (
63
- <div className="columns is-multiline">
64
- <div className="column is-half">
65
- <InputField
66
- label={t("password.newPassword")}
67
- type="password"
68
- onChange={this.handlePasswordChange}
69
- value={this.state.password ? this.state.password : ""}
70
- validationError={!this.state.passwordValid}
71
- errorMessage={t("password.passwordInvalid")}
72
- />
73
- </div>
74
- <div className="column is-half">
75
- <InputField
76
- label={t("password.confirmPassword")}
77
- type="password"
78
- onChange={this.handlePasswordValidationChange}
79
- value={this.state ? this.state.confirmedPassword : ""}
80
- validationError={this.state.passwordConfirmationFailed}
81
- errorMessage={t("password.passwordConfirmFailed")}
82
- onReturnPressed={onReturnPressed}
83
- />
84
- </div>
85
- </div>
86
- );
87
- }
46
+ useEffect(() => passwordChanged(password, isValid), [password, isValid]);
88
47
 
89
- validatePassword = (password: string) => {
90
- const { passwordValidator } = this.props;
48
+ const validatePassword = (newPassword: string) => {
91
49
  if (passwordValidator) {
92
- return passwordValidator(password);
50
+ return passwordValidator(newPassword);
93
51
  }
94
52
 
95
- return password.length >= 6 && password.length < 32;
53
+ return newPassword.length >= 6 && newPassword.length < 32;
96
54
  };
97
55
 
98
- handlePasswordValidationChange = (confirmedPassword: string) => {
99
- const passwordConfirmed = this.state.password === confirmedPassword;
100
-
101
- this.setState(
102
- {
103
- confirmedPassword,
104
- passwordConfirmationFailed: !passwordConfirmed,
105
- },
106
- this.propagateChange
107
- );
56
+ const handlePasswordValidationChange = (newConfirmedPassword: string) => {
57
+ setConfirmedPassword(newConfirmedPassword);
58
+ setPasswordConfirmationFailed(password !== newConfirmedPassword);
108
59
  };
109
60
 
110
- handlePasswordChange = (password: string) => {
111
- const passwordConfirmationFailed = password !== this.state.confirmedPassword;
112
-
113
- this.setState(
114
- {
115
- passwordValid: this.validatePassword(password),
116
- passwordConfirmationFailed,
117
- password: password,
118
- },
119
- this.propagateChange
120
- );
61
+ const handlePasswordChange = (newPassword: string) => {
62
+ setPasswordConfirmationFailed(newPassword !== confirmedPassword);
63
+ setPassword(newPassword);
64
+ setPasswordValid(validatePassword(newPassword));
121
65
  };
122
66
 
123
- isValid = () => {
124
- return this.state.passwordValid && !this.state.passwordConfirmationFailed;
125
- };
126
-
127
- propagateChange = () => {
128
- this.props.passwordChanged(this.state.password, this.isValid());
129
- };
130
- }
67
+ return (
68
+ <div className="columns is-multiline">
69
+ <div className="column is-half">
70
+ <InputField
71
+ label={t("password.newPassword")}
72
+ type="password"
73
+ onChange={event => handlePasswordChange(event.target.value)}
74
+ value={password}
75
+ validationError={!passwordValid}
76
+ errorMessage={t("password.passwordInvalid")}
77
+ ref={innerRef}
78
+ onReturnPressed={onReturnPressed}
79
+ />
80
+ </div>
81
+ <div className="column is-half">
82
+ <InputField
83
+ label={t("password.confirmPassword")}
84
+ type="password"
85
+ onChange={handlePasswordValidationChange}
86
+ value={confirmedPassword}
87
+ validationError={passwordConfirmationFailed}
88
+ errorMessage={t("password.passwordConfirmFailed")}
89
+ onReturnPressed={onReturnPressed}
90
+ />
91
+ </div>
92
+ </div>
93
+ );
94
+ };
131
95
 
132
- export default withTranslation("commons")(PasswordConfirmation);
96
+ export default React.forwardRef<HTMLInputElement, BaseProps>((props, ref) => (
97
+ <PasswordConfirmation {...props} innerRef={ref} />
98
+ ));
@@ -37,15 +37,27 @@ const buttons = [
37
37
  {
38
38
  className: "is-outlined",
39
39
  label: "Cancel",
40
- onClick: () => null,
40
+ onClick: () => null
41
41
  },
42
42
  {
43
- label: "Submit",
43
+ label: "Submit"
44
+ }
45
+ ];
46
+
47
+ const buttonsWithAutofocus = [
48
+ {
49
+ className: "is-outlined",
50
+ label: "Cancel",
51
+ onClick: () => null
44
52
  },
53
+ {
54
+ label: "I should be focused",
55
+ autofocus: true
56
+ }
45
57
  ];
46
58
 
47
59
  storiesOf("Modal/ConfirmAlert", module)
48
- .addDecorator((story) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
60
+ .addDecorator(story => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
49
61
  .add("Default", () => <ConfirmAlert message={body} title={"Are you sure about that?"} buttons={buttons} />)
50
62
  .add("WithButton", () => {
51
63
  const buttonClick = () => {
@@ -57,4 +69,7 @@ storiesOf("Modal/ConfirmAlert", module)
57
69
  <div id="modalRoot" />
58
70
  </>
59
71
  );
60
- });
72
+ })
73
+ .add("Autofocus", () => (
74
+ <ConfirmAlert message={body} title={"Are you sure about that?"} buttons={buttonsWithAutofocus} />
75
+ ));
@@ -22,7 +22,7 @@
22
22
  * SOFTWARE.
23
23
  */
24
24
  import * as React from "react";
25
- import { FC, useState } from "react";
25
+ import { FC, useRef, useState } from "react";
26
26
  import ReactDOM from "react-dom";
27
27
  import Modal from "./Modal";
28
28
  import classNames from "classnames";
@@ -32,6 +32,7 @@ type Button = {
32
32
  label: string;
33
33
  isLoading?: boolean;
34
34
  onClick?: () => void | null;
35
+ autofocus?: boolean;
35
36
  };
36
37
 
37
38
  type Props = {
@@ -43,6 +44,7 @@ type Props = {
43
44
 
44
45
  export const ConfirmAlert: FC<Props> = ({ title, message, buttons, close }) => {
45
46
  const [showModal, setShowModal] = useState(true);
47
+ const initialFocusButton = useRef<HTMLButtonElement>(null);
46
48
 
47
49
  const onClose = () => {
48
50
  if (typeof close === "function") {
@@ -69,8 +71,9 @@ export const ConfirmAlert: FC<Props> = ({ title, message, buttons, close }) => {
69
71
  className={classNames("button", "is-info", button.className, button.isLoading ? "is-loading" : "")}
70
72
  key={index}
71
73
  onClick={() => handleClickButton(button)}
72
- onKeyDown={(e) => e.key === "Enter" && handleClickButton(button)}
74
+ onKeyDown={e => e.key === "Enter" && handleClickButton(button)}
73
75
  tabIndex={0}
76
+ ref={button.autofocus ? initialFocusButton : undefined}
74
77
  >
75
78
  {button.label}
76
79
  </button>
@@ -80,7 +83,14 @@ export const ConfirmAlert: FC<Props> = ({ title, message, buttons, close }) => {
80
83
  );
81
84
 
82
85
  return (
83
- (showModal && <Modal title={title} closeFunction={onClose} body={body} active={true} footer={footer} />) || null
86
+ <Modal
87
+ title={title}
88
+ closeFunction={onClose}
89
+ body={body}
90
+ active={showModal}
91
+ footer={footer}
92
+ initialFocusRef={initialFocusButton}
93
+ />
84
94
  );
85
95
  };
86
96
 
@@ -22,13 +22,13 @@
22
22
  * SOFTWARE.
23
23
  */
24
24
 
25
- import React, { FC, useEffect, useState } from "react";
26
- import { Modal, InputField, Button, apiClient } from "@scm-manager/ui-components";
27
- import { WithTranslation, withTranslation } from "react-i18next";
25
+ import React, { FC, useEffect, useRef, useState } from "react";
26
+ import { apiClient, Button, InputField, Modal } from "@scm-manager/ui-components";
27
+ import { useTranslation } from "react-i18next";
28
28
  import { Tag } from "@scm-manager/ui-types";
29
29
  import { isBranchValid } from "../validation";
30
30
 
31
- type Props = WithTranslation & {
31
+ type Props = {
32
32
  existingTagsLink: string;
33
33
  tagCreationLink: string;
34
34
  onClose: () => void;
@@ -40,16 +40,18 @@ type Props = WithTranslation & {
40
40
  /**
41
41
  * @deprecated
42
42
  */
43
- const CreateTagModal: FC<Props> = ({ t, onClose, tagCreationLink, existingTagsLink, onCreated, onError, revision }) => {
43
+ const CreateTagModal: FC<Props> = ({ onClose, tagCreationLink, existingTagsLink, onCreated, onError, revision }) => {
44
+ const [t] = useTranslation("repos");
44
45
  const [newTagName, setNewTagName] = useState("");
45
46
  const [loading, setLoading] = useState(false);
46
47
  const [tagNames, setTagNames] = useState<string[]>([]);
48
+ const initialFocusRef = useRef<HTMLInputElement>(null);
47
49
 
48
50
  useEffect(() => {
49
51
  apiClient
50
52
  .get(existingTagsLink)
51
- .then((response) => response.json())
52
- .then((json) => setTagNames(json._embedded.tags.map((tag: Tag) => tag.name)));
53
+ .then(response => response.json())
54
+ .then(json => setTagNames(json._embedded.tags.map((tag: Tag) => tag.name)));
53
55
  }, [existingTagsLink]);
54
56
 
55
57
  const createTag = () => {
@@ -57,7 +59,7 @@ const CreateTagModal: FC<Props> = ({ t, onClose, tagCreationLink, existingTagsLi
57
59
  apiClient
58
60
  .post(tagCreationLink, {
59
61
  revision,
60
- name: newTagName,
62
+ name: newTagName
61
63
  })
62
64
  .then(onCreated)
63
65
  .catch(onError)
@@ -83,10 +85,12 @@ const CreateTagModal: FC<Props> = ({ t, onClose, tagCreationLink, existingTagsLi
83
85
  <InputField
84
86
  name="name"
85
87
  label={t("tags.create.form.field.name.label")}
86
- onChange={(val) => setNewTagName(val)}
88
+ onChange={e => setNewTagName(e.target.value)}
87
89
  value={newTagName}
88
90
  validationError={!!validationError}
89
91
  errorMessage={t(validationError)}
92
+ onReturnPressed={() => !validationError && newTagName.length > 0 && createTag()}
93
+ ref={initialFocusRef}
90
94
  />
91
95
  <div className="mt-6">{t("tags.create.hint")}</div>
92
96
  </>
@@ -105,8 +109,9 @@ const CreateTagModal: FC<Props> = ({ t, onClose, tagCreationLink, existingTagsLi
105
109
  </>
106
110
  }
107
111
  closeFunction={onClose}
112
+ initialFocusRef={initialFocusRef}
108
113
  />
109
114
  );
110
115
  };
111
116
 
112
- export default withTranslation("repos")(CreateTagModal);
117
+ export default CreateTagModal;
@@ -22,7 +22,7 @@
22
22
  * SOFTWARE.
23
23
  */
24
24
  import * as React from "react";
25
- import { FC, ReactNode } from "react";
25
+ import { FC, MutableRefObject, ReactNode } from "react";
26
26
  import { useTranslation } from "react-i18next";
27
27
  import { Modal } from "./Modal";
28
28
  import Button from "../buttons/Button";
@@ -34,6 +34,7 @@ type Props = {
34
34
  body: ReactNode;
35
35
  active: boolean;
36
36
  closeButtonLabel?: string;
37
+ initialFocusRef?: MutableRefObject<HTMLElement | null>;
37
38
  };
38
39
 
39
40
  const FullSizedModal = styled(Modal)`
@@ -43,11 +44,22 @@ const FullSizedModal = styled(Modal)`
43
44
  }
44
45
  `;
45
46
 
46
- const FullscreenModal: FC<Props> = ({ title, closeFunction, body, active, closeButtonLabel }) => {
47
+ const FullscreenModal: FC<Props> = ({ title, closeFunction, body, active, initialFocusRef, closeButtonLabel }) => {
47
48
  const [t] = useTranslation("repos");
48
- const footer = <Button label={closeButtonLabel || t("diff.fullscreen.close")} action={closeFunction} color="secondary" />;
49
+ const footer = (
50
+ <Button label={closeButtonLabel || t("diff.fullscreen.close")} action={closeFunction} color="secondary" />
51
+ );
49
52
 
50
- return <FullSizedModal title={title} closeFunction={closeFunction} body={body} footer={footer} active={active} />;
53
+ return (
54
+ <FullSizedModal
55
+ title={title}
56
+ closeFunction={closeFunction}
57
+ body={body}
58
+ footer={footer}
59
+ active={active}
60
+ initialFocusRef={initialFocusRef}
61
+ />
62
+ );
51
63
  };
52
64
 
53
65
  export default FullscreenModal;
@@ -24,13 +24,13 @@
24
24
 
25
25
  import { storiesOf } from "@storybook/react";
26
26
  import { MemoryRouter } from "react-router-dom";
27
- import React, { useState, FC } from "react";
27
+ import React, { FC, useRef, useState } from "react";
28
28
  import Modal from "./Modal";
29
29
  import Checkbox from "../forms/Checkbox";
30
30
  import styled from "styled-components";
31
31
  import ExternalLink from "../navigation/ExternalLink";
32
- import { Radio, Textarea, InputField } from "../forms";
33
- import { ButtonGroup, Button } from "../buttons";
32
+ import { InputField, Radio, Textarea } from "../forms";
33
+ import { Button, ButtonGroup } from "../buttons";
34
34
  import Notification from "../Notification";
35
35
  import { Autocomplete } from "../index";
36
36
  import { SelectValue } from "@scm-manager/ui-types";
@@ -52,9 +52,8 @@ const text = `Mind-paralyzing change needed improbability vortex machine sorts s
52
52
  Kakrafoon humanoid undergarment ship powered by GPP-guided bowl of petunias nothing was frequently away incredibly
53
53
  ordinary mob.`;
54
54
 
55
- // eslint-disable-next-line @typescript-eslint/no-empty-function
56
55
  const doNothing = () => {
57
- // nothing to do
56
+ // Do nothing
58
57
  };
59
58
  const withFormElementsBody = (
60
59
  <>
@@ -114,6 +113,21 @@ storiesOf("Modal/Modal", module)
114
113
  footer={withFormElementsFooter}
115
114
  />
116
115
  ))
116
+ .add("With initial input field focus", () => {
117
+ const ref = useRef<HTMLInputElement | null>(null);
118
+ return (
119
+ <Modal
120
+ closeFunction={doNothing}
121
+ active={true}
122
+ title={"Hitchhiker Modal"}
123
+ footer={withFormElementsFooter}
124
+ initialFocusRef={ref}
125
+ >
126
+ <InputField ref={ref} />
127
+ </Modal>
128
+ );
129
+ })
130
+ .add("With initial button focus", () => <RefModal />)
117
131
  .add("With long tooltips", () => {
118
132
  return (
119
133
  <NonCloseableModal>
@@ -285,9 +299,20 @@ const NestedModal: FC = ({ children }) => {
285
299
  const [showOuter, setShowOuter] = useState(true);
286
300
  const [showInner, setShowInner] = useState(false);
287
301
  const outerBody = (
288
- <Button title="Open inner modal" className="button" action={() => setShowInner(true)}>
289
- Open inner modal
290
- </Button>
302
+ <>
303
+ {showInner && (
304
+ <Modal
305
+ body={children}
306
+ closeFunction={() => setShowInner(!showInner)}
307
+ active={showInner}
308
+ title="Inner Hitchhiker Modal"
309
+ />
310
+ )}
311
+
312
+ <Button title="Open inner modal" className="button" action={() => setShowInner(true)}>
313
+ Open inner modal
314
+ </Button>
315
+ </>
291
316
  );
292
317
 
293
318
  return (
@@ -301,14 +326,21 @@ const NestedModal: FC = ({ children }) => {
301
326
  size="M"
302
327
  />
303
328
  )}
304
- {showInner && (
305
- <Modal
306
- body={children}
307
- closeFunction={() => setShowInner(!showInner)}
308
- active={showInner}
309
- title="Inner Hitchhiker Modal"
310
- />
311
- )}
312
329
  </>
313
330
  );
314
331
  };
332
+
333
+ const RefModal = () => {
334
+ const ref = useRef<HTMLButtonElement>(null);
335
+ return (
336
+ <Modal
337
+ closeFunction={doNothing}
338
+ active={true}
339
+ title={"Hitchhiker Modal"}
340
+ footer={withFormElementsFooter}
341
+ initialFocusRef={ref}
342
+ >
343
+ <button ref={ref}>Hello</button>
344
+ </Modal>
345
+ );
346
+ };
@@ -21,12 +21,10 @@
21
21
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
22
  * SOFTWARE.
23
23
  */
24
- import React, { FC, KeyboardEvent, useRef } from "react";
24
+ import React, { FC, MutableRefObject, useRef } from "react";
25
25
  import classNames from "classnames";
26
- import usePortalRootElement from "../usePortalRootElement";
27
- import ReactDOM from "react-dom";
28
26
  import styled from "styled-components";
29
- import { useTrapFocus } from "../useTrapFocus";
27
+ import { Dialog } from "@headlessui/react";
30
28
 
31
29
  type ModalSize = "S" | "M" | "L";
32
30
 
@@ -35,13 +33,14 @@ const modalSizes: { [key in ModalSize]: number } = { S: 33, M: 50, L: 66 };
35
33
  type Props = {
36
34
  title: string;
37
35
  closeFunction: () => void;
38
- body: any;
36
+ body?: any;
39
37
  footer?: any;
40
38
  active: boolean;
41
39
  className?: string;
42
40
  headColor?: string;
43
41
  headTextColor?: string;
44
42
  size?: ModalSize;
43
+ initialFocusRef?: MutableRefObject<HTMLElement | null>;
45
44
  overflowVisible?: boolean;
46
45
  };
47
46
 
@@ -50,82 +49,67 @@ const SizedModal = styled.div<{ size?: ModalSize; overflow: string }>`
50
49
  overflow: ${props => props.overflow};
51
50
  `;
52
51
 
53
- const DivWithOptionalOverflow = styled.div<{ overflow: string; borderBottomRadius: string }>`
54
- overflow: ${props => props.overflow};
55
- border-bottom-left-radius: ${props => props.borderBottomRadius};
56
- border-bottom-right-radius: ${props => props.borderBottomRadius};
57
- `;
58
-
59
- const SectionWithOptionalOverflow = styled.section<{ overflow: string; borderBottomRadius: string }>`
60
- overflow: ${props => props.overflow};
61
- border-bottom-left-radius: ${props => props.borderBottomRadius};
62
- border-bottom-right-radius: ${props => props.borderBottomRadius};
63
- `;
64
-
65
52
  export const Modal: FC<Props> = ({
66
53
  title,
67
54
  closeFunction,
68
55
  body,
56
+ children,
69
57
  footer,
70
58
  active,
71
59
  className,
72
60
  headColor = "secondary-less",
73
61
  headTextColor = "secondary-most",
74
62
  size,
63
+ initialFocusRef,
75
64
  overflowVisible
76
65
  }) => {
77
- const portalRootElement = usePortalRootElement("modalsRoot");
78
- const initialFocusRef = useRef(null);
79
- const trapRef = useTrapFocus({
80
- includeContainer: true,
81
- initialFocus: initialFocusRef.current,
82
- returnFocus: true,
83
- updateNodes: false
84
- });
85
-
86
- if (!portalRootElement) {
87
- return null;
88
- }
89
-
90
- const isActive = active ? "is-active" : null;
91
-
66
+ const closeButtonRef = useRef<HTMLButtonElement | null>(null);
92
67
  let showFooter = null;
68
+
93
69
  if (footer) {
94
70
  showFooter = <footer className="modal-card-foot">{footer}</footer>;
95
71
  }
96
72
 
97
- const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
98
- if (closeFunction && "Escape" === event.key) {
99
- closeFunction();
100
- }
101
- };
102
-
103
73
  const overflowAttribute = overflowVisible ? "visible" : "auto";
104
74
  const borderBottomRadiusAttribute = overflowVisible && !footer ? "inherit" : "unset";
105
75
 
106
- const modalElement = (
107
- <DivWithOptionalOverflow
108
- className={classNames("modal", className, isActive)}
109
- ref={trapRef}
110
- onKeyDown={onKeyDown}
111
- overflow={overflowAttribute}
112
- borderBottomRadius={borderBottomRadiusAttribute}
76
+ return (
77
+ <Dialog
78
+ open={active}
79
+ onClose={closeFunction}
80
+ className={classNames(
81
+ "modal",
82
+ { "is-active": active },
83
+ `is-overflow-${overflowAttribute}`,
84
+ `is-border-bottom-radius-${borderBottomRadiusAttribute}`,
85
+ className
86
+ )}
87
+ initialFocus={initialFocusRef ?? closeButtonRef}
113
88
  >
114
- <div className="modal-background" onClick={closeFunction} />
89
+ <Dialog.Overlay className="modal-background" />
115
90
  <SizedModal className="modal-card" size={size} overflow={overflowAttribute}>
116
- <header className={classNames("modal-card-head", `has-background-${headColor}`)}>
91
+ <Dialog.Title as="header" className={classNames("modal-card-head", `has-background-${headColor}`)}>
117
92
  <h2 className={`modal-card-title m-0 has-text-${headTextColor}`}>{title}</h2>
118
- <button className="delete" aria-label="close" onClick={closeFunction} ref={initialFocusRef} autoFocus />
119
- </header>
120
- <SectionWithOptionalOverflow className="modal-card-body" overflow={overflowAttribute} borderBottomRadius={borderBottomRadiusAttribute}>
121
- {body}
122
- </SectionWithOptionalOverflow>
93
+ <button
94
+ className="delete"
95
+ aria-label="close"
96
+ onClick={closeFunction}
97
+ ref={!initialFocusRef ? closeButtonRef : undefined}
98
+ />
99
+ </Dialog.Title>
100
+ <section
101
+ className={classNames(
102
+ "modal-card-body",
103
+ `is-overflow-${overflowAttribute}`,
104
+ `is-border-bottom-radius-${borderBottomRadiusAttribute}`
105
+ )}
106
+ >
107
+ {body || children}
108
+ </section>
123
109
  {showFooter}
124
110
  </SizedModal>
125
- </DivWithOptionalOverflow>
111
+ </Dialog>
126
112
  );
127
-
128
- return ReactDOM.createPortal(modalElement, portalRootElement);
129
113
  };
130
114
 
131
115
  export default Modal;