From f5dfd52f6efece2f4cad227044ecf4dd66301bbc Mon Sep 17 00:00:00 2001
From: crupest <crupest@outlook.com>
Date: Sat, 26 Aug 2023 21:36:58 +0800
Subject: ...

---
 FrontEnd/src/components/tab/TabPages.tsx | 71 ++++++++++++++++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 FrontEnd/src/components/tab/TabPages.tsx

(limited to 'FrontEnd/src/components/tab/TabPages.tsx')

diff --git a/FrontEnd/src/components/tab/TabPages.tsx b/FrontEnd/src/components/tab/TabPages.tsx
new file mode 100644
index 00000000..6a5f4469
--- /dev/null
+++ b/FrontEnd/src/components/tab/TabPages.tsx
@@ -0,0 +1,71 @@
+import * as React from "react";
+
+import { I18nText, UiLogicError } from "~src/common";
+
+import Tabs from "./Tabs";
+
+export interface TabPage {
+  name: string;
+  text: I18nText;
+  page: React.ReactNode;
+}
+
+export interface TabPagesProps {
+  pages: TabPage[];
+  actions?: React.ReactNode;
+  dense?: boolean;
+  className?: string;
+  style?: React.CSSProperties;
+  navClassName?: string;
+  navStyle?: React.CSSProperties;
+  pageContainerClassName?: string;
+  pageContainerStyle?: React.CSSProperties;
+}
+
+const TabPages: React.FC<TabPagesProps> = ({
+  pages,
+  actions,
+  dense,
+  className,
+  style,
+  navClassName,
+  navStyle,
+  pageContainerClassName,
+  pageContainerStyle,
+}) => {
+  if (pages.length === 0) {
+    throw new UiLogicError("Page list can't be empty.");
+  }
+
+  const [tab, setTab] = React.useState<string>(pages[0].name);
+
+  const currentPage = pages.find((p) => p.name === tab);
+
+  if (currentPage == null) {
+    throw new UiLogicError("Current tab value is bad.");
+  }
+
+  return (
+    <div className={className} style={style}>
+      <Tabs
+        tabs={pages.map((page) => ({
+          name: page.name,
+          text: page.text,
+          onClick: () => {
+            setTab(page.name);
+          },
+        }))}
+        dense={dense}
+        activeTabName={tab}
+        className={navClassName}
+        style={navStyle}
+        actions={actions}
+      />
+      <div className={pageContainerClassName} style={pageContainerStyle}>
+        {currentPage.page}
+      </div>
+    </div>
+  );
+};
+
+export default TabPages;
-- 
cgit v1.2.3


From a1f69d978426c6a4cb7e8f3116e087553dbbffd5 Mon Sep 17 00:00:00 2001
From: crupest <crupest@outlook.com>
Date: Thu, 31 Aug 2023 19:18:48 +0800
Subject: ...

---
 FrontEnd/src/components/tab/TabBar.css           | 38 +++++++++++++
 FrontEnd/src/components/tab/TabBar.tsx           | 69 ++++++++++++++++++++++++
 FrontEnd/src/components/tab/TabPages.css         |  0
 FrontEnd/src/components/tab/TabPages.tsx         | 56 ++++++++-----------
 FrontEnd/src/components/tab/Tabs.css             | 33 ------------
 FrontEnd/src/components/tab/Tabs.tsx             | 62 ---------------------
 FrontEnd/src/components/tab/index.ts             |  2 +
 FrontEnd/src/pages/timeline/MarkdownPostEdit.css | 13 +++++
 FrontEnd/src/pages/timeline/MarkdownPostEdit.tsx | 10 ++--
 9 files changed, 148 insertions(+), 135 deletions(-)
 create mode 100644 FrontEnd/src/components/tab/TabBar.css
 create mode 100644 FrontEnd/src/components/tab/TabBar.tsx
 create mode 100644 FrontEnd/src/components/tab/TabPages.css
 delete mode 100644 FrontEnd/src/components/tab/Tabs.css
 delete mode 100644 FrontEnd/src/components/tab/Tabs.tsx
 create mode 100644 FrontEnd/src/components/tab/index.ts

(limited to 'FrontEnd/src/components/tab/TabPages.tsx')

