From cd521b4fca89bebe036b70695f3b45b0cfcc035d Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 7 Jul 2021 01:19:47 +0800 Subject: feat: Add dialog animations. --- FrontEnd/src/views/admin/UserAdmin.tsx | 344 ++++++++------------- FrontEnd/src/views/center/index.tsx | 14 +- FrontEnd/src/views/common/dailog/ConfirmDialog.tsx | 2 +- FrontEnd/src/views/common/dailog/Dialog.css | 20 ++ FrontEnd/src/views/common/dailog/Dialog.tsx | 51 +-- .../src/views/common/dailog/FullPageDialog.css | 4 - .../src/views/common/dailog/FullPageDialog.tsx | 8 +- FrontEnd/src/views/settings/index.tsx | 49 ++- .../src/views/timeline-common/MarkdownPostEdit.tsx | 16 +- .../timeline-common/PostPropertyChangeDialog.tsx | 5 +- .../src/views/timeline-common/TimelineMember.tsx | 2 +- .../timeline-common/TimelinePageCardTemplate.tsx | 33 +- .../src/views/timeline-common/TimelinePostView.tsx | 56 ++-- FrontEnd/src/views/timeline/TimelineCard.tsx | 16 +- 14 files changed, 268 insertions(+), 352 deletions(-) (limited to 'FrontEnd/src') diff --git a/FrontEnd/src/views/admin/UserAdmin.tsx b/FrontEnd/src/views/admin/UserAdmin.tsx index f713b8b7..68d65409 100644 --- a/FrontEnd/src/views/admin/UserAdmin.tsx +++ b/FrontEnd/src/views/admin/UserAdmin.tsx @@ -6,29 +6,17 @@ import OperationDialog, { } from "../common/dailog/OperationDialog"; import { AuthUser } from "@/services/user"; -import { - getHttpUserClient, - HttpUser, - kUserPermissionList, - UserPermission, -} from "@/http/user"; +import { getHttpUserClient, HttpUser, kUserPermissionList } from "@/http/user"; import { Trans, useTranslation } from "react-i18next"; import Button from "../common/button/Button"; import Spinner from "../common/Spinner"; import FlatButton from "../common/button/FlatButton"; -interface DialogProps { +const CreateUserDialog: React.FC<{ open: boolean; close: () => void; - data: TData; - onSuccess: (data: TReturn) => void; -} - -const CreateUserDialog: React.FC> = ({ - open, - close, - onSuccess, -}) => { + onSuccess: (user: HttpUser) => void; +}> = ({ open, close, onSuccess }) => { return ( { return {props.children}; }; -const UserDeleteDialog: React.FC> = - ({ open, close, data: { username }, onSuccess }) => { - return ( - ( - - 0{username}2 - - )} - onProcess={() => getHttpUserClient().delete(username)} - onSuccessAndClose={onSuccess} - /> - ); - }; +const UserDeleteDialog: React.FC<{ + open: boolean; + close: () => void; + user: HttpUser; + onSuccess: () => void; +}> = ({ open, close, user, onSuccess }) => { + return ( + ( + + 0{user.username}2 + + )} + onProcess={() => getHttpUserClient().delete(user.username)} + onSuccessAndClose={onSuccess} + /> + ); +}; -const UserModifyDialog: React.FC< - DialogProps< - { - oldUser: HttpUser; - }, - HttpUser - > -> = ({ open, close, data: { oldUser }, onSuccess }) => { +const UserModifyDialog: React.FC<{ + open: boolean; + close: () => void; + user: HttpUser; + onSuccess: () => void; +}> = ({ open, close, user, onSuccess }) => { return ( ( - 0{oldUser.username}2 + 0{user.username}2 )} inputScheme={ @@ -100,21 +90,21 @@ const UserModifyDialog: React.FC< { type: "text", label: "admin:user.username", - initValue: oldUser.username, + initValue: user.username, }, { type: "text", label: "admin:user.password" }, { type: "text", label: "admin:user.nickname", - initValue: oldUser.nickname, + initValue: user.nickname, }, ] as const } onProcess={([username, password, nickname]) => - getHttpUserClient().patch(oldUser.username, { - username: username !== oldUser.username ? username : undefined, + getHttpUserClient().patch(user.username, { + username: username !== user.username ? username : undefined, password: password !== "" ? password : undefined, - nickname: nickname !== oldUser.nickname ? nickname : undefined, + nickname: nickname !== user.nickname ? nickname : undefined, }) } onSuccessAndClose={onSuccess} @@ -122,17 +112,14 @@ const UserModifyDialog: React.FC< ); }; -const UserPermissionModifyDialog: React.FC< - DialogProps< - { - username: string; - permissions: UserPermission[]; - }, - UserPermission[] - > -> = ({ open, close, data: { username, permissions }, onSuccess }) => { +const UserPermissionModifyDialog: React.FC<{ + open: boolean; + close: () => void; + user: HttpUser; + onSuccess: () => void; +}> = ({ open, close, user, onSuccess }) => { const oldPermissionBoolList: boolean[] = kUserPermissionList.map( - (permission) => permissions.includes(permission) + (permission) => user.permissions.includes(permission) ); return ( @@ -143,7 +130,7 @@ const UserPermissionModifyDialog: React.FC< themeColor="danger" inputPrompt={() => ( - 0{username}2 + 0{user.username}2 )} inputScheme={kUserPermissionList.map( @@ -160,90 +147,102 @@ const UserPermissionModifyDialog: React.FC< const permission = kUserPermissionList[index]; if (oldValue === newValue) continue; if (newValue) { - await getHttpUserClient().putUserPermission(username, permission); + await getHttpUserClient().putUserPermission( + user.username, + permission + ); } else { await getHttpUserClient().deleteUserPermission( - username, + user.username, permission ); } } return newPermissionBoolList; }} - onSuccessAndClose={(newPermissionBoolList: boolean[]) => { - const permissions: UserPermission[] = []; - for (let index = 0; index < kUserPermissionList.length; index++) { - if (newPermissionBoolList[index]) { - permissions.push(kUserPermissionList[index]); - } - } - onSuccess(permissions); - }} + onSuccessAndClose={onSuccess} /> ); }; -const kModify = "modify"; -const kModifyPermission = "permission"; -const kDelete = "delete"; - -type TModify = typeof kModify; -type TModifyPermission = typeof kModifyPermission; -type TDelete = typeof kDelete; - -type ContextMenuItem = TModify | TModifyPermission | TDelete; - interface UserItemProps { - on: { [key in ContextMenuItem]: () => void }; user: HttpUser; + onChange: () => void; } -const UserItem: React.FC = ({ user, on }) => { +const UserItem: React.FC = ({ user, onChange }) => { const { t } = useTranslation(); + const [dialog, setDialog] = useState< + "delete" | "modify" | "permission" | null + >(null); + const [editMaskVisible, setEditMaskVisible] = React.useState(false); return ( -
- setEditMaskVisible(true)} - /> -
{user.username}
- - {t("admin:user.nickname")} - {user.nickname} - - - {t("admin:user.uniqueId")} - {user.uniqueId} - - - {t("admin:user.permissions")} - {user.permissions.map((permission) => { - return ( - - {permission}{" "} - - ); - })} - -
setEditMaskVisible(false)} - > - - - +
+ setEditMaskVisible(true)} /> +
{user.username}
+ + {t("admin:user.nickname")} + {user.nickname} + + + {t("admin:user.uniqueId")} + {user.uniqueId} + + + {t("admin:user.permissions")} + {user.permissions.map((permission) => { + return ( + + {permission}{" "} + + ); + })} + +
setEditMaskVisible(false)} + > + setDialog("modify")} + /> + setDialog("permission")} + /> + setDialog("delete")} + /> +
-
+ setDialog(null)} + user={user} + onSuccess={onChange} + /> + setDialog(null)} + user={user} + onSuccess={onChange} + /> + setDialog(null)} + user={user} + onSuccess={onChange} + /> + ); }; @@ -252,24 +251,8 @@ interface UserAdminProps { } const UserAdmin: React.FC = () => { - type DialogInfo = - | null - | { - type: "create"; - } - | { - type: TModify; - user: HttpUser; - } - | { - type: TModifyPermission; - username: string; - permissions: UserPermission[]; - } - | { type: TDelete; username: string }; - const [users, setUsers] = useState(null); - const [dialog, setDialog] = useState(null); + const [dialog, setDialog] = useState<"create" | null>(null); const [usersVersion, setUsersVersion] = useState(0); const updateUsers = (): void => { setUsersVersion(usersVersion + 1); @@ -289,83 +272,10 @@ const UserAdmin: React.FC = () => { }; }, [usersVersion]); - let dialogNode: React.ReactNode; - if (dialog) { - switch (dialog.type) { - case "create": - dialogNode = ( - setDialog(null)} - data={undefined} - onSuccess={updateUsers} - /> - ); - break; - case kDelete: - dialogNode = ( - setDialog(null)} - data={{ username: dialog.username }} - onSuccess={updateUsers} - /> - ); - break; - case kModify: - dialogNode = ( - setDialog(null)} - data={{ oldUser: dialog.user }} - onSuccess={updateUsers} - /> - ); - break; - case kModifyPermission: - dialogNode = ( - setDialog(null)} - data={{ - username: dialog.username, - permissions: dialog.permissions, - }} - onSuccess={updateUsers} - /> - ); - break; - } - } - if (users) { const userComponents = users.map((user) => { return ( - { - setDialog({ - type: "modify", - user, - }); - }, - permission: () => { - setDialog({ - type: kModifyPermission, - username: user.username, - permissions: user.permissions, - }); - }, - delete: () => { - setDialog({ - type: "delete", - username: user.username, - }); - }, - }} - /> + ); }); @@ -376,16 +286,16 @@ const UserAdmin: React.FC = () => {
{userComponents} - {dialogNode} + setDialog(null)} + onSuccess={updateUsers} + /> ); } else { diff --git a/FrontEnd/src/views/center/index.tsx b/FrontEnd/src/views/center/index.tsx index d226e63c..430d9781 100644 --- a/FrontEnd/src/views/center/index.tsx +++ b/FrontEnd/src/views/center/index.tsx @@ -47,14 +47,12 @@ const HomePage: React.FC = () => { - {dialog === "create" && ( - { - setDialog(null); - }} - /> - )} + { + setDialog(null); + }} + /> ); }; diff --git a/FrontEnd/src/views/common/dailog/ConfirmDialog.tsx b/FrontEnd/src/views/common/dailog/ConfirmDialog.tsx index c10b1cdb..3817ce1e 100644 --- a/FrontEnd/src/views/common/dailog/ConfirmDialog.tsx +++ b/FrontEnd/src/views/common/dailog/ConfirmDialog.tsx @@ -6,7 +6,7 @@ import Button from "../button/Button"; import Dialog from "./Dialog"; const ConfirmDialog: React.FC<{ - open?: boolean; + open: boolean; onClose: () => void; onConfirm: () => void; title: I18nText; diff --git a/FrontEnd/src/views/common/dailog/Dialog.css b/FrontEnd/src/views/common/dailog/Dialog.css index 6dd1a32e..21ea52fc 100644 --- a/FrontEnd/src/views/common/dailog/Dialog.css +++ b/FrontEnd/src/views/common/dailog/Dialog.css @@ -33,3 +33,23 @@ .cru-dialog-bottom-area > * { margin: 0 0.5em; } + +.cru-dialog-enter .cru-dialog-container { + transform: scale(0, 0); + opacity: 0; + transform-origin: center; +} + +.cru-dialog-enter-active .cru-dialog-container { + transform: scale(1, 1); + opacity: 1; + transition: transform 0.3s, opacity 0.3s; + transform-origin: center; +} + +.cru-dialog-exit-active .cru-dialog-container { + transition: transform 0.3s, opacity 0.3s; + transform: scale(0, 0); + opacity: 0; + transform-origin: center; +} diff --git a/FrontEnd/src/views/common/dailog/Dialog.tsx b/FrontEnd/src/views/common/dailog/Dialog.tsx index ee58080f..ca733e3d 100644 --- a/FrontEnd/src/views/common/dailog/Dialog.tsx +++ b/FrontEnd/src/views/common/dailog/Dialog.tsx @@ -1,11 +1,12 @@ import React from "react"; import ReactDOM from "react-dom"; +import { CSSTransition } from "react-transition-group"; import "./Dialog.css"; export interface DialogProps { onClose: () => void; - open?: boolean; + open: boolean; children?: React.ReactNode; disableCloseOnClickOnOverlay?: boolean; } @@ -13,27 +14,33 @@ export interface DialogProps { export default function Dialog(props: DialogProps): React.ReactElement | null { const { open, onClose, children, disableCloseOnClickOnOverlay } = props; - return open - ? ReactDOM.createPortal( + return ReactDOM.createPortal( + +
{ + onClose(); + } + } + >
{ - onClose(); - } - } + className="cru-dialog-container" + onClick={(e) => e.stopPropagation()} > -
e.stopPropagation()} - > - {children} -
-
, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - document.getElementById("portal")! - ) - : null; + {children} +
+ +
, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + document.getElementById("portal")! + ); } diff --git a/FrontEnd/src/views/common/dailog/FullPageDialog.css b/FrontEnd/src/views/common/dailog/FullPageDialog.css index fd125a5a..2f1fc636 100644 --- a/FrontEnd/src/views/common/dailog/FullPageDialog.css +++ b/FrontEnd/src/views/common/dailog/FullPageDialog.css @@ -42,7 +42,3 @@ transition: transform 0.3s; transform: translate(100%, 0); } - -.cru-full-page-exit-done { - display: none; -} diff --git a/FrontEnd/src/views/common/dailog/FullPageDialog.tsx b/FrontEnd/src/views/common/dailog/FullPageDialog.tsx index 2aff9f2e..45454b7d 100644 --- a/FrontEnd/src/views/common/dailog/FullPageDialog.tsx +++ b/FrontEnd/src/views/common/dailog/FullPageDialog.tsx @@ -18,7 +18,13 @@ const FullPageDialog: React.FC = ({ contentContainerClassName, }) => { return createPortal( - +
{
- {(() => { - switch (dialog) { - case "changepassword": - return setDialog(null)} />; - case "logout": - return ( - setDialog(null)} - open - onConfirm={() => { - void userService.logout().then(() => { - history.push("/"); - }); - }} - /> - ); - case "changeavatar": - return setDialog(null)} />; - case "changenickname": - return setDialog(null)} />; - default: - return null; - } - })()} + setDialog(null)} + /> + setDialog(null)} + open={dialog === "logout"} + onConfirm={() => { + void userService.logout().then(() => { + history.push("/"); + }); + }} + /> + setDialog(null)} + /> + setDialog(null)} + /> ); }; diff --git a/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx b/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx index 300b3d9d..42ba37de 100644 --- a/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx +++ b/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx @@ -190,15 +190,13 @@ const MarkdownPostEdit: React.FC = ({ }, ]} /> - {showLeaveConfirmDialog && ( - setShowLeaveConfirmDialog(false)} - onConfirm={onClose} - open - title="timeline.dropDraft" - body="timeline.confirmLeave" - /> - )} + setShowLeaveConfirmDialog(false)} + onConfirm={onClose} + open={showLeaveConfirmDialog} + title="timeline.dropDraft" + body="timeline.confirmLeave" + /> ); }; diff --git a/FrontEnd/src/views/timeline-common/PostPropertyChangeDialog.tsx b/FrontEnd/src/views/timeline-common/PostPropertyChangeDialog.tsx index 988124b6..c1dd416c 100644 --- a/FrontEnd/src/views/timeline-common/PostPropertyChangeDialog.tsx +++ b/FrontEnd/src/views/timeline-common/PostPropertyChangeDialog.tsx @@ -5,17 +5,18 @@ import { getHttpTimelineClient, HttpTimelinePostInfo } from "@/http/timeline"; import OperationDialog from "../common/dailog/OperationDialog"; function PostPropertyChangeDialog(props: { + open: boolean; onClose: () => void; post: HttpTimelinePostInfo; onSuccess: (post: HttpTimelinePostInfo) => void; }): React.ReactElement | null { - const { onClose, post, onSuccess } = props; + const { open, onClose, post, onSuccess } = props; return ( = ( props ) => { return ( - + ); diff --git a/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx b/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx index 844bece3..eb17a9d0 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx +++ b/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx @@ -134,27 +134,18 @@ const TimelinePageCardTemplate: React.FC = ({
{content}
)} - {(() => { - if (dialog === "member") { - return ( - setDialog(null)} - open - onChange={onReload} - /> - ); - } else if (dialog === "property") { - return ( - setDialog(null)} - open - onChange={onReload} - /> - ); - } - })()} + setDialog(null)} + open={dialog === "member"} + onChange={onReload} + /> + setDialog(null)} + open={dialog === "property"} + onChange={onReload} + /> ); }; diff --git a/FrontEnd/src/views/timeline-common/TimelinePostView.tsx b/FrontEnd/src/views/timeline-common/TimelinePostView.tsx index de6c3e7c..086176f8 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePostView.tsx +++ b/FrontEnd/src/views/timeline-common/TimelinePostView.tsx @@ -124,36 +124,34 @@ const TimelinePostView: React.FC = (props) => { ) : null} - {dialog === "delete" ? ( - { - setDialog(null); - setOperationMaskVisible(false); - }} - onConfirm={() => { - void getHttpTimelineClient() - .deletePost(post.timelineName, post.id) - .then(onDeleted, () => { - pushAlert({ - type: "danger", - message: "timeline.deletePostFailed", - }); + { + setDialog(null); + setOperationMaskVisible(false); + }} + onConfirm={() => { + void getHttpTimelineClient() + .deletePost(post.timelineName, post.id) + .then(onDeleted, () => { + pushAlert({ + type: "danger", + message: "timeline.deletePostFailed", }); - }} - /> - ) : dialog === "changeproperty" ? ( - { - setDialog(null); - setOperationMaskVisible(false); - }} - post={post} - onSuccess={onChanged} - /> - ) : null} + }); + }} + /> + { + setDialog(null); + setOperationMaskVisible(false); + }} + post={post} + onSuccess={onChanged} + /> ); }; diff --git a/FrontEnd/src/views/timeline/TimelineCard.tsx b/FrontEnd/src/views/timeline/TimelineCard.tsx index 56057560..339fbfa0 100644 --- a/FrontEnd/src/views/timeline/TimelineCard.tsx +++ b/FrontEnd/src/views/timeline/TimelineCard.tsx @@ -58,17 +58,11 @@ const TimelineCard: React.FC = (props) => { setDialog={setDialog} {...props} /> - {(() => { - if (dialog === "delete") { - return ( - setDialog(null)} - /> - ); - } - })()} + setDialog(null)} + /> ); }; -- cgit v1.2.3