@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
418 lines (417 loc) • 14.8 kB
JavaScript
"use client";
import _extends from "@babel/runtime/helpers/esm/extends";
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
const _excluded = ["root_id", "content_id", "disabled", "labelled_by", "focus_selector", "header_content", "bar_content", "bypass_invalidation_selectors", "vertical_alignment", "id", "open_delay", "omit_trigger_button", "trigger", "trigger_attributes"];
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 classnames from 'classnames';
import { SuffixContext } from '../../shared/helpers/Suffix';
import Context from '../../shared/Context';
import { warn, isTrue, makeUniqueId, extendPropsWithContextInClassComponent, processChildren, dispatchCustomElementEvent } from '../../shared/component-helper';
import { createSpacingClasses } from '../space/SpacingHelper';
import HelpButtonInstance from '../help-button/HelpButtonInstance';
import { getListOfModalRoots, getModalRoot } from './helpers';
import ModalInner from './parts/ModalInner';
import ModalHeader from './parts/ModalHeader';
import ModalHeaderBar from './parts/ModalHeaderBar';
import CloseButton from './parts/CloseButton';
import ModalRoot from './ModalRoot';
import { classWithCamelCaseProps } from '../../shared/helpers/withCamelCaseProps';
export const ANIMATION_DURATION = 300;
class Modal extends React.PureComponent {
static getContent(props) {
if (typeof props.modal_content === 'string') {
return props.modal_content;
} else if (typeof props.modal_content === 'function') {
return props.modal_content(props);
}
return processChildren(props);
}
static getDerivedStateFromProps(props, state) {
if (typeof window !== 'undefined' && window['IS_TEST']) {
state.animation_duration = 0;
state.no_animation = true;
} else {
state.animation_duration = props.animation_duration;
state.no_animation = props.no_animation;
}
if (props.open_state !== state._open_state) {
switch (props.open_state) {
case 'opened':
case true:
state.hide = false;
if (isTrue(state.no_animation)) {
state.modalActive = true;
}
break;
case 'closed':
case false:
state.hide = true;
if (isTrue(state.no_animation)) {
state.modalActive = false;
}
break;
}
}
state._open_state = props.open_state;
return state;
}
constructor(props) {
super(props);
_defineProperty(this, "context", void 0);
_defineProperty(this, "_id", void 0);
_defineProperty(this, "_triggerRef", void 0);
_defineProperty(this, "_onUnmount", void 0);
_defineProperty(this, "_openTimeout", void 0);
_defineProperty(this, "_closeTimeout", void 0);
_defineProperty(this, "_sideEffectsTimeout", void 0);
_defineProperty(this, "_tryToOpenTimeout", void 0);
_defineProperty(this, "activeElement", void 0);
_defineProperty(this, "isInTransition", void 0);
_defineProperty(this, "modalContentCloseRef", void 0);
_defineProperty(this, "state", {
hide: false,
modalActive: false,
preventAutoFocus: true,
animation_duration: ANIMATION_DURATION,
no_animation: false
});
_defineProperty(this, "toggleOpenClose", (event = null, showModal = null) => {
if (event && event.preventDefault) {
event.preventDefault();
}
const toggleNow = () => {
const {
animation_duration = ANIMATION_DURATION,
no_animation = false
} = this.state;
const timeoutDuration = typeof animation_duration === 'string' ? parseFloat(animation_duration) : animation_duration;
const modalActive = typeof showModal === 'boolean' ? showModal : !this.state.modalActive;
this.isInTransition = true;
const doItNow = () => {
this.setState({
hide: false,
modalActive
}, () => {
this.isInTransition = false;
this.handleSideEffects();
});
};
if (modalActive === false && !isTrue(no_animation)) {
this.setState({
hide: true
});
this._closeTimeout = setTimeout(doItNow, timeoutDuration);
} else {
doItNow();
}
};
const waitBeforeOpen = () => {
const {
open_delay
} = this.props;
const {
no_animation
} = this.state;
const delay = typeof open_delay === 'string' ? parseFloat(open_delay) : open_delay;
if (delay > 0 && !isTrue(no_animation)) {
this._openTimeout = setTimeout(toggleNow, delay);
} else {
toggleNow();
}
};
clearTimeout(this._closeTimeout);
clearTimeout(this._openTimeout);
const {
open_modal
} = this.props;
if (typeof open_modal === 'function') {
const fn = open_modal(waitBeforeOpen, this);
if (fn) {
this._onUnmount.push(fn);
}
} else {
waitBeforeOpen();
}
});
_defineProperty(this, "handleSideEffects", () => {
const {
modalActive,
preventAutoFocus,
animation_duration
} = this.state;
const {
close_modal,
open_state
} = this.props;
if (modalActive) {
if (typeof close_modal === 'function') {
const fn = close_modal(() => {
this.toggleOpenClose(null, false);
}, this);
if (fn) {
this._onUnmount.push(fn);
}
}
this.setActiveState(this._id);
} else if (modalActive === false && !preventAutoFocus) {
var _this$_triggerRef;
const focus = elem => {
elem.setAttribute('data-autofocus', 'true');
elem.focus({
preventScroll: true
});
return new Promise(resolve => {
setTimeout(() => {
elem === null || elem === void 0 ? void 0 : elem.removeAttribute('data-autofocus');
resolve();
}, parseFloat(String(animation_duration)) / 3);
});
};
if ((_this$_triggerRef = this._triggerRef) !== null && _this$_triggerRef !== void 0 && _this$_triggerRef.current) {
focus(this._triggerRef.current);
}
if ((open_state === 'opened' || open_state === true) && this.activeElement instanceof HTMLElement) {
try {
focus(this.activeElement).then(() => {
this.activeElement = null;
});
} catch (e) {}
}
this.removeActiveState();
}
if (preventAutoFocus) {
this.setState({
preventAutoFocus: false
});
}
});
_defineProperty(this, "open", e => {
this.toggleOpenClose(e, true);
});
_defineProperty(this, "close", (event, {
ifIsLatest,
triggeredBy = 'handler'
} = {
ifIsLatest: true
}) => {
var _this$modalContentClo, _this$modalContentClo2;
(_this$modalContentClo = (_this$modalContentClo2 = this.modalContentCloseRef).current) === null || _this$modalContentClo === void 0 ? void 0 : _this$modalContentClo.call(_this$modalContentClo2, event, {
triggeredBy
});
const {
prevent_close = false
} = this.props;
if (isTrue(prevent_close)) {
const id = this._id;
dispatchCustomElementEvent(this, 'on_close_prevent', {
id,
event,
triggeredBy,
close: e => {
this.toggleOpenClose(e, false);
}
});
} else {
if (ifIsLatest) {
const list = getListOfModalRoots();
if (list.length > 1) {
const last = getModalRoot(-1);
if (last !== this) {
return;
}
}
}
this.toggleOpenClose(event, false);
}
});
this._id = props.id || makeUniqueId('modal-');
this._triggerRef = React.createRef();
this.modalContentCloseRef = React.createRef();
this._onUnmount = [];
}
componentDidMount() {
this.openBasedOnStateUpdate();
}
componentWillUnmount() {
clearTimeout(this._openTimeout);
clearTimeout(this._closeTimeout);
this.removeActiveState();
this._onUnmount.forEach(fn => {
if (typeof fn === 'function') {
fn();
}
});
}
componentDidUpdate(prevProps) {
if (prevProps !== this.props) {
this.openBasedOnStateUpdate();
}
}
openBasedOnStateUpdate() {
const {
hide
} = this.state;
const {
open_state
} = this.props;
if (!this.activeElement && typeof document !== 'undefined') {
this.activeElement = document.activeElement;
}
if (!hide && (open_state === 'opened' || open_state === true)) {
this.toggleOpenClose(null, true);
} else if (hide && (open_state === 'closed' || open_state === false)) {
this.toggleOpenClose(null, false);
}
}
removeActiveState() {
const last = getModalRoot(-1);
if (last !== null && last !== void 0 && last._id && last._id !== this._id) {
return this.setActiveState(last._id);
}
try {
document.documentElement.removeAttribute('data-dnb-modal-active');
} catch (e) {
warn('Modal: Error on remove "data-dnb-modal-active"', e);
}
}
setActiveState(modalId) {
if (!modalId) {
warn('Modal: A valid modalId is required');
}
if (typeof document !== 'undefined') {
try {
document.documentElement.setAttribute('data-dnb-modal-active', modalId);
} catch (e) {
warn('Modal: Error on set "data-dnb-modal-active"', e);
}
}
}
render() {
const visualTestsPropsOverride = typeof window !== 'undefined' && window['IS_TEST'] ? {
animation_duration: 0,
no_animation: true
} : {};
const props = extendPropsWithContextInClassComponent(this.props, Modal.defaultProps, this.context.getTranslation(this.props).Modal, this.context.Modal, visualTestsPropsOverride);
const {
root_id = 'root',
content_id = null,
disabled = null,
labelled_by = null,
focus_selector = null,
header_content = null,
bar_content = null,
bypass_invalidation_selectors = null,
vertical_alignment = 'center',
id,
open_delay,
omit_trigger_button = false,
trigger = null,
trigger_attributes = null
} = props,
rest = _objectWithoutProperties(props, _excluded);
const {
hide,
modalActive
} = this.state;
const modal_content = Modal.getContent(typeof this.props.children === 'function' ? Object.freeze(_objectSpread(_objectSpread({}, this.props), {}, {
close: this.close
})) : this.props);
const render = suffixProps => {
const triggerAttributes = _objectSpread({
hidden: false,
variant: 'secondary',
icon_position: 'left'
}, trigger_attributes);
if (isTrue(disabled)) {
triggerAttributes.disabled = true;
}
if (triggerAttributes.id) {
this._id = triggerAttributes.id;
}
let fallbackTitle;
if (triggerAttributes.title) {
fallbackTitle = triggerAttributes.title;
} else if (suffixProps) {
fallbackTitle = this.context.translation.HelpButton.title;
}
const TriggerButton = trigger ? trigger : HelpButtonInstance;
const title = !triggerAttributes.text ? rest.title || fallbackTitle : null;
return React.createElement(React.Fragment, null, TriggerButton && !isTrue(omit_trigger_button) && React.createElement(TriggerButton, _extends({}, triggerAttributes, {
id: this._id,
title: title,
onClick: this.toggleOpenClose,
innerRef: this._triggerRef,
className: classnames('dnb-modal__trigger', createSpacingClasses(props), triggerAttributes.className, triggerAttributes.class)
})), modalActive && modal_content && React.createElement(ModalRoot, _extends({}, rest, {
id: this._id,
root_id: root_id,
content_id: content_id || `dnb-modal-${this._id}`,
labelled_by: labelled_by,
focus_selector: focus_selector,
modal_content: modal_content,
header_content: header_content,
vertical_alignment: vertical_alignment,
bar_content: bar_content,
bypass_invalidation_selectors: bypass_invalidation_selectors,
close: this.close,
hide: hide,
title: rest.title || fallbackTitle,
modalContentCloseRef: this.modalContentCloseRef
})));
};
return React.createElement(SuffixContext.Consumer, null, render);
}
}
_defineProperty(Modal, "contextType", Context);
_defineProperty(Modal, "Bar", ModalHeaderBar);
_defineProperty(Modal, "Header", ModalHeader);
_defineProperty(Modal, "Content", ModalInner);
_defineProperty(Modal, "defaultProps", {
id: null,
focus_selector: null,
labelled_by: null,
title: null,
disabled: null,
spacing: true,
open_delay: null,
content_id: null,
dialog_title: 'Vindu',
close_title: 'Lukk',
hide_close_button: false,
close_button_attributes: null,
prevent_close: false,
prevent_core_style: false,
animation_duration: ANIMATION_DURATION,
no_animation: false,
no_animation_on_mobile: false,
fullscreen: 'auto',
min_width: null,
max_width: null,
align_content: 'left',
container_placement: null,
vertical_alignment: null,
open_state: null,
direct_dom_return: false,
root_id: 'root',
omit_trigger_button: false,
className: null,
children: null,
on_open: null,
on_close: null,
on_close_prevent: null,
open_modal: null,
close_modal: null,
trigger: null,
trigger_attributes: null,
overlay_class: null,
content_class: null,
modal_content: null,
header_content: null,
bar_content: null
});
export { CloseButton, Modal as OriginalComponent };
export default classWithCamelCaseProps(Modal);
//# sourceMappingURL=Modal.js.map