diff --git a/FrontEnd/src/components/tab/TabBar.css b/FrontEnd/src/components/tab/TabBar.css
new file mode 100644
index 00000000..09d48c59
--- /dev/null
+++ b/FrontEnd/src/components/tab/TabBar.css
@@ -0,0 +1,38 @@
+.cru-tab-bar {
+  border-bottom: var(--cru-clickable-normal-color) 1px solid;
+  display: flex;
+}
+
+.cru-tab-bar-tab-area {
+  display: flex;
+  align-items: center;
+  gap: 0.5em;
+  border: var(--cru-clickable-normal-color) 1px solid;
+  border-bottom: none;
+  border-top-left-radius: 5px;
+  border-top-right-radius: 5px;
+  padding: 0.2em 0.5em;
+}
+
+.cru-tab-bar-item {
+  color: var(--cru-clickable-normal-color);
+  transition: all 0.5s;
+  border-radius: 5px;
+  cursor: pointer;
+  padding: 0.3em 1em;
+}
+
+.cru-tab-bar-item:hover {
+  color: var(--cru-push-button-text-color);
+  background-color: var(--cru-clickable-normal-color);
+}
+
+.cru-tab-bar-item.active {
+  color: var(--cru-push-button-text-color);
+  background-color: var(--cru-clickable-normal-color);
+  border-color: var(--cru-primary-color);
+}
+
+.cru-tab-bar-action-area {
+  margin-left: auto;
+}
diff --git a/FrontEnd/src/components/tab/TabBar.tsx b/FrontEnd/src/components/tab/TabBar.tsx
new file mode 100644
index 00000000..601f664d
--- /dev/null
+++ b/FrontEnd/src/components/tab/TabBar.tsx
@@ -0,0 +1,69 @@
+import { ReactNode } from "react";
+import { Link } from "react-router-dom";
+import classNames from "classnames";
+
+import { Text, ThemeColor, useC } from "../common";
+
+import "./TabBar.css";
+
+export interface Tab {
+  name: string;
+  text: Text;
+  link?: string;
+  onClick?: () => void;
+}
+
+export interface TabsProps {
+  activeTabName?: string;
+  tabs: Tab[];
+  color?: ThemeColor;
+  actions?: ReactNode;
+  dense?: boolean;
+  className?: string;
+}
+
+export default function TabBar(props: TabsProps) {
+  const { tabs, color, activeTabName, className, dense, actions } = props;
+
+  const c = useC();
+
+  return (
+    <div
+      className={classNames(
+        "cru-tab-bar",
+        dense && "dense",
+        `cru-clickable-${color ?? "primary"}`,
+        className,
+      )}
+    >
+      <div className="cru-tab-bar-tab-area">
+        {tabs.map((tab) => {
+          const { name, text, link, onClick } = tab;
+
+          const active = activeTabName === name;
+          const className = classNames("cru-tab-bar-item", active && "active");
+
+          if (link != null) {
+            return (
+              <Link
+                key={name}
+                to={link}
+                onClick={onClick}
+                className={className}
+              >
+                {c(text)}
+              </Link>
+            );
+          } else {
+            return (
+              <span key={name} onClick={onClick} className={className}>
+                {c(text)}
+              </span>
+            );
+          }
+        })}
+      </div>
+      <div className="cru-tab-bar-action-area">{actions}</div>
+    </div>
+  );
+}
diff --git a/FrontEnd/src/components/tab/TabPages.css b/FrontEnd/src/components/tab/TabPages.css
new file mode 100644
index 00000000..e69de29b
diff --git a/FrontEnd/src/components/tab/TabPages.tsx b/FrontEnd/src/components/tab/TabPages.tsx
index 6a5f4469..ab45ffdf 100644
--- a/FrontEnd/src/components/tab/TabPages.tsx
+++ b/FrontEnd/src/components/tab/TabPages.tsx
@@ -1,52 +1,43 @@
-import * as React from "react";
+import { ReactNode, useState } from "react";
+import classNames from "classnames";
 
-import { I18nText, UiLogicError } from "~src/common";
+import { Text, UiLogicError } from "../common";
 
-import Tabs from "./Tabs";
+import Tabs from "./TabBar";
 
