Skip to content

Commit

Permalink
Refactor #2360 - React 18 support for ConfirmDialog and ConfirmPopup
Browse files Browse the repository at this point in the history
  • Loading branch information
mertsincan committed Apr 1, 2022
1 parent 981005e commit b1db5b8
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 132 deletions.
5 changes: 4 additions & 1 deletion components/lib/confirmdialog/ConfirmDialog.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ interface ConfirmDialogOptions {
}

export interface ConfirmDialogProps extends Omit<DialogProps, 'onHide'> {
tagKey?: string;
visible?: boolean;
message?: ConfirmDialogTemplateType;
rejectLabel?: string;
Expand All @@ -46,6 +47,8 @@ interface ConfirmDialogReturn {
hide(): void;
}

export declare class ConfirmDialog extends React.Component<ConfirmDialogProps, any> { }
export declare class ConfirmDialog extends React.Component<ConfirmDialogProps, any> {
public confirm(props?: ConfirmDialogProps): void;
}

export declare function confirmDialog(props: ConfirmDialogProps): ConfirmDialogReturn;
144 changes: 85 additions & 59 deletions components/lib/confirmdialog/ConfirmDialog.js
Original file line number Diff line number Diff line change
@@ -1,115 +1,140 @@
import React, { forwardRef, memo, useState } from 'react';
import ReactDOM from 'react-dom';
import React, { forwardRef, memo, useEffect, useImperativeHandle, useRef, useState } from 'react';
import { localeOption } from '../api/Api';
import { Dialog } from '../dialog/Dialog';
import { Button } from '../button/Button';
import { Portal } from '../portal/Portal';
import { DomHandler, ObjectUtils, classNames, IconUtils } from '../utils/Utils';
import { useUpdateEffect } from '../hooks/Hooks';

export const confirmDialog = (props) => {
const appendTo = props.appendTo || document.body;

const confirmDialogWrapper = document.createDocumentFragment();
DomHandler.appendChild(confirmDialogWrapper, appendTo);
import { ObjectUtils, classNames, IconUtils } from '../utils/Utils';
import { useUnmountEffect, useUpdateEffect } from '../hooks/Hooks';
import { OverlayService } from '../overlayservice/OverlayService';

export const confirmDialog = (props = {}) => {
props = { ...props, ...{ visible: props.visible === undefined ? true : props.visible } };
props.visible && OverlayService.emit('confirm-dialog', props);

const confirmDialogEl = React.createElement(ConfirmDialog, props);
ReactDOM.render(confirmDialogEl, confirmDialogWrapper);

const updateConfirmDialog = (newProps) => {
props = { ...props, ...newProps };
ReactDOM.render(React.cloneElement(confirmDialogEl, props), confirmDialogWrapper);
};

return {
_destroy: () => {
ReactDOM.unmountComponentAtNode(confirmDialogWrapper);
},
show: () => {
updateConfirmDialog({
visible: true, onHide: () => {
updateConfirmDialog({ visible: false }); // reset
}
});
},
hide: () => {
updateConfirmDialog({ visible: false });
},
update: (newProps) => {
updateConfirmDialog(newProps);
}
const show = (updatedProps = {}) => {
OverlayService.emit('confirm-dialog', { ...props, ...updatedProps, ...{ visible: true } });
}

const hide = () => {
OverlayService.emit('confirm-dialog', { visible: false });
}

return [show, hide];
}

export const ConfirmDialog = memo(forwardRef((props, ref) => {
const [visibleState, setVisibleState] = useState(props.visible);
const acceptLabel = props.acceptLabel || localeOption('accept');
const rejectLabel = props.rejectLabel || localeOption('reject');
const [reshowState, setReshowState] = useState(false);
const confirmProps = useRef(null);
const getCurrentProps = () => confirmProps.current || props;
const getPropValue = (key) => (confirmProps.current || props)[key];
const callbackFromProp = (key, ...param) => ObjectUtils.getPropValue(getPropValue(key), param);

const acceptLabel = getPropValue('acceptLabel') || localeOption('accept');
const rejectLabel = getPropValue('rejectLabel') || localeOption('reject');

const accept = () => {
props.accept && props.accept();
callbackFromProp('accept');
hide('accept');
}

const reject = () => {
props.reject && props.reject();
callbackFromProp('reject');
hide('reject');
}

const show = () => {
setVisibleState(true)
setVisibleState(true);
}

const hide = (result) => {
setVisibleState(false);
props.onHide && props.onHide(result);
callbackFromProp('onHide', result);
}

useUpdateEffect(() => {
setVisibleState(props.visible);
const confirm = (updatedProps) => {
if (updatedProps.tagKey === props.tagKey) {
const isVisibleChanged = visibleState !== updatedProps.visible;
const targetChanged = getPropValue('target') !== updatedProps.target;

if (targetChanged && !props.target) {
hide();
confirmProps.current = updatedProps;
setReshowState(true);
}
else if (isVisibleChanged) {
confirmProps.current = updatedProps;
updatedProps.visible ? show() : hide();
}
}
}

useEffect(() => {
props.visible ? show() : hide();
}, [props.visible]);

useEffect(() => {
if (!props.target && !props.message) {
OverlayService.on('confirm-dialog', confirm);
}

return () => {
OverlayService.off('confirm-dialog', confirm);
}
}, [props.target]);

useUpdateEffect(() => {
reshowState && show();
}, [reshowState]);

useUnmountEffect(() => {
OverlayService.off('confirm-dialog', confirm);
});

useImperativeHandle(ref, () => ({
confirm
}));

const createFooter = () => {
const acceptClassName = classNames('p-confirm-dialog-accept', props.acceptClassName);
const acceptClassName = classNames('p-confirm-dialog-accept', getPropValue('acceptClassName'));
const rejectClassName = classNames('p-confirm-dialog-reject', {
'p-button-text': !props.rejectClassName
}, props.rejectClassName);
'p-button-text': !getPropValue('rejectClassName')
}, getPropValue('rejectClassName'));
const content = (
<>
<Button label={rejectLabel} icon={props.rejectIcon} className={rejectClassName} onClick={reject} />
<Button label={acceptLabel} icon={props.acceptIcon} className={acceptClassName} onClick={accept} autoFocus />
<Button label={rejectLabel} icon={getPropValue('rejectIcon')} className={rejectClassName} onClick={reject} />
<Button label={acceptLabel} icon={getPropValue('acceptIcon')} className={acceptClassName} onClick={accept} autoFocus />
</>
);

if (props.footer) {
if (getPropValue('footer')) {
const defaultContentOptions = {
accept: accept,
reject: reject,
accept,
reject,
acceptClassName,
rejectClassName,
acceptLabel,
rejectLabel,
element: content,
props
props: getCurrentProps()
};

return ObjectUtils.getJSXElement(props.footer, defaultContentOptions);
return ObjectUtils.getJSXElement(getPropValue('footer'), defaultContentOptions);
}

return content;
}

const createElement = () => {
const className = classNames('p-confirm-dialog', props.className);
const dialogProps = ObjectUtils.findDiffKeys(props, ConfirmDialog.defaultProps);
const message = ObjectUtils.getJSXElement(props.message, props);
const icon = IconUtils.getJSXIcon(props.icon, { className: 'p-confirm-dialog-icon' }, { props });
const currentProps = getCurrentProps();
const className = classNames('p-confirm-dialog', getPropValue('className'));
const dialogProps = ObjectUtils.findDiffKeys(currentProps, ConfirmDialog.defaultProps);
const message = ObjectUtils.getJSXElement(getPropValue('message'), currentProps);
const icon = IconUtils.getJSXIcon(getPropValue('icon'), { className: 'p-confirm-dialog-icon' }, { props: currentProps });
const footer = createFooter();

return (
<Dialog visible={visibleState} {...dialogProps} className={className} footer={footer} onHide={hide} breakpoints={props.breakpoints}>
<Dialog visible={visibleState} {...dialogProps} className={className} footer={footer} onHide={hide} breakpoints={getPropValue('breakpoints')}>
{icon}
<span className="p-confirm-dialog-message">{message}</span>
</Dialog>
Expand All @@ -118,11 +143,12 @@ export const ConfirmDialog = memo(forwardRef((props, ref) => {

const element = createElement();

return <Portal element={element} appendTo={props.appendTo} />
return <Portal element={element} appendTo={getPropValue('appendTo')} />
}));

ConfirmDialog.defaultProps = {
__TYPE: 'ConfirmDialog',
tagKey: undefined,
visible: false,
message: null,
rejectLabel: null,
Expand Down
5 changes: 4 additions & 1 deletion components/lib/confirmpopup/ConfirmPopup.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface ConfirmPopupOptions {
}

export interface ConfirmPopupProps {
tagKey?: string;
target?: HTMLElement;
visible?: boolean;
message?: ConfirmPopupTemplateType;
Expand Down Expand Up @@ -47,6 +48,8 @@ interface ConfirmPopupReturn {
hide(): void;
}

export declare class ConfirmPopup extends React.Component<ConfirmPopupProps, any> { }
export declare class ConfirmPopup extends React.Component<ConfirmPopupProps, any> {
public confirm(props?: ConfirmPopupProps): void;
}

export declare function confirmPopup(props: ConfirmPopupProps): ConfirmPopupReturn;
Loading

0 comments on commit b1db5b8

Please sign in to comment.