0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

refactor(console): extract confirm modal component (#1163)

This commit is contained in:
Xiao Yijun 2022-06-20 20:20:26 +08:00 committed by GitHub
parent 5c43da2201
commit 3e1c040ebe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 82 additions and 155 deletions

View file

@ -8,9 +8,11 @@ import { Ring as Spinner } from '@/components/Spinner';
import DangerousRaw from '../DangerousRaw';
import * as styles from './index.module.scss';
export type ButtonType = 'primary' | 'danger' | 'outline' | 'plain' | 'default';
type BaseProps = Omit<HTMLProps<HTMLButtonElement>, 'type' | 'size' | 'title'> & {
htmlType?: 'button' | 'submit' | 'reset';
type?: 'primary' | 'danger' | 'outline' | 'plain' | 'default';
type?: ButtonType;
size?: 'small' | 'medium' | 'large';
isLoading?: boolean;
loadingDelay?: number;

View file

@ -0,0 +1,9 @@
@use '@/scss/underscore' as _;
.content {
font: var(--font-body-medium);
> :not(:first-child) {
margin: _.unit(6) 0;
}
}

View file

@ -1,55 +1,58 @@
import { AdminConsoleKey, I18nKey } from '@logto/phrases';
import React from 'react';
import Modal from 'react-modal';
import classNames from 'classnames';
import React, { ReactNode } from 'react';
import ReactModal from 'react-modal';
import Button, { ButtonType } from '@/components/Button';
import * as modalStyles from '@/scss/modal.module.scss';
import Button from '../Button';
import ModalLayout from '../ModalLayout';
import * as styles from './index.module.scss';
type Props = {
title: AdminConsoleKey;
children: React.ReactNode;
export type ConfirmModalProps = {
children: ReactNode;
className?: string;
title?: AdminConsoleKey;
confirmButtonType?: ButtonType;
confirmButtonText?: I18nKey;
cancelButtonText?: I18nKey;
isOpen: boolean;
isPending?: boolean;
onConfirm: () => void;
onCancel: () => void;
onConfirm: () => void;
};
const ConfirmModal = ({
title,
children,
className,
title = 'form.confirm',
confirmButtonType = 'danger',
confirmButtonText = 'general.confirm',
cancelButtonText = 'general.cancel',
isOpen,
isPending,
onConfirm,
onCancel,
}: Props) => (
<Modal isOpen={isOpen} className={modalStyles.content} overlayClassName={modalStyles.overlay}>
<ModalLayout
title={title}
footer={
<>
<Button type="outline" title={cancelButtonText} onClick={onCancel} />
<Button
isLoading={isPending}
type="primary"
title={confirmButtonText}
onClick={onConfirm}
/>
</>
}
className={className}
onClose={onCancel}
onConfirm,
}: ConfirmModalProps) => {
return (
<ReactModal
isOpen={isOpen}
className={modalStyles.content}
overlayClassName={modalStyles.overlay}
>
{children}
</ModalLayout>
</Modal>
);
<ModalLayout
title={title}
footer={
<>
<Button type="outline" title={cancelButtonText} onClick={onCancel} />
<Button type={confirmButtonType} title={confirmButtonText} onClick={onConfirm} />
</>
}
className={classNames(styles.content, className)}
onClose={onCancel}
>
{children}
</ModalLayout>
</ReactModal>
);
};
export default ConfirmModal;

View file

@ -1,44 +0,0 @@
import { I18nKey } from '@logto/phrases';
import React from 'react';
import { useTranslation } from 'react-i18next';
import Button from '../Button';
import ModalLayout from '../ModalLayout';
import * as styles from './index.module.scss';
type Props = {
title: I18nKey;
onConfirm: () => void;
onClose: () => void;
};
const DeleteConfirm = ({ title, onConfirm, onClose }: Props) => {
const { t } = useTranslation();
return (
<ModalLayout
title="form.confirm"
footer={
<>
<Button type="outline" title="admin_console.form.cancel" onClick={onClose} />
<Button
type="danger"
title="admin_console.form.delete"
onClick={() => {
onConfirm();
onClose();
}}
/>
</>
}
className={styles.content}
onClose={onClose}
>
<div className={styles.confirmation}>
{t('admin_console.form.deletion_confirmation', { title: t(title) })}
</div>
</ModalLayout>
);
};
export default DeleteConfirm;

View file

@ -30,15 +30,3 @@
margin-top: _.unit(1);
}
}
.reminder {
.content {
> :not(:first-child) {
margin: _.unit(6) 0;
}
.confirmation {
font: var(--font-body-medium);
}
}
}

View file

