@clayui/modal
Version:
ClayModal component
168 lines (166 loc) • 4.38 kB
JavaScript
import { ClayPortal, stack } from "@clayui/shared";
import { suppressOthers } from "aria-hidden";
import classNames from "classnames";
import React, { useEffect, useMemo, useRef } from "react";
import warning from "warning";
import Body from "./Body";
import Context from "./Context";
import Footer from "./Footer";
import Header, {
Item,
ItemGroup,
Subtitle,
SubtitleSection,
Title,
TitleIndicator,
TitleSection
} from "./Header";
import { useUserInteractions } from "./Hook";
import { ObserverType } from "./types";
const warningMessage = `You need to pass the 'observer' prop to ClayModal for everything to work fine, use the 'useModal' hook that exposes the observer.
> const {observer} = useModal({...});
>
> return (
> <ClayModal observer={observer}>
> ...
> </ClayModal>
> );
`;
let counter = 0;
function Modal({
center,
children,
className,
containerElementRef,
containerProps = {},
disableAutoClose = false,
observer,
role = "dialog",
size,
spritemap,
status,
zIndex,
...otherProps
}) {
const modalElementRef = useRef(null);
const modalBodyElementRef = useRef(null);
const [show, content] = observer && observer.mutation ? observer.mutation : [false, false];
warning(observer !== void 0, warningMessage);
useUserInteractions(
modalElementRef,
modalBodyElementRef,
() => !disableAutoClose && observer.dispatch(ObserverType.Close),
show,
content
);
useEffect(() => {
observer.dispatch(ObserverType.RestoreFocus, document.activeElement);
observer.dispatch(ObserverType.Open);
}, []);
useEffect(() => {
if (modalBodyElementRef.current && show && content) {
const focusedElement = modalBodyElementRef.current.querySelector("h1");
if (focusedElement) {
focusedElement.focus();
} else {
modalBodyElementRef.current.focus();
}
}
}, [show, content]);
const ariaLabelledby = useMemo(() => {
counter++;
return `clay-modal-label-${counter}`;
}, []);
useEffect(() => {
if (show && content) {
stack.push(modalElementRef);
}
return () => {
const index = stack.indexOf(modalElementRef);
if (index >= 0) {
stack.splice(index, 1);
}
};
}, [show, modalElementRef, content]);
useEffect(() => {
if (modalElementRef.current && show && stack[stack.length - 1] === modalElementRef) {
return suppressOthers(modalElementRef.current);
}
}, [show]);
return /* @__PURE__ */ React.createElement(
ClayPortal,
{
...containerProps,
containerRef: containerElementRef,
subPortalRef: modalElementRef
},
/* @__PURE__ */ React.createElement(
"div",
{
"aria-hidden": "true",
className: classNames("modal-backdrop fade", {
show
}),
style: { zIndex }
}
),
/* @__PURE__ */ React.createElement(
"div",
{
...otherProps,
className: classNames("fade modal d-block", className, {
show
}),
ref: modalElementRef,
style: { zIndex: zIndex && zIndex + 10 }
},
/* @__PURE__ */ React.createElement(
"div",
{
className: classNames("modal-dialog", {
[`modal-${size}`]: size,
[`modal-${status}`]: status,
"modal-dialog-centered": center
})
},
/* @__PURE__ */ React.createElement(
"div",
{
"aria-labelledby": ariaLabelledby,
"aria-modal": "true",
className: "modal-content",
ref: modalBodyElementRef,
role,
tabIndex: -1
},
/* @__PURE__ */ React.createElement(
Context.Provider,
{
value: {
ariaLabelledby,
onClose: () => observer.dispatch(ObserverType.Close),
spritemap,
status
}
},
content && children
)
)
)
)
);
}
Modal.Body = Body;
Modal.Footer = Footer;
Modal.Header = Header;
Modal.Item = Item;
Modal.ItemGroup = ItemGroup;
Modal.Subtitle = Subtitle;
Modal.SubtitleSection = SubtitleSection;
Modal.Title = Title;
Modal.TitleIndicator = TitleIndicator;
Modal.TitleSection = TitleSection;
var Modal_default = Modal;
export {
Modal_default as default
};