-export interface TabPage {
+import "./TabPages.css";
+
+interface TabPage {
   name: string;
-  text: I18nText;
-  page: React.ReactNode;
+  text: Text;
+  page: ReactNode;
 }
 
-export interface TabPagesProps {
+interface TabPagesProps {
   pages: TabPage[];
-  actions?: React.ReactNode;
+  actions?: ReactNode;
   dense?: boolean;
   className?: string;
-  style?: React.CSSProperties;
-  navClassName?: string;
-  navStyle?: React.CSSProperties;
+  tabBarClassName?: string;
   pageContainerClassName?: string;
-  pageContainerStyle?: React.CSSProperties;
 }
 
-const TabPages: React.FC<TabPagesProps> = ({
+export default function TabPages({
   pages,
   actions,
   dense,
   className,
-  style,
-  navClassName,
-  navStyle,
+  tabBarClassName,
   pageContainerClassName,
-  pageContainerStyle,
-}) => {
-  if (pages.length === 0) {
-    throw new UiLogicError("Page list can't be empty.");
-  }
-
-  const [tab, setTab] = React.useState<string>(pages[0].name);
+}: TabPagesProps) {
+  const [tab, setTab] = useState<string>(pages[0].name);
 
   const currentPage = pages.find((p) => p.name === tab);
 
-  if (currentPage == null) {
-    throw new UiLogicError("Current tab value is bad.");
-  }
+  if (currentPage == null) throw new UiLogicError();
 
   return (
-    <div className={className} style={style}>
+    <div className={className}>
       <Tabs
         tabs={pages.map((page) => ({
           name: page.name,
@@ -57,15 +48,14 @@ const TabPages: React.FC<TabPagesProps> = ({
         }))}
         dense={dense}
         activeTabName={tab}
-        className={navClassName}
-        style={navStyle}
+        className={tabBarClassName}
         actions={actions}
       />
-      <div className={pageContainerClassName} style={pageContainerStyle}>
+      <div
+        className={classNames("cru-tab-page-container", pageContainerClassName)}
+      >
         {currentPage.page}
       </div>
     </div>
   );
-};
-
-export default TabPages;
+}
diff --git a/FrontEnd/src/components/tab/Tabs.css b/FrontEnd/src/components/tab/Tabs.css
deleted file mode 100644
index 395d16a7..00000000
--- a/FrontEnd/src/components/tab/Tabs.css
+++ /dev/null
@@ -1,33 +0,0 @@
-.cru-nav {
-  border-bottom: var(--cru-primary-color) 1px solid;
-  display: flex;
-}
-
-.cru-nav-item {
-  color: var(--cru-primary-color);
-  border: var(--cru-background-2-color) 0.5px solid;
-  border-bottom: none;
-  padding: 0.5em 1.5em;
-  border-top-left-radius: 5px;
-  border-top-right-radius: 5px;
-  transition: all 0.5s;
-  cursor: pointer;
-}
-
-.cru-nav.dense .cru-nav-item {
-  padding: 0.2em 1em;
-}
-
-.cru-nav-item:hover {
-  background-color: var(--cru-background-1-color);
-}
-
-.cru-nav-item.active {
-  color: var(--cru-primary-t-color);
-  background-color: var(--cru-primary-color);
-  border-color: var(--cru-primary-color);
-}
-
-.cru-nav-action-area {
-  margin-left: auto;
-}
diff --git a/FrontEnd/src/components/tab/Tabs.tsx b/FrontEnd/src/components/tab/Tabs.tsx
deleted file mode 100644
index dc8d9c01..00000000
--- a/FrontEnd/src/components/tab/Tabs.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import * as React from "react";
-import { Link } from "react-router-dom";
-import { useTranslation } from "react-i18next";
-import classnames from "classnames";
-
-import { convertI18nText, I18nText } from "~src/common";
-
-import "./Tabs.css";
-
-export interface Tab {
-  name: string;
-  text: I18nText;
-  link?: string;
-  onClick?: () => void;
-}
-
-export interface TabsProps {
-  activeTabName?: string;
-  actions?: React.ReactNode;
-  dense?: boolean;
-  tabs: Tab[];
-  className?: string;
-  style?: React.CSSProperties;
-}
-
-export default function Tabs(props: TabsProps): React.ReactElement | null {
-  const { tabs, activeTabName, className, style, dense, actions } = props;
-
-  const { t } = useTranslation();
-
-  return (
-    <div
-      className={classnames("cru-nav", dense && "dense", className)}
-      style={style}
-    >
-      {tabs.map((tab) => {
-        const active = activeTabName === tab.name;
-        const className = classnames("cru-nav-item", active && "active");
-
-        if (tab.link != null) {
-          return (
-            <Link
-              key={tab.name}
-              to={tab.link}
-              onClick={tab.onClick}
-              className={className}
-            >
-              {convertI18nText(tab.text, t)}
-            </Link>
-          );
-        } else {
-          return (
-            <span key={tab.name} onClick={tab.onClick} className={className}>
-              {convertI18nText(tab.text, t)}
-            </span>
-          );
-        }
-      })}
-      <div className="cru-nav-action-area">{actions}</div>
-    </div>
-  );
-}
diff --git a/FrontEnd/src/components/tab/index.ts b/FrontEnd/src/components/tab/index.ts
new file mode 100644
index 00000000..43d545cc
--- /dev/null
+++ b/FrontEnd/src/components/tab/index.ts
@@ -0,0 +1,2 @@
+export { default as TabBar } from "./TabBar";
+export { default as TabPages } from "./TabPages";
diff --git a/FrontEnd/src/pages/timeline/MarkdownPostEdit.css b/FrontEnd/src/pages/timeline/MarkdownPostEdit.css
index e36be992..33a77943 100644
--- a/FrontEnd/src/pages/timeline/MarkdownPostEdit.css
+++ b/FrontEnd/src/pages/timeline/MarkdownPostEdit.css
@@ -3,6 +3,19 @@
   max-height: 300px;
 }
 
+.timeline-post-create-markdown-edit-area {
+  border: 1px solid var(--cru-clickable-primary-normal-color);
+  border-top: none;
+  border-bottom-left-radius: 5px;
+  border-bottom-right-radius: 5px;
+  padding: 0.6em;
+}
+
+.timeline-post-create-markdown-edit-area:hover {
+  border: 1px solid var(--cru-clickable-primary-normal-color);
+  border-top: none;
+}
+
 .timeline-markdown-post-edit-image-container {
   position: relative;
   text-align: center;
diff --git a/FrontEnd/src/pages/timeline/MarkdownPostEdit.tsx b/FrontEnd/src/pages/timeline/MarkdownPostEdit.tsx
index fc7b882f..d10d3f2d 100644
--- a/FrontEnd/src/pages/timeline/MarkdownPostEdit.tsx
+++ b/FrontEnd/src/pages/timeline/MarkdownPostEdit.tsx
@@ -10,13 +10,13 @@ import {
 import TimelinePostBuilder from "~src/services/TimelinePostBuilder";
 
 import FlatButton from "~src/components/button/FlatButton";
-import TabPages from "~src/components/tab/TabPages";
+import { TabPages } from "~src/components/tab";
 import ConfirmDialog from "~src/components/dialog/ConfirmDialog";
 import Spinner from "~src/components/Spinner";
 import IconButton from "~src/components/button/IconButton";
+import { DialogProvider, useDialog } from "~src/components/dialog";
 
 import "./MarkdownPostEdit.css";
-import { DialogProvider, useDialog } from "~src/components/dialog";
 
 export interface MarkdownPostEditProps {
   owner: string;
@@ -25,7 +25,6 @@ export interface MarkdownPostEditProps {
   onPostError: () => void;
   onClose: () => void;
   className?: string;
-  style?: React.CSSProperties;
 }
 
 const MarkdownPostEdit: React.FC<MarkdownPostEditProps> = ({
@@ -35,7 +34,6 @@ const MarkdownPostEdit: React.FC<MarkdownPostEditProps> = ({
   onClose,
   onPostError,
   className,
-  style,
 }) => {
   const { t } = useTranslation();
 
@@ -117,8 +115,6 @@ const MarkdownPostEdit: React.FC<MarkdownPostEditProps> = ({
     <>
       <TabPages
         className={className}
-        style={style}
-        pageContainerClassName="py-2"
         dense
         actions={
           process ? (
@@ -152,7 +148,7 @@ const MarkdownPostEdit: React.FC<MarkdownPostEditProps> = ({
               <textarea
                 value={text}
                 disabled={process}
-                className="cru-fill-parent"
+                className="timeline-post-create-markdown-edit-area cru-fill-parent"
                 onChange={(event) => {
                   getBuilder().setMarkdownText(event.currentTarget.value);
                 }}
-- 
cgit v1.2.3

