UNPKG

@primer/react

Version:

An implementation of GitHub's Primer Design System using React

572 lines (565 loc) • 15.7 kB
'use strict'; var reactCompilerRuntime = require('react-compiler-runtime'); var React = require('react'); var IconButton = require('../Button/IconButton.js'); var Button = require('../Button/Button.js'); var useFocusTrap = require('../hooks/useFocusTrap.js'); var octiconsReact = require('@primer/octicons-react'); var useFocusZone = require('../hooks/useFocusZone.js'); var behaviors = require('@primer/behaviors'); var Portal = require('../Portal/Portal.js'); var useRefObjectAsForwardedRef = require('../hooks/useRefObjectAsForwardedRef.js'); var useId = require('../hooks/useId.js'); var Dialog_module = require('./Dialog.module.css.js'); var clsx = require('clsx'); var BoxWithFallback = require('../internal/components/BoxWithFallback.js'); var jsxRuntime = require('react/jsx-runtime'); var useOnEscapePress = require('../hooks/useOnEscapePress.js'); var ScrollableRegion = require('../ScrollableRegion/ScrollableRegion.js'); var useProvidedRefOrCreate = require('../hooks/useProvidedRefOrCreate.js'); var Box = require('../Box/Box.js'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var React__default = /*#__PURE__*/_interopDefault(React); const DefaultHeader = t0 => { const $ = reactCompilerRuntime.c(16); const { dialogLabelId, title, subtitle, dialogDescriptionId, onClose } = t0; let t1; if ($[0] !== onClose) { t1 = () => { onClose("close-button"); }; $[0] = onClose; $[1] = t1; } else { t1 = $[1]; } const onCloseClick = t1; const t2 = title !== null && title !== void 0 ? title : "Dialog"; let t3; if ($[2] !== dialogLabelId || $[3] !== t2) { t3 = /*#__PURE__*/jsxRuntime.jsx(Dialog.Title, { id: dialogLabelId, children: t2 }); $[2] = dialogLabelId; $[3] = t2; $[4] = t3; } else { t3 = $[4]; } let t4; if ($[5] !== dialogDescriptionId || $[6] !== subtitle) { t4 = subtitle && /*#__PURE__*/jsxRuntime.jsx(Dialog.Subtitle, { id: dialogDescriptionId, children: subtitle }); $[5] = dialogDescriptionId; $[6] = subtitle; $[7] = t4; } else { t4 = $[7]; } let t5; if ($[8] !== t3 || $[9] !== t4) { t5 = /*#__PURE__*/jsxRuntime.jsxs(Box, { display: "flex", px: 2, py: "6px", flexDirection: "column", flexGrow: 1, children: [t3, t4] }); $[8] = t3; $[9] = t4; $[10] = t5; } else { t5 = $[10]; } let t6; if ($[11] !== onCloseClick) { t6 = /*#__PURE__*/jsxRuntime.jsx(Dialog.CloseButton, { onClose: onCloseClick }); $[11] = onCloseClick; $[12] = t6; } else { t6 = $[12]; } let t7; if ($[13] !== t5 || $[14] !== t6) { t7 = /*#__PURE__*/jsxRuntime.jsx(Dialog.Header, { children: /*#__PURE__*/jsxRuntime.jsxs(Box, { display: "flex", children: [t5, t6] }) }); $[13] = t5; $[14] = t6; $[15] = t7; } else { t7 = $[15]; } return t7; }; const DefaultBody = t0 => { const $ = reactCompilerRuntime.c(2); const { children } = t0; let t1; if ($[0] !== children) { t1 = /*#__PURE__*/jsxRuntime.jsx(Dialog.Body, { children: children }); $[0] = children; $[1] = t1; } else { t1 = $[1]; } return t1; }; const DefaultFooter = t0 => { const $ = reactCompilerRuntime.c(4); const { footerButtons } = t0; let t1; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t1 = { bindKeys: behaviors.FocusKeys.ArrowHorizontal | behaviors.FocusKeys.Tab, focusInStrategy: "closest" }; $[0] = t1; } else { t1 = $[0]; } const { containerRef: footerRef } = useFocusZone.useFocusZone(t1); let t2; if ($[1] !== footerButtons || $[2] !== footerRef) { t2 = footerButtons ? /*#__PURE__*/jsxRuntime.jsx(Dialog.Footer, { ref: footerRef, children: /*#__PURE__*/jsxRuntime.jsx(Dialog.Buttons, { buttons: footerButtons }) }) : null; $[1] = footerButtons; $[2] = footerRef; $[3] = t2; } else { t2 = $[3]; } return t2; }; const defaultPosition = { narrow: 'center', regular: 'center' }; const defaultFooterButtons = []; const _Dialog = /*#__PURE__*/React__default.default.forwardRef((props, forwardedRef) => { const { title = 'Dialog', subtitle = '', renderHeader, renderBody, renderFooter, onClose, role = 'dialog', width = 'xlarge', height = 'auto', footerButtons = defaultFooterButtons, position = defaultPosition, returnFocusRef, initialFocusRef, sx, className } = props; const dialogLabelId = useId.useId(); const dialogDescriptionId = useId.useId(); const autoFocusedFooterButtonRef = React.useRef(null); for (const footerButton of footerButtons) { if (footerButton.autoFocus) { // eslint-disable-next-line react-compiler/react-compiler footerButton.ref = autoFocusedFooterButtonRef; } } const [lastMouseDownIsBackdrop, setLastMouseDownIsBackdrop] = React.useState(false); const defaultedProps = { ...props, title, subtitle, role, dialogLabelId, dialogDescriptionId }; const onBackdropClick = React.useCallback(e => { if (e.target === e.currentTarget && lastMouseDownIsBackdrop) { onClose('escape'); } }, [onClose, lastMouseDownIsBackdrop]); const dialogRef = React.useRef(null); useRefObjectAsForwardedRef.useRefObjectAsForwardedRef(forwardedRef, dialogRef); const backdropRef = React.useRef(null); useFocusTrap.useFocusTrap({ containerRef: dialogRef, initialFocusRef: initialFocusRef !== null && initialFocusRef !== void 0 ? initialFocusRef : autoFocusedFooterButtonRef, restoreFocusOnCleanUp: returnFocusRef !== null && returnFocusRef !== void 0 && returnFocusRef.current ? false : true, returnFocusRef }); useOnEscapePress.useOnEscapePress(event => { onClose('escape'); event.preventDefault(); }, [onClose]); React__default.default.useEffect(() => { var _dialogRef$current; const scrollbarWidth = window.innerWidth - document.body.clientWidth; // If the dialog is rendered, we add a class to the dialog element to disable (_dialogRef$current = dialogRef.current) === null || _dialogRef$current === void 0 ? void 0 : _dialogRef$current.classList.add(Dialog_module.DisableScroll); // and set a CSS variable to the scrollbar width so that the dialog can // account for the scrollbar width when calculating its width. document.body.style.setProperty('--prc-dialog-scrollgutter', `${scrollbarWidth}px`); }, []); const header = (renderHeader !== null && renderHeader !== void 0 ? renderHeader : DefaultHeader)(defaultedProps); const body = (renderBody !== null && renderBody !== void 0 ? renderBody : DefaultBody)(defaultedProps); const footer = (renderFooter !== null && renderFooter !== void 0 ? renderFooter : DefaultFooter)(defaultedProps); const positionDataAttributes = typeof position === 'string' ? { 'data-position-regular': position } : Object.fromEntries(Object.entries(position).map(([key, value]) => { return [`data-position-${key}`, value]; })); return /*#__PURE__*/jsxRuntime.jsx(jsxRuntime.Fragment, { children: /*#__PURE__*/jsxRuntime.jsx(Portal.Portal, { children: /*#__PURE__*/jsxRuntime.jsx(BoxWithFallback.BoxWithFallback, { as: "div", ref: backdropRef, className: Dialog_module.Backdrop, ...positionDataAttributes, onClick: onBackdropClick, onMouseDown: e_0 => { setLastMouseDownIsBackdrop(e_0.target === e_0.currentTarget); }, children: /*#__PURE__*/jsxRuntime.jsxs(BoxWithFallback.BoxWithFallback, { as: "div", ref: dialogRef, role: role, "aria-labelledby": dialogLabelId, "aria-describedby": dialogDescriptionId, "aria-modal": true, ...positionDataAttributes, "data-width": width, "data-height": height, sx: sx, className: clsx.clsx(className, Dialog_module.Dialog), children: [header, /*#__PURE__*/jsxRuntime.jsx(ScrollableRegion.ScrollableRegion, { "aria-labelledby": dialogLabelId, className: Dialog_module.DialogOverflowWrapper, children: body }), footer] }) }) }) }); }); _Dialog.displayName = 'Dialog'; const Header = /*#__PURE__*/React__default.default.forwardRef(function Header(t0, forwardRef) { const $ = reactCompilerRuntime.c(9); let className; let rest; if ($[0] !== t0) { ({ className, ...rest } = t0); $[0] = t0; $[1] = className; $[2] = rest; } else { className = $[1]; rest = $[2]; } let t1; if ($[3] !== className) { t1 = clsx.clsx(className, Dialog_module.Header); $[3] = className; $[4] = t1; } else { t1 = $[4]; } let t2; if ($[5] !== forwardRef || $[6] !== rest || $[7] !== t1) { t2 = /*#__PURE__*/jsxRuntime.jsx(BoxWithFallback.BoxWithFallback, { as: "div", ref: forwardRef, className: t1, ...rest }); $[5] = forwardRef; $[6] = rest; $[7] = t1; $[8] = t2; } else { t2 = $[8]; } return t2; }); Header.displayName = 'Dialog.Header'; const Title = /*#__PURE__*/React__default.default.forwardRef(function Title(t0, forwardRef) { const $ = reactCompilerRuntime.c(9); let className; let rest; if ($[0] !== t0) { ({ className, ...rest } = t0); $[0] = t0; $[1] = className; $[2] = rest; } else { className = $[1]; rest = $[2]; } let t1; if ($[3] !== className) { t1 = clsx.clsx(className, Dialog_module.Title); $[3] = className; $[4] = t1; } else { t1 = $[4]; } let t2; if ($[5] !== forwardRef || $[6] !== rest || $[7] !== t1) { t2 = /*#__PURE__*/jsxRuntime.jsx(BoxWithFallback.BoxWithFallback, { as: "h1", ref: forwardRef, className: t1, ...rest }); $[5] = forwardRef; $[6] = rest; $[7] = t1; $[8] = t2; } else { t2 = $[8]; } return t2; }); Title.displayName = 'Dialog.Title'; const Subtitle = /*#__PURE__*/React__default.default.forwardRef(function Subtitle(t0, forwardRef) { const $ = reactCompilerRuntime.c(9); let className; let rest; if ($[0] !== t0) { ({ className, ...rest } = t0); $[0] = t0; $[1] = className; $[2] = rest; } else { className = $[1]; rest = $[2]; } let t1; if ($[3] !== className) { t1 = clsx.clsx(className, Dialog_module.Subtitle); $[3] = className; $[4] = t1; } else { t1 = $[4]; } let t2; if ($[5] !== forwardRef || $[6] !== rest || $[7] !== t1) { t2 = /*#__PURE__*/jsxRuntime.jsx(BoxWithFallback.BoxWithFallback, { as: "h2", ref: forwardRef, className: t1, ...rest }); $[5] = forwardRef; $[6] = rest; $[7] = t1; $[8] = t2; } else { t2 = $[8]; } return t2; }); Subtitle.displayName = 'Dialog.Subtitle'; const Body = /*#__PURE__*/React__default.default.forwardRef(function Body(t0, forwardRef) { const $ = reactCompilerRuntime.c(9); let className; let rest; if ($[0] !== t0) { ({ className, ...rest } = t0); $[0] = t0; $[1] = className; $[2] = rest; } else { className = $[1]; rest = $[2]; } let t1; if ($[3] !== className) { t1 = clsx.clsx(className, Dialog_module.Body); $[3] = className; $[4] = t1; } else { t1 = $[4]; } let t2; if ($[5] !== forwardRef || $[6] !== rest || $[7] !== t1) { t2 = /*#__PURE__*/jsxRuntime.jsx(BoxWithFallback.BoxWithFallback, { as: "div", ref: forwardRef, className: t1, ...rest }); $[5] = forwardRef; $[6] = rest; $[7] = t1; $[8] = t2; } else { t2 = $[8]; } return t2; }); Body.displayName = 'Dialog.Body'; const Footer = /*#__PURE__*/React__default.default.forwardRef(function Footer(t0, forwardRef) { const $ = reactCompilerRuntime.c(9); let className; let rest; if ($[0] !== t0) { ({ className, ...rest } = t0); $[0] = t0; $[1] = className; $[2] = rest; } else { className = $[1]; rest = $[2]; } let t1; if ($[3] !== className) { t1 = clsx.clsx(className, Dialog_module.Footer); $[3] = className; $[4] = t1; } else { t1 = $[4]; } let t2; if ($[5] !== forwardRef || $[6] !== rest || $[7] !== t1) { t2 = /*#__PURE__*/jsxRuntime.jsx(BoxWithFallback.BoxWithFallback, { as: "div", ref: forwardRef, className: t1, ...rest }); $[5] = forwardRef; $[6] = rest; $[7] = t1; $[8] = t2; } else { t2 = $[8]; } return t2; }); Footer.displayName = 'Dialog.Footer'; const Buttons = ({ buttons }) => { var _buttons$find; const autoFocusRef = useProvidedRefOrCreate.useProvidedRefOrCreate((_buttons$find = buttons.find(button => button.autoFocus)) === null || _buttons$find === void 0 ? void 0 : _buttons$find.ref); let autoFocusCount = 0; const [hasRendered, setHasRendered] = React.useState(0); React.useEffect(() => { // hack to work around dialogs originating from other focus traps. if (hasRendered === 1) { var _autoFocusRef$current; (_autoFocusRef$current = autoFocusRef.current) === null || _autoFocusRef$current === void 0 ? void 0 : _autoFocusRef$current.focus(); } else { setHasRendered(hasRendered + 1); } }, [autoFocusRef, hasRendered]); return /*#__PURE__*/jsxRuntime.jsx(jsxRuntime.Fragment, { children: buttons.map((dialogButtonProps, index) => { const { content, buttonType = 'default', autoFocus = false, ...buttonProps } = dialogButtonProps; return /*#__PURE__*/jsxRuntime.jsx(Button.ButtonComponent, { ...buttonProps, // 'normal' value is equivalent to 'default', this is used for backwards compatibility variant: buttonType === 'normal' ? 'default' : buttonType, ref: autoFocus && autoFocusCount === 0 ? (autoFocusCount++, autoFocusRef) : null, children: content }, index); }) }); }; const CloseButton = t0 => { const $ = reactCompilerRuntime.c(2); const { onClose } = t0; let t1; if ($[0] !== onClose) { t1 = /*#__PURE__*/jsxRuntime.jsx(IconButton.IconButton, { icon: octiconsReact.XIcon, "aria-label": "Close", onClick: onClose, variant: "invisible" }); $[0] = onClose; $[1] = t1; } else { t1 = $[1]; } return t1; }; /** * A dialog is a type of overlay that can be used for confirming actions, asking * for disambiguation, and presenting small forms. They generally allow the user * to focus on a quick task without having to navigate to a different page. * * Dialogs appear in the page after a direct user interaction. Don't show dialogs * on page load or as system alerts. * * Dialogs appear centered in the page, with a visible backdrop that dims the rest * of the window for focus. * * All dialogs have a title and a close button. * * Dialogs are modal. Dialogs can be dismissed by clicking on the close button, * pressing the escape key, or by interacting with another button in the dialog. * To avoid losing information and missing important messages, clicking outside * of the dialog will not close it. * * The sub components provided (e.g. Header, Title, etc.) are available for custom * renderers only. They are not intended to be used otherwise. */ const Dialog = Object.assign(_Dialog, { Header, Title, Subtitle, Body, Footer, Buttons, CloseButton }); exports.Dialog = Dialog;