diff --git a/src/bundle/ui-dev/src/modules/common/popup/popup.component.js b/src/bundle/ui-dev/src/modules/common/popup/popup.component.js index f8234288a4..6025d84c8c 100644 --- a/src/bundle/ui-dev/src/modules/common/popup/popup.component.js +++ b/src/bundle/ui-dev/src/modules/common/popup/popup.component.js @@ -1,8 +1,10 @@ -import React, { Component } from 'react'; +import React, { useEffect, useRef, useMemo } from 'react'; import PropTypes from 'prop-types'; import Icon from '../icon/icon'; -const { Translator } = window; +import { createCssClassNames } from '@ibexa-admin-ui/src/bundle/ui-dev/src/modules/common/helpers/css.class.names'; + +const { bootstrap, Translator } = window; const CLASS_NON_SCROLLABLE = 'ibexa-non-scrollable'; const CLASS_MODAL_OPEN = 'modal-open'; @@ -16,205 +18,128 @@ const MODAL_SIZE_CLASS = { large: 'modal-lg', }; -class Popup extends Component { - constructor(props) { - super(props); - - this._refModal = null; - - this.setModalRef = this.setModalRef.bind(this); - this.onKeyUp = this.onKeyUp.bind(this); - - this.state = { - currentProps: { - isVisible: props.isVisible, - isLoading: props.isLoading, - }, - isVisible: props.isVisible, - isLoading: props.isLoading, - }; - } - - static getDerivedStateFromProps(nextProps, prevState) { - if (nextProps.isVisible !== prevState.currentProps.isVisible || nextProps.isLoading !== prevState.currentProps.isLoading) { - return { - currentProps: { - isVisible: nextProps.isVisible, - isLoading: nextProps.isLoading, - }, - isVisible: nextProps.isVisible, - isLoading: nextProps.isLoading, - }; - } - +const Popup = ({ + isVisible, + onClose, + children, + title, + subtitle, + hasFocus, + noKeyboard, + actionBtns, + size, + noHeader, + noCloseBtn, + extraClasses, +}) => { + if (!isVisible) { return null; } - componentDidMount() { - const { noKeyboard, hasFocus } = this.props; - const { isVisible: show } = this.state; - - if (show) { - const bootstrapModal = window.bootstrap.Modal.getOrCreateInstance(this._refModal, { - ...MODAL_CONFIG, - keyboard: !noKeyboard, - focus: hasFocus, - }); - - bootstrapModal.show(); - - this.attachModalEventHandlers(); - } - } - - componentDidUpdate() { - const { isVisible: show } = this.state; - - const bootstrapModal = window.bootstrap.Modal.getOrCreateInstance(this._refModal, { + const modalRef = useRef(null); + const modalClasses = createCssClassNames({ + 'c-popup modal fade': true, + 'c-popup--no-header': noHeader, + [extraClasses]: extraClasses, + }); + const closeBtnLabel = Translator.trans(/*@Desc("Close")*/ 'popup.close.label', {}, 'ibexa_universal_discovery_widget'); + const hidePopup = () => { + bootstrap.Modal.getOrCreateInstance(modalRef.current).hide(); + document.body.classList.remove(CLASS_MODAL_OPEN, CLASS_NON_SCROLLABLE); + }; + const showPopup = () => { + const bootstrapModal = bootstrap.Modal.getOrCreateInstance(modalRef.current, { ...MODAL_CONFIG, - focus: this.props.hasFocus, + keyboard: !noKeyboard, + focus: hasFocus, }); - if (show) { - bootstrapModal.show(); - this.attachModalEventHandlers(); - } else { - bootstrapModal.hide(); - } - } - - componentWillUnmount() { - window.bootstrap.Modal.getOrCreateInstance(this._refModal).hide(); - document.body.classList.remove(CLASS_MODAL_OPEN, CLASS_NON_SCROLLABLE); - } - - attachModalEventHandlers() { - this._refModal.addEventListener('keyup', this.onKeyUp); - this._refModal.addEventListener('hidden.bs.modal', this.props.onClose); - } - - onKeyUp(event) { - const { originalEvent } = event; - const escKeyCode = 27; - const escKeyPressed = originalEvent && (originalEvent.which === escKeyCode || originalEvent.keyCode === escKeyCode); - - if (escKeyPressed) { - this.props.onClose(); - } - } - - setModalRef(component) { - this._refModal = component; - } - - renderHeader() { - return ( -
- {this.renderHeadline()} - {this.renderCloseButton()} -
- ); - } - - renderCloseButton() { - if (this.props.noCloseBtn) { - return; + bootstrapModal.show(); + }; + const handleOnClick = (event, onClick) => { + modalRef.current.removeEventListener('hidden.bs.modal', onClose); + hidePopup(); + onClick(event); + }; + const closeButton = useMemo(() => { + if (noCloseBtn) { + return null; } - const closeBtnLabel = Translator.trans(/*@Desc("Close")*/ 'popup.close.label', {}, 'ibexa_universal_discovery_widget'); - return ( ); - } - - renderHeadline() { - const { title } = this.props; - - if (!title) { - return null; - } - - return ( -

- {this.props.title} - {this.renderSubtitle()} -

- ); - } - - renderSubtitle() { - const { subtitle } = this.props; - - if (!subtitle) { - return null; - } - - return {subtitle}; - } - - renderFooter() { - const { footerChildren } = this.props; - - if (!footerChildren) { - return; - } - - return
{footerChildren}
; - } - - render() { - const { isVisible } = this.state; - const { additionalClasses, size, noHeader, extraClasses } = this.props; - const modalAttrs = { - className: `c-popup modal fade ${extraClasses}`, - ref: this.setModalRef, - tabIndex: this.props.hasFocus ? -1 : undefined, - }; + }, [hidePopup, closeBtnLabel, noCloseBtn]); + useEffect(() => { document.body.classList.toggle(CLASS_MODAL_OPEN, isVisible); document.body.classList.toggle(CLASS_NON_SCROLLABLE, isVisible); - if (additionalClasses) { - modalAttrs.className = `${modalAttrs.className} ${additionalClasses}`; - } - - if (noHeader) { - modalAttrs.className = `${modalAttrs.className} c-popup--no-header`; + if (isVisible) { + showPopup(); + modalRef.current.addEventListener('hidden.bs.modal', onClose); } - - return ( -
-
-
- {noHeader ? this.renderCloseButton() : this.renderHeader()} -
{this.props.children}
- {this.renderFooter()} + }, [isVisible]); + + return ( +
+
+
+ {noHeader + ? closeButton + : title && ( +
+

+ {title} + {subtitle && {subtitle}} +

+ {closeButton} +
+ )} +
{children}
+
+ {actionBtns.map(({ className, onClick, disabled = false, label, ...extraProps }) => ( + + ))}
- ); - } -} +
+ ); +}; Popup.propTypes = { - isVisible: PropTypes.bool, - isLoading: PropTypes.bool, - onClose: PropTypes.func.isRequired, - children: PropTypes.element.isRequired, + actionBtns: PropTypes.arrayOf( + PropTypes.shape({ + label: PropTypes.string.isRequired, + onClick: PropTypes.func, + disabled: PropTypes.bool, + className: PropTypes.string, + }), + ).isRequired, + children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired, + isVisible: PropTypes.bool.isRequired, + onClose: PropTypes.func, title: PropTypes.string, subtitle: PropTypes.string, hasFocus: PropTypes.bool, - additionalClasses: PropTypes.string, - footerChildren: PropTypes.element, size: PropTypes.string, noHeader: PropTypes.bool, noCloseBtn: PropTypes.bool, @@ -223,18 +148,15 @@ Popup.propTypes = { }; Popup.defaultProps = { - isVisible: false, - isLoading: true, hasFocus: true, + noKeyboard: false, + onClose: null, size: 'large', noHeader: false, noCloseBtn: false, - noKeyboard: false, extraClasses: '', title: null, subtitle: null, - additionalClasses: null, - footerChildren: null, }; export default Popup; diff --git a/src/bundle/ui-dev/src/modules/multi-file-upload/components/upload-popup/upload.popup.component.js b/src/bundle/ui-dev/src/modules/multi-file-upload/components/upload-popup/upload.popup.component.js index 8dcef5c2cd..b927cb26ca 100644 --- a/src/bundle/ui-dev/src/modules/multi-file-upload/components/upload-popup/upload.popup.component.js +++ b/src/bundle/ui-dev/src/modules/multi-file-upload/components/upload-popup/upload.popup.component.js @@ -51,7 +51,6 @@ export default class UploadPopupModule extends Component { } UploadPopupModule.propTypes = { - popupTitle: PropTypes.string.isRequired, visible: PropTypes.bool, itemsToUpload: PropTypes.array, onAfterUpload: PropTypes.func.isRequired, diff --git a/src/bundle/ui-dev/src/modules/sub-items/sub.items.module.js b/src/bundle/ui-dev/src/modules/sub-items/sub.items.module.js index 59beb1d8ca..0a7fc80f44 100644 --- a/src/bundle/ui-dev/src/modules/sub-items/sub.items.module.js +++ b/src/bundle/ui-dev/src/modules/sub-items/sub.items.module.js @@ -1,4 +1,4 @@ -import React, { Component, Fragment } from 'react'; +import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; @@ -867,7 +867,6 @@ export default class SubItemsModule extends Component { } renderDeleteConfirmationPopupFooter(selectionInfo) { - const cancelLabel = Translator.trans(/*@Desc("Cancel")*/ 'bulk_action.popup.cancel', {}, 'ibexa_sub_items'); const { isUserContentItemSelected, isNonUserContentItemSelected } = selectionInfo; let confirmLabel = ''; @@ -880,72 +879,48 @@ export default class SubItemsModule extends Component { 'ibexa_sub_items', ); } + const confirmBtn = { + label: confirmLabel, + onClick: this.onBulkDeletePopupConfirm, + className: 'ibexa-btn--primary ibexa-btn--trigger', + }; + const cancelBtn = { + label: Translator.trans(/*@Desc("Cancel")*/ 'bulk_action.popup.cancel', {}, 'ibexa_sub_items'), + className: 'ibexa-btn--secondary', + 'data-bs-dismiss': 'modal', + }; - return ( - - - - - ); + return [confirmBtn, cancelBtn]; } renderHideConfirmationPopupFooter() { - const cancelLabel = Translator.trans(/*@Desc("Cancel")*/ 'bulk_action.popup.cancel', {}, 'ibexa_sub_items'); - const confirmLabel = Translator.trans(/*@Desc("Hide")*/ 'bulk_hide.popup.confirm', {}, 'ibexa_sub_items'); + const confirmBtn = { + label: Translator.trans(/*@Desc("Hide")*/ 'bulk_hide.popup.confirm', {}, 'ibexa_sub_items'), + onClick: this.onBulkHidePopupConfirm, + className: 'ibexa-btn--primary ibexa-btn--trigger', + }; + const cancelBtn = { + label: Translator.trans(/*@Desc("Cancel")*/ 'bulk_action.popup.cancel', {}, 'ibexa_sub_items'), + className: 'ibexa-btn--secondary', + 'data-bs-dismiss': 'modal', + }; - return ( - - - - - ); + return [confirmBtn, cancelBtn]; } renderUnhideConfirmationPopupFooter() { - const cancelLabel = Translator.trans(/*@Desc("Cancel")*/ 'bulk_action.popup.cancel', {}, 'ibexa_sub_items'); - const confirmLabel = Translator.trans(/*@Desc("Reveal")*/ 'bulk_unhide.popup.confirm', {}, 'ibexa_sub_items'); + const confirmBtn = { + label: Translator.trans(/*@Desc("Reveal")*/ 'bulk_unhide.popup.confirm', {}, 'ibexa_sub_items'), + onClick: this.onBulkUnhidePopupConfirm, + className: 'ibexa-btn--primary ibexa-btn--trigger', + }; + const cancelBtn = { + label: Translator.trans(/*@Desc("Cancel")*/ 'bulk_action.popup.cancel', {}, 'ibexa_sub_items'), + className: 'ibexa-btn--secondary', + 'data-bs-dismiss': 'modal', + }; - return ( - - - - - ); + return [confirmBtn, cancelBtn]; } getSelectionInfo() { @@ -1011,9 +986,8 @@ export default class SubItemsModule extends Component {
{confirmationMessage}
@@ -1040,9 +1014,8 @@ export default class SubItemsModule extends Component {
{confirmationMessage}
@@ -1069,9 +1042,8 @@ export default class SubItemsModule extends Component {
{confirmationMessage}