@onesy/ui-react
Version:
UI for React
325 lines (321 loc) • 11.9 kB
JavaScript
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
const _excluded = ["ref", "tonal", "color", "size", "open", "openDefault", "mainRef", "backgroundRef", "partialyOpened", "minWidth", "maxWidth", "fullScreen", "fullWidth", "background", "modalWrapper", "modalWrapperSurface", "portal", "focus", "freezeScroll", "disableKeyboardClose", "disableBackgroundClose", "backgroundInvisible", "onClose", "NoSurfaceProps", "SurfaceProps", "BackgroundProps", "PortalProps", "TransitionComponentProps", "BackgroundComponent", "TransitionComponent", "Component", "className", "children"];
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
import React from 'react';
import { is, isEnvironment } from '@onesy/utils';
import { style as styleMethod, classNames, useOnesyTheme } from '@onesy/style-react';
import PortalElement from '../Portal';
import FocusElement from '../Focus';
import FadeElement from '../Fade';
import SurfaceElement from '../Surface';
import useMediaQuery from '../useMediaQuery';
import { staticClassName } from '../utils';
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
const useStyle = styleMethod(theme => ({
root: {
position: 'fixed',
inset: '0',
zIndex: theme.z_index.modal,
pointerEvents: 'none'
},
// Size
size_small: {
padding: theme.methods.space.value(2, 'px'),
borderRadius: `${theme.shape.radius.unit * 2 + theme.shape.radius.unit / 2}px`
},
size_regular: {
padding: theme.methods.space.value('md', 'px'),
borderRadius: `${theme.shape.radius.unit * 3 + theme.shape.radius.unit / 2}px`
},
size_large: {
padding: theme.methods.space.value('lg', 'px'),
borderRadius: `${theme.shape.radius.unit * 4 + theme.shape.radius.unit / 2}px`
},
background: {
position: 'absolute',
inset: '0',
width: '100%',
height: '100%',
background: 'rgba(0, 0, 0, 0.44)',
zIndex: -1,
pointerEvents: 'all'
},
backgroundInvisible: {
background: 'transparent'
},
modalRoot: {
position: 'relative',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
pointerEvents: 'none'
},
surface: {
display: 'flex',
flexDirection: 'column',
minWidth: `280px`,
maxHeight: `calc(100% - ${theme.methods.space.value('xl') * 2}px)`,
margin: theme.methods.space.value('xl', 'px'),
overflow: 'hidden',
pointerEvents: 'all'
},
noSurface: {
pointerEvents: 'all',
maxWidth: '100%'
},
fullScreen: {
width: '100%',
height: '100%',
borderRadius: '0 !important',
maxWidth: 'unset !important',
maxHeight: 'unset !important',
margin: '0 !important'
},
fullWidth: {
width: `calc(100% - ${theme.methods.space.value('xl') * 2}px)`
},
// minWidth
minWidth_xxs: {
minWidth: 'clamp(0px, 320px, calc(100vw - 48px))'
},
minWidth_xs: {
minWidth: 'clamp(0px, 400px, calc(100vw - 48px))'
},
minWidth_sm: {
minWidth: 'clamp(0px, 480px, calc(100vw - 48px))'
},
minWidth_rg: {
minWidth: 'clamp(0px, 560px, calc(100vw - 48px))'
},
minWidth_lg: {
minWidth: 'clamp(0px, 800px, calc(100vw - 48px))'
},
minWidth_xl: {
minWidth: 'clamp(0px, 1120px, calc(100vw - 48px))'
},
minWidth_xxl: {
minWidth: 'clamp(0px, 1360px, calc(100vw - 48px))'
},
minWidth_unset: {
minWidth: `unset`
},
// maxWidth
maxWidth_xxs: {
maxWidth: `320px`
},
maxWidth_xs: {
maxWidth: `400px`
},
maxWidth_sm: {
maxWidth: `480px`
},
maxWidth_rg: {
maxWidth: `560px`
},
maxWidth_lg: {
maxWidth: `800px`
},
maxWidth_xl: {
maxWidth: `1120px`
},
maxWidth_xxl: {
maxWidth: `1360px`
},
maxWidth_unset: {
maxWidth: `unset`
}
}), {
name: 'onesy-Modal'
});
let MODALS_OPEN = 0;
// To do
// 1. Add padding right for freezeScroll for the scroll bar if it exists and width that it is
const Modal = props_ => {
const theme = useOnesyTheme();
const props = _objectSpread(_objectSpread(_objectSpread({}, theme?.ui?.elements?.all?.props?.default), theme?.ui?.elements?.onesyModal?.props?.default), props_);
const Surface = theme?.elements?.Surface || SurfaceElement;
const Portal = theme?.elements?.Portal || PortalElement;
const Focus = theme?.elements?.Focus || FocusElement;
const Fade = theme?.elements?.Fade || FadeElement;
const {
ref,
tonal = true,
color = 'primary',
size = 'regular',
open: open_,
openDefault,
mainRef,
backgroundRef,
partialyOpened,
minWidth: minWidth_,
maxWidth: maxWidth_ = 'rg',
fullScreen,
fullWidth,
background = true,
modalWrapper = true,
modalWrapperSurface = true,
portal = true,
focus = true,
freezeScroll = true,
disableKeyboardClose,
disableBackgroundClose,
backgroundInvisible,
onClose: onClose_,
NoSurfaceProps,
SurfaceProps,
BackgroundProps,
PortalProps: PortalProps_,
TransitionComponentProps,
BackgroundComponent = Fade,
TransitionComponent = Fade,
Component = 'div',
className,
children
} = props,
other = _objectWithoutProperties(props, _excluded);
const {
classes
} = useStyle();
const [open, setOpen] = React.useState(openDefault !== undefined ? openDefault : open_);
const [inProp, setInProp] = React.useState(open_);
const [portalElement, setPortalElement] = React.useState();
const refs = {
root: React.useRef(undefined),
focus: React.useRef(undefined),
open: React.useRef(open),
freezeScroll: React.useRef(undefined),
interval: React.useRef(undefined)
};
const mobile = useMediaQuery('(max-width: 767px)', {
element: refs.root.current
});
const minWidth = minWidth_ !== undefined ? minWidth_ : !mobile ? 'sm' : undefined;
refs.open.current = open;
refs.freezeScroll.current = freezeScroll;
const modal = {
open: () => {
MODALS_OPEN++;
const rootDocument = isEnvironment('browser') ? refs.root.current?.ownerDocument || window.document : undefined;
if (freezeScroll) rootDocument.body.style.overflow = 'hidden';
},
close: () => {
MODALS_OPEN--;
const rootDocument_0 = isEnvironment('browser') ? refs.root.current?.ownerDocument || window.document : undefined;
if (MODALS_OPEN <= 0 && freezeScroll) rootDocument_0.body.style.removeProperty('overflow');
}
};
let maxWidth = maxWidth_;
if (fullWidth) maxWidth = undefined;
const onClose = () => {
if (is('function', onClose_) && open) onClose_();
};
const onKeyDown = event => {
if (!refs.open.current) return;
if (event.key === 'Escape' && !disableKeyboardClose) {
event.stopPropagation();
onClose();
}
};
React.useEffect(() => {
const rootDocument_1 = isEnvironment('browser') ? refs.root.current?.ownerDocument || window.document : undefined;
if (open) modal.open();
// Bug clean up fix
refs.interval.current = setInterval(() => {
if (MODALS_OPEN <= 0 && refs.freezeScroll.current && rootDocument_1.body.style.overflow === 'hidden') {
rootDocument_1.body.style.removeProperty('overflow');
}
}, 1400);
return () => {
if (open) modal.close();
clearInterval(refs.interval.current);
};
}, []);
React.useEffect(() => {
if (open_ && !open) {
setOpen(true);
setInProp(true);
modal.open();
} else if (!open_ && open) modalWrapper ? setInProp(false) : setOpen(false);
}, [open_]);
React.useEffect(() => {
if (open) refs.focus.current?.focus();
}, [open]);
const onExited = value => {
setOpen(false);
modal.close();
if (is('function', TransitionComponentProps?.onExited)) TransitionComponentProps?.onExited(value);
};
if (!open && !partialyOpened) return null;
const PortalComponent = portal ? Portal : React.Fragment;
let PortalProps = {};
if (portal) {
const rootDocumentElement = isEnvironment('browser') ? (portalElement || refs.root.current)?.ownerDocument || window.document : undefined;
if (isEnvironment('browser')) PortalProps.element = rootDocumentElement.body;
PortalProps = _objectSpread(_objectSpread({}, PortalProps), PortalProps_);
}
const FocusComponent = focus ? Focus : React.Fragment;
const FocusProps = focus ? {
ref: refs.focus,
onKeyDown
} : {};
const MainProps = {
role: 'dialog',
'aria-labelledby': 'onesy-modal-title',
'aria-describedby': 'onesy-modal-text',
'aria-modal': 'true',
'aria-live': 'assertive'
};
let Main = children && /*#__PURE__*/React.cloneElement(children, _objectSpread({}, MainProps));
if (modalWrapper) Main = /*#__PURE__*/_jsx("div", _objectSpread(_objectSpread({}, MainProps), {}, {
className: classNames([staticClassName('Modal', theme) && ['onesy-Modal-modal-root'], classes.modalRoot]),
children: /*#__PURE__*/_jsx(TransitionComponent, _objectSpread(_objectSpread({
in: inProp
}, TransitionComponentProps), {}, {
onExited: onExited,
add: true,
children: !modalWrapperSurface ? /*#__PURE__*/_jsx("div", {
className: classNames([staticClassName('Modal', theme) && ['onesy-Modal-no-surface'], NoSurfaceProps?.className, classes.noSurface]),
children: children
}) : /*#__PURE__*/_jsx(Surface, _objectSpread(_objectSpread({
ref: mainRef,
tonal: tonal,
color: color,
tabIndex: "-1"
}, SurfaceProps), {}, {
className: classNames([staticClassName('Modal', theme) && ['onesy-Modal-surface', `onesy-Modal-size-${size}`], classes.surface, classes[`minWidth_${minWidth}`], classes[`maxWidth_${maxWidth}`], classes[`size_${size}`], fullScreen && classes.fullScreen, fullWidth && classes.fullWidth, SurfaceProps?.className]),
onKeyDown: onKeyDown,
children: children
}))
}))
}));
return /*#__PURE__*/_jsx(PortalComponent, _objectSpread(_objectSpread({}, PortalProps), {}, {
children: /*#__PURE__*/_jsx(Component, _objectSpread(_objectSpread({
ref: item => {
if (ref) {
if (is('function', ref)) ref(item);else ref.current = item;
}
refs.root.current = item;
setPortalElement(item);
},
className: classNames([staticClassName('Modal', theme) && ['onesy-Modal-root', open && `onesy-Modal-open`, focus && `onesy-Modal-focus`], className, classes.root])
}, other), {}, {
children: /*#__PURE__*/_jsxs(FocusComponent, _objectSpread(_objectSpread({}, FocusProps), {}, {
children: [background && /*#__PURE__*/_jsx(BackgroundComponent, _objectSpread(_objectSpread({
in: inProp,
add: true
}, BackgroundProps), {}, {
children: /*#__PURE__*/_jsx("div", {
ref: backgroundRef,
onClick: () => !disableBackgroundClose && onClose(),
className: classNames([staticClassName('Modal', theme) && ['onesy-Modal-background', backgroundInvisible && 'onesy-Modal-background-invisible'], classes.background, backgroundInvisible && classes.backgroundInvisible])
})
})), Main]
}))
}))
}));
};
Modal.displayName = 'onesy-Modal';
export default Modal;