feat: esc key to exit multiple dialogs (#692)

* fix: `esc` key to exit multiple dialogs

* update

* update

* update

* Update web/src/components/Dialog/BaseDialog.tsx

Co-authored-by: boojack <stevenlgtm@gmail.com>
This commit is contained in:
Zeng1998 2022-12-18 18:09:12 +08:00 committed by GitHub
parent e79d67d127
commit 55695f2189
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 87 additions and 3 deletions

View File

@ -59,6 +59,7 @@ export default function showAboutSiteDialog(): void {
generateDialog(
{
className: "about-site-dialog",
dialogName: "about-site-dialog",
},
AboutSiteDialog
);

View File

@ -69,6 +69,7 @@ export default function showArchivedMemoDialog(): void {
generateDialog(
{
className: "archived-memo-dialog",
dialogName: "archived-memo-dialog",
},
ArchivedMemoDialog,
{}

View File

@ -117,6 +117,7 @@ function showChangeMemberPasswordDialog(user: User) {
generateDialog(
{
className: "change-member-password-dialog",
dialogName: "change-member-password-dialog",
},
ChangeMemberPasswordDialog,
{ user }

View File

@ -97,6 +97,7 @@ function showChangeMemoCreatedTsDialog(memoId: MemoId) {
generateDialog(
{
className: "change-memo-created-ts-dialog",
dialogName: "change-memo-created-ts-dialog",
},
ChangeMemoCreatedTsDialog,
{

View File

@ -115,6 +115,7 @@ function showChangePasswordDialog() {
generateDialog(
{
className: "change-password-dialog",
dialogName: "change-password-dialog",
},
ChangePasswordDialog
);

View File

@ -87,6 +87,7 @@ function showChangeResourceFilenameDialog(resourceId: ResourceId, resourceFilena
generateDialog(
{
className: "change-resource-filename-dialog",
dialogName: "change-resource-filename-dialog",
},
ChangeResourceFilenameDialog,
{

View File

@ -291,6 +291,7 @@ export default function showCreateShortcutDialog(shortcutId?: ShortcutId): void
generateDialog(
{
className: "create-shortcut-dialog",
dialogName: "create-shortcut-dialog",
},
CreateShortcutDialog,
{ shortcutId }

View File

@ -115,6 +115,7 @@ export default function showDailyReviewDialog(datestamp: DateStamp = Date.now())
generateDialog(
{
className: "daily-review-dialog",
dialogName: "daily-review-dialog",
},
DailyReviewDialog,
{ currentDateStamp: datestamp }

View File

@ -3,13 +3,15 @@ import { createRoot } from "react-dom/client";
import { Provider } from "react-redux";
import { ANIMATION_DURATION } from "../../helpers/consts";
import store from "../../store";
import "../../less/base-dialog.less";
import { useDialogStore } from "../../store/module";
import { CssVarsProvider } from "@mui/joy";
import theme from "../../theme";
import "../../less/base-dialog.less";
interface DialogConfig {
className: string;
clickSpaceDestroy?: boolean;
dialogName: string;
}
interface Props extends DialogConfig, DialogProps {
@ -17,12 +19,17 @@ interface Props extends DialogConfig, DialogProps {
}
const BaseDialog: React.FC<Props> = (props: Props) => {
const { children, className, clickSpaceDestroy, destroy } = props;
const { children, className, clickSpaceDestroy, dialogName, destroy } = props;
const dialogStore = useDialogStore();
useEffect(() => {
dialogStore.pushDialogStack(dialogName);
const handleKeyDown = (event: KeyboardEvent) => {
if (event.code === "Escape") {
destroy();
if (dialogName === dialogStore.topDialogStack()) {
dialogStore.popDialogStack();
destroy();
}
}
};

View File

@ -72,6 +72,7 @@ interface CommonDialogProps {
content: string;
className?: string;
style?: DialogStyle;
dialogName: string;
closeBtnText?: string;
confirmBtnText?: string;
onClose?: () => void;
@ -82,6 +83,7 @@ export const showCommonDialog = (props: CommonDialogProps) => {
generateDialog(
{
className: `common-dialog ${props?.className ?? ""}`,
dialogName: `common-dialog ${props?.className ?? ""}`,
},
CommonDialog,
props

View File

@ -124,6 +124,7 @@ export default function showPreviewImageDialog(imgUrls: string[] | string, initi
generateDialog(
{
className: "preview-image-dialog",
dialogName: "preview-image-dialog",
},
PreviewImageDialog,
{

View File

@ -125,6 +125,7 @@ const ResourcesDialog: React.FC<Props> = (props: Props) => {
title: t("resources.delete-resource"),
content: warningText,
style: "warning",
dialogName: "delete-unused-resources",
onConfirm: async () => {
for (const resource of unusedResources) {
await resourceStore.deleteResourceById(resource.id);
@ -143,6 +144,7 @@ const ResourcesDialog: React.FC<Props> = (props: Props) => {
title: t("resources.delete-resource"),
content: warningText,
style: "warning",
dialogName: "delete-resource-dialog",
onConfirm: async () => {
await resourceStore.deleteResourceById(resource.id);
},
@ -242,6 +244,7 @@ export default function showResourcesDialog() {
generateDialog(
{
className: "resources-dialog",
dialogName: "resources-dialog",
},
ResourcesDialog,
{}

View File

@ -148,6 +148,7 @@ export default function showResourcesSelectorDialog() {
generateDialog(
{
className: "resources-selector-dialog",
dialogName: "resources-selector-dialog",
},
ResourcesSelectorDialog,
{}

View File

@ -92,6 +92,7 @@ export default function showSettingDialog(): void {
generateDialog(
{
className: "setting-dialog",
dialogName: "setting-dialog",
},
SettingDialog,
{}

View File

@ -79,6 +79,7 @@ const PreferencesSection = () => {
title: `Archive Member`,
content: `Are you sure to archive ${user.username}?`,
style: "warning",
dialogName: "archive-user-dialog",
onConfirm: async () => {
await userStore.patchUser({
id: user.id,
@ -102,6 +103,7 @@ const PreferencesSection = () => {
title: `Delete Member`,
content: `Are you sure to delete ${user.username}? THIS ACTION IS IRREVERSIABLE.❗️`,
style: "warning",
dialogName: "delete-user-dialog",
onConfirm: async () => {
await userStore.deleteUser({
id: user.id,

View File

@ -16,6 +16,7 @@ const MyAccountSection = () => {
title: "Reset Open API",
content: "❗The existing API will be invalidated and a new one will be generated, are you sure you want to reset?",
style: "warning",
dialogName: "reset-openid-dialog",
onConfirm: async () => {
await userStore.patchUser({
id: user.id,

View File

@ -190,6 +190,7 @@ export default function showShareMemoDialog(memo: Memo): void {
generateDialog(
{
className: "share-memo-dialog",
dialogName: "share-memo-dialog",
},
ShareMemoDialog,
{ memo }

View File

@ -139,6 +139,7 @@ function showUpdateAccountDialog() {
generateDialog(
{
className: "update-account-dialog",
dialogName: "update-account-dialog",
},
UpdateAccountDialog
);

View File

@ -7,6 +7,7 @@ import editorReducer from "./reducer/editor";
import shortcutReducer from "./reducer/shortcut";
import locationReducer from "./reducer/location";
import resourceReducer from "./reducer/resource";
import dialogReducer from "./reducer/dialog";
const store = configureStore({
reducer: {
@ -17,6 +18,7 @@ const store = configureStore({
shortcut: shortcutReducer,
location: locationReducer,
resource: resourceReducer,
dialog: dialogReducer,
},
});

View File

@ -0,0 +1,23 @@
import store, { useAppSelector } from "..";
import { pushDialogStack, popDialogStack } from "../reducer/dialog";
import { last } from "lodash";
export const useDialogStore = () => {
const state = useAppSelector((state) => state.editor);
return {
state,
getState: () => {
return store.getState().dialog;
},
pushDialogStack: (dialogName: string) => {
store.dispatch(pushDialogStack(dialogName));
},
popDialogStack: () => {
store.dispatch(popDialogStack());
},
topDialogStack: () => {
return last(store.getState().dialog.dialogStack);
},
};
};

View File

@ -5,3 +5,4 @@ export * from "./memo";
export * from "./resource";
export * from "./shortcut";
export * from "./user";
export * from "./dialog";

View File

@ -0,0 +1,30 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
interface State {
dialogStack: string[];
}
const dialogSlice = createSlice({
name: "dialog",
initialState: {
dialogStack: [],
} as State,
reducers: {
pushDialogStack: (state, action: PayloadAction<string>) => {
return {
...state,
dialogStack: [...state.dialogStack, action.payload],
};
},
popDialogStack: (state) => {
return {
...state,
dialogStack: state.dialogStack.slice(0, state.dialogStack.length - 1),
};
},
},
});
export const { pushDialogStack, popDialogStack } = dialogSlice.actions;
export default dialogSlice.reducer;