UNPKG

@dnb/eufemia

Version:

DNB Eufemia Design System UI Library

412 lines (411 loc) 14.5 kB
"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) { var _this; super(props); _this = this; _defineProperty(this, "state", { hide: false, modalActive: false, preventAutoFocus: true, animation_duration: ANIMATION_DURATION, no_animation: false }); _defineProperty(this, "toggleOpenClose", function () { let event = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; let showModal = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 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", function (event) { var _this$modalContentClo, _this$modalContentClo2; let { ifIsLatest, triggeredBy = 'handler' } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { ifIsLatest: true }; (_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