@ -2,15 +2,13 @@ import { I18nKey } from '@logto/phrases';
import classNames from 'classnames';
import React, { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import Modal from 'react-modal';
import * as textButtonStyles from '@/components/TextButton/index.module.scss';
import Minus from '@/icons/Minus';
import * as modalStyles from '@/scss/modal.module.scss';
import ConfirmModal from '../ConfirmModal';
import IconButton from '../IconButton';
import TextInput from '../TextInput';
import DeleteConfirm from './DeleteConfirm';
import * as styles from './index.module.scss';
import { MultiTextInputError } from './types';
@ -22,9 +20,7 @@ type Props = {
};
const MultiTextInput = ({ title, value, onChange, error }: Props) => {
const { t } = useTranslation(undefined, {
keyPrefix: 'admin_console',
});
const { t } = useTranslation();
const [deleteFieldIndex, setDeleteFieldIndex] = useState<number>();
@ -86,25 +82,23 @@ const MultiTextInput = ({ title, value, onChange, error }: Props) => {
</div>
))}
<div className={classNames(textButtonStyles.button, styles.addAnother)} onClick={handleAdd}>
{t('form.add_another')}
{t('admin_console.form.add_another')}
</div>
<Modal
<ConfirmModal
isOpen={deleteFieldIndex !== undefined}
className={modalStyles.content}
overlayClassName={modalStyles.overlay}
>
<DeleteConfirm
title={title}
onClose={() => {
confirmButtonText="admin_console.form.delete"
onCancel={() => {
setDeleteFieldIndex(undefined);
}}
onConfirm={() => {
if (deleteFieldIndex !== undefined) {
handleRemove(deleteFieldIndex);
setDeleteFieldIndex(undefined);
}}
onConfirm={() => {
if (deleteFieldIndex !== undefined) {
handleRemove(deleteFieldIndex);
}
}}
/>
</Modal>
}
}}
>
{t('admin_console.form.deletion_confirmation', { title: t(title) })}
</ConfirmModal>
</div>
);
};

View file

@ -71,6 +71,7 @@ const GetStarted = () => {
<ConfirmModal
title="get_started.confirm"
isOpen={showConfirmModal}
confirmButtonType="primary"
confirmButtonText="admin_console.get_started.hide_this"
onConfirm={hideGetStarted}
onCancel={() => {

View file

@ -2,40 +2,19 @@ import { SignInExperience } from '@logto/schemas';
import React from 'react';
import { useTranslation } from 'react-i18next';
import Button from '@/components/Button';
import ModalLayout from '@/components/ModalLayout';
import * as styles from './SaveAlert.module.scss';
import * as styles from './SignInMethodsChangePreview.module.scss';
import SignInMethodsPreview from './SignInMethodsPreview';
type Props = {
before: SignInExperience;
after: SignInExperience;
onClose: () => void;
onConfirm: () => void;
};
const SaveAlert = ({ before, after, onClose, onConfirm }: Props) => {
const SignInMethodsChangePreview = ({ before, after }: Props) => {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
return (
<ModalLayout
title="sign_in_exp.save_alert.title"
footer={
<>
<Button type="outline" title="general.cancel" onClick={onClose} />
<Button
type="danger"
title="general.confirm"
onClick={() => {
onClose();
onConfirm();
}}
/>
</>
}
onClose={onClose}
>
<div>
<div className={styles.description}>{t('sign_in_exp.save_alert.description')}</div>
<div className={styles.content}>
<div className={styles.section}>
@ -47,8 +26,8 @@ const SaveAlert = ({ before, after, onClose, onConfirm }: Props) => {
<SignInMethodsPreview data={after} />
</div>
</div>
</ModalLayout>
</div>
);
};
export default SaveAlert;
export default SignInMethodsChangePreview;

View file

@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next';
import UnnamedTrans from '@/components/UnnamedTrans';
import useConnectorGroups from '@/hooks/use-connector-groups';
import * as styles from './SaveAlert.module.scss';
import * as styles from './SignInMethodsChangePreview.module.scss';
type Props = {
data: SignInExperience;

View file

@ -4,23 +4,22 @@ import React, { useEffect, useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
import ReactModal from 'react-modal';
import { useParams } from 'react-router-dom';
import useSWR from 'swr';
import Button from '@/components/Button';
import Card from '@/components/Card';
import CardTitle from '@/components/CardTitle';
import ConfirmModal from '@/components/ConfirmModal';
import TabNav, { TabNavItem } from '@/components/TabNav';
import useApi, { RequestError } from '@/hooks/use-api';
import useSettings from '@/hooks/use-settings';
import * as detailsStyles from '@/scss/details.module.scss';
import * as modalStyles from '@/scss/modal.module.scss';
import BrandingForm from './components/BrandingForm';
import LanguagesForm from './components/LanguagesForm';
import Preview from './components/Preview';
import SaveAlert from './components/SaveAlert';
import SignInMethodsChangePreview from './components/SignInMethodsChangePreview';
import SignInMethodsForm from './components/SignInMethodsForm';
import TermsForm from './components/TermsForm';
import Welcome from './components/Welcome';
@ -144,22 +143,18 @@ const SignInExperience = () => {
</div>
{formData.id && <Preview signInExperience={previewConfigs} />}
{data && (
<ReactModal
<ConfirmModal
isOpen={Boolean(dataToCompare)}
className={modalStyles.content}
overlayClassName={modalStyles.overlay}
onCancel={() => {
setDataToCompare(undefined);
}}
onConfirm={async () => {
await saveData();
setDataToCompare(undefined);
}}
>
{dataToCompare && (
<SaveAlert
before={data}
after={dataToCompare}
onClose={() => {
setDataToCompare(undefined);
}}
onConfirm={saveData}
/>
)}
</ReactModal>
{dataToCompare && <SignInMethodsChangePreview before={data} after={dataToCompare} />}
</ConfirmModal>
)}
</div>
);