From e83da106a259f4a8ab11324eceef2c15a9a08bf0 Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 18 Mar 2021 21:01:07 +0800 Subject: ... --- FrontEnd/src/app/views/common/TabPages.tsx | 74 ++++++++++++++++++++++++++++++ FrontEnd/src/app/views/common/common.sass | 4 ++ 2 files changed, 78 insertions(+) create mode 100644 FrontEnd/src/app/views/common/TabPages.tsx (limited to 'FrontEnd/src/app/views/common') diff --git a/FrontEnd/src/app/views/common/TabPages.tsx b/FrontEnd/src/app/views/common/TabPages.tsx new file mode 100644 index 00000000..424e769f --- /dev/null +++ b/FrontEnd/src/app/views/common/TabPages.tsx @@ -0,0 +1,74 @@ +import React from "react"; +import { Nav } from "react-bootstrap"; +import { useTranslation } from "react-i18next"; + +import { convertI18nText, I18nText, UiLogicError } from "@/common"; + +export interface TabPage { + id: string; + tabText: I18nText; + page: React.ReactNode; +} + +export interface TabPagesProps { + pages: TabPage[]; + actions?: React.ReactNode; + className?: string; + style?: React.CSSProperties; + navClassName?: string; + navStyle?: React.CSSProperties; + pageContainerClassName?: string; + pageContainerStyle?: React.CSSProperties; +} + +const TabPages: React.FC = ({ + pages, + actions, + className, + style, + navClassName, + navStyle, + pageContainerClassName, + pageContainerStyle, +}) => { + if (pages.length === 0) { + throw new UiLogicError("Page list can't be empty."); + } + + const { t } = useTranslation(); + + const [tab, setTab] = React.useState(pages[0].id); + + const currentPage = pages.find((p) => p.id === tab); + + if (currentPage == null) { + throw new UiLogicError("Current tab value is bad."); + } + + return ( +
+ +
+ {currentPage.page} +
+
+ ); +}; + +export default TabPages; diff --git a/FrontEnd/src/app/views/common/common.sass b/FrontEnd/src/app/views/common/common.sass index 0a30d995..f3022d19 100644 --- a/FrontEnd/src/app/views/common/common.sass +++ b/FrontEnd/src/app/views/common/common.sass @@ -92,3 +92,7 @@ .cru-menu-divider border-top: 1px solid $gray-200 + +.cru-tab-pages-action-area + display: flex + align-items: center -- cgit v1.2.3 From cf161b23fb1eaecec1c4b4ab3cf5ec620cd897bc Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 18 Mar 2021 21:28:30 +0800 Subject: feat: Prevent leave. --- FrontEnd/src/app/locales/en/translation.json | 1 + FrontEnd/src/app/locales/zh/translation.json | 3 +- FrontEnd/src/app/views/common/ConfirmDialog.tsx | 40 ++++++++++++++++++++++ .../app/views/timeline-common/MarkdownPostEdit.tsx | 25 +++++++++++++- 4 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 FrontEnd/src/app/views/common/ConfirmDialog.tsx (limited to 'FrontEnd/src/app/views/common') diff --git a/FrontEnd/src/app/locales/en/translation.json b/FrontEnd/src/app/locales/en/translation.json index 8fcd5bcd..65ddbe0c 100644 --- a/FrontEnd/src/app/locales/en/translation.json +++ b/FrontEnd/src/app/locales/en/translation.json @@ -67,6 +67,7 @@ "send": "Send", "deletePostFailed": "Failed to delete post.", "sendPostFailed": "Failed to send post.", + "dropDraft": "Drop Draft", "confirmLeave": "Are you sure to leave? All content you typed would be lost.", "visibility": { "public": "public to everyone", diff --git a/FrontEnd/src/app/locales/zh/translation.json b/FrontEnd/src/app/locales/zh/translation.json index f03c4c03..f6971241 100644 --- a/FrontEnd/src/app/locales/zh/translation.json +++ b/FrontEnd/src/app/locales/zh/translation.json @@ -67,7 +67,8 @@ "send": "发送", "deletePostFailed": "删除消息失败。", "sendPostFailed": "发送消息失败。", - "confirmLeave":"确定要离开吗?所有输入的内容将会丢失。", + "dropDraft": "放弃草稿", + "confirmLeave": "确定要离开吗?所有输入的内容将会丢失。", "visibility": { "public": "对所有人公开", "register": "仅注册可见", diff --git a/FrontEnd/src/app/views/common/ConfirmDialog.tsx b/FrontEnd/src/app/views/common/ConfirmDialog.tsx new file mode 100644 index 00000000..72940c51 --- /dev/null +++ b/FrontEnd/src/app/views/common/ConfirmDialog.tsx @@ -0,0 +1,40 @@ +import { convertI18nText, I18nText } from "@/common"; +import React from "react"; +import { Modal, Button } from "react-bootstrap"; +import { useTranslation } from "react-i18next"; + +const ConfirmDialog: React.FC<{ + onClose: () => void; + onConfirm: () => void; + title: I18nText; + body: I18nText; +}> = ({ onClose, onConfirm, title, body }) => { + const { t } = useTranslation(); + + return ( + + + + {convertI18nText(title, t)} + + + {convertI18nText(body, t)} + + + + + + ); +}; + +export default ConfirmDialog; diff --git a/FrontEnd/src/app/views/timeline-common/MarkdownPostEdit.tsx b/FrontEnd/src/app/views/timeline-common/MarkdownPostEdit.tsx index ab6aafea..bad6b2b0 100644 --- a/FrontEnd/src/app/views/timeline-common/MarkdownPostEdit.tsx +++ b/FrontEnd/src/app/views/timeline-common/MarkdownPostEdit.tsx @@ -7,6 +7,7 @@ import { getHttpTimelineClient, HttpTimelinePostInfo } from "@/http/timeline"; import TabPages from "../common/TabPages"; import TimelinePostBuilder from "@/services/TimelinePostBuilder"; +import ConfirmDialog from "../common/ConfirmDialog"; export interface MarkdownPostEditProps { timeline: string; @@ -31,6 +32,11 @@ const MarkdownPostEdit: React.FC = ({ const [process, setProcess] = React.useState(false); + const [ + showLeaveConfirmDialog, + setShowLeaveConfirmDialog, + ] = React.useState(false); + const [text, _setText] = React.useState(""); const [images, _setImages] = React.useState<{ file: File; url: string }[]>( [] @@ -94,7 +100,16 @@ const MarkdownPostEdit: React.FC = ({ pageContainerClassName="py-2" actions={ <> -
+
{ + if (canLeave) { + onClose(); + } else { + setShowLeaveConfirmDialog(true); + } + }} + > {t("operationDialog.cancel")}
@@ -155,6 +170,14 @@ const MarkdownPostEdit: React.FC = ({ }, ]} /> + {showLeaveConfirmDialog && ( + setShowLeaveConfirmDialog(false)} + onConfirm={onClose} + title="timeline.dropDraft" + body="timeline.confirmLeave" + /> + )} ); }; -- cgit v1.2.3 From 4aecd115b435b3217329824ddf54c87b32e77b89 Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 18 Mar 2021 21:41:11 +0800 Subject: feat: Prevent send empty. --- FrontEnd/src/app/index.sass | 17 +++++++--- FrontEnd/src/app/views/common/FlatButton.tsx | 36 ++++++++++++++++++++++ .../app/views/timeline-common/MarkdownPostEdit.tsx | 12 +++++--- 3 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 FrontEnd/src/app/views/common/FlatButton.tsx (limited to 'FrontEnd/src/app/views/common') diff --git a/FrontEnd/src/app/index.sass b/FrontEnd/src/app/index.sass index 60f274c2..2079cad8 100644 --- a/FrontEnd/src/app/index.sass +++ b/FrontEnd/src/app/index.sass @@ -45,8 +45,15 @@ small cursor: pointer padding: 0.2em 0.5em border-radius: 0.2em - &:hover + &:hover:not(.disabled) background-color: $gray-200 + &.disabled + cursor: default + @each $color, $value in $theme-colors + &.#{$color} + color: $value + &.disabled + color: adjust-color($value, $lightness: +15%) .cursor-pointer cursor: pointer @@ -81,10 +88,10 @@ textarea .text-yellow color: $yellow -@each $color, $value in $theme-colors - .text-button - background: transparent - border: none +.text-button + background: transparent + border: none + @each $color, $value in $theme-colors &.#{$color} color: $value &:hover diff --git a/FrontEnd/src/app/views/common/FlatButton.tsx b/FrontEnd/src/app/views/common/FlatButton.tsx new file mode 100644 index 00000000..80bb654c --- /dev/null +++ b/FrontEnd/src/app/views/common/FlatButton.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import clsx from "clsx"; + +import { BootstrapThemeColor } from "@/common"; + +export interface FlatButtonProps { + variant?: BootstrapThemeColor | string; + disabled?: boolean; + className?: string; + style?: React.CSSProperties; + onClick?: () => void; +} + +const FlatButton: React.FC = (props) => { + const { disabled, className, style } = props; + const variant = props.variant ?? "primary"; + + const onClick = disabled ? undefined : props.onClick; + + return ( +
+ {props.children} +
+ ); +}; + +export default FlatButton; diff --git a/FrontEnd/src/app/views/timeline-common/MarkdownPostEdit.tsx b/FrontEnd/src/app/views/timeline-common/MarkdownPostEdit.tsx index bad6b2b0..f4351db0 100644 --- a/FrontEnd/src/app/views/timeline-common/MarkdownPostEdit.tsx +++ b/FrontEnd/src/app/views/timeline-common/MarkdownPostEdit.tsx @@ -5,6 +5,7 @@ import { Prompt } from "react-router"; import { getHttpTimelineClient, HttpTimelinePostInfo } from "@/http/timeline"; +import FlatButton from "../common/FlatButton"; import TabPages from "../common/TabPages"; import TimelinePostBuilder from "@/services/TimelinePostBuilder"; import ConfirmDialog from "../common/ConfirmDialog"; @@ -100,8 +101,9 @@ const MarkdownPostEdit: React.FC = ({ pageContainerClassName="py-2" actions={ <> -
{ if (canLeave) { onClose(); @@ -111,10 +113,10 @@ const MarkdownPostEdit: React.FC = ({ }} > {t("operationDialog.cancel")} -
-
+ + {t("timeline.send")} -
+ } pages={[ -- cgit v1.2.3 From 0a944720fecb3a2fdfdc483449c5ab0e58df7c02 Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 18 Mar 2021 21:48:46 +0800 Subject: fix: Fix a list with no key. --- FrontEnd/src/app/views/common/Menu.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'FrontEnd/src/app/views/common') diff --git a/FrontEnd/src/app/views/common/Menu.tsx b/FrontEnd/src/app/views/common/Menu.tsx index 54650f22..d942d452 100644 --- a/FrontEnd/src/app/views/common/Menu.tsx +++ b/FrontEnd/src/app/views/common/Menu.tsx @@ -30,12 +30,13 @@ const Menu: React.FC = ({ items, className, onItemClicked }) => { return (
- {items.map((item) => { + {items.map((item, index) => { if (item.type === "divider") { - return
; + return
; } else { return (