refactor(DomainManager): improve expand/collapse UI with card style and chevron toggles
- Collapsed state: card style, ChevronDown button, no hover effect - Expanded state: card style, ChevronUp in header - Selected domain: 3px left indicator bar with primary color - Remove bottom toggle areas - Add expand/collapse translation keys
This commit is contained in:
@@ -2,19 +2,14 @@
|
|||||||
* DomainManager Component
|
* DomainManager Component
|
||||||
* Main container for domain management
|
* Main container for domain management
|
||||||
* Supports collapsed/expanded view
|
* Supports collapsed/expanded view
|
||||||
* Expand/collapse triggered by clicking bottom area
|
* Expand/collapse triggered by clicking header chevron
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Spin, Space } from "antd";
|
import { Spin } from "antd";
|
||||||
import { Button, Tooltip, Avatar, Empty } from "@lobehub/ui";
|
import { Button, Tooltip, Avatar, Empty } from "@lobehub/ui";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import { Plus, Cloud, ChevronUp, ChevronDown } from "lucide-react";
|
||||||
Plus,
|
|
||||||
Cloud,
|
|
||||||
ChevronUp,
|
|
||||||
ChevronDown,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { createStyles } from "antd-style";
|
import { createStyles } from "antd-style";
|
||||||
import { useDomainStore, useUIStore } from "@renderer/stores";
|
import { useDomainStore, useUIStore } from "@renderer/stores";
|
||||||
import DomainList from "./DomainList";
|
import DomainList from "./DomainList";
|
||||||
@@ -25,13 +20,17 @@ const useStyles = createStyles(({ token, css }) => ({
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: ${token.colorBgContainer};
|
background: ${token.colorFillSecondary};
|
||||||
|
border-radius: ${token.borderRadiusLG}px;
|
||||||
|
padding: ${token.paddingSM}px;
|
||||||
`,
|
`,
|
||||||
collapsedContainer: css`
|
collapsedContainer: css`
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: ${token.colorBgContainer};
|
background: ${token.colorFillSecondary};
|
||||||
|
border-radius: ${token.borderRadiusLG}px;
|
||||||
|
padding: ${token.paddingSM}px;
|
||||||
position: relative;
|
position: relative;
|
||||||
`,
|
`,
|
||||||
header: css`
|
header: css`
|
||||||
@@ -70,10 +69,6 @@ const useStyles = createStyles(({ token, css }) => ({
|
|||||||
padding: ${token.paddingSM}px ${token.paddingMD}px;
|
padding: ${token.paddingSM}px ${token.paddingMD}px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background 0.2s;
|
transition: background 0.2s;
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: ${token.colorBgTextHover};
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
collapsedInfo: css`
|
collapsedInfo: css`
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -105,31 +100,6 @@ const useStyles = createStyles(({ token, css }) => ({
|
|||||||
font-size: ${token.fontSizeSM}px;
|
font-size: ${token.fontSizeSM}px;
|
||||||
padding: ${token.paddingSM}px ${token.paddingMD}px;
|
padding: ${token.paddingSM}px ${token.paddingMD}px;
|
||||||
`,
|
`,
|
||||||
// Bottom toggle icon - minimal style
|
|
||||||
bottomToggleIcon: css`
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 20px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
cursor: pointer;
|
|
||||||
color: ${token.colorTextTertiary};
|
|
||||||
font-size: 12px;
|
|
||||||
z-index: 10;
|
|
||||||
opacity: 0.6;
|
|
||||||
transition: all 0.2s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
background: ${token.colorFillTertiary};
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
bottomToggleVisible: css`
|
|
||||||
opacity: 0.6;
|
|
||||||
`,
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
interface DomainManagerProps {
|
interface DomainManagerProps {
|
||||||
@@ -147,7 +117,6 @@ const DomainManager: React.FC<DomainManagerProps> = ({
|
|||||||
const { domainIconColors } = useUIStore();
|
const { domainIconColors } = useUIStore();
|
||||||
const [formOpen, setFormOpen] = React.useState(false);
|
const [formOpen, setFormOpen] = React.useState(false);
|
||||||
const [editingDomain, setEditingDomain] = React.useState<string | null>(null);
|
const [editingDomain, setEditingDomain] = React.useState<string | null>(null);
|
||||||
const [isHoveringToggle, setIsHoveringToggle] = React.useState(false);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
loadDomains();
|
loadDomains();
|
||||||
@@ -177,7 +146,7 @@ const DomainManager: React.FC<DomainManagerProps> = ({
|
|||||||
if (collapsed) {
|
if (collapsed) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.collapsedContainer}>
|
<div className={styles.collapsedContainer}>
|
||||||
<div className={styles.collapsedHeader}>
|
<div className={styles.collapsedHeader} onClick={onToggleCollapse}>
|
||||||
<div className={styles.collapsedInfo}>
|
<div className={styles.collapsedInfo}>
|
||||||
<Avatar
|
<Avatar
|
||||||
icon={<Cloud size={14} />}
|
icon={<Cloud size={14} />}
|
||||||
@@ -203,27 +172,30 @@ const DomainManager: React.FC<DomainManagerProps> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Tooltip title={t("addDomain")}>
|
<div className={styles.collapsedActions}>
|
||||||
<Button
|
<Tooltip title={t("expand")}>
|
||||||
type="text"
|
<Button
|
||||||
size="small"
|
type="text"
|
||||||
icon={<Plus size={16} />}
|
size="small"
|
||||||
onClick={(e) => {
|
icon={<ChevronDown size={16} />}
|
||||||
e.stopPropagation();
|
onClick={(e) => {
|
||||||
handleAdd();
|
e.stopPropagation();
|
||||||
}}
|
onToggleCollapse?.();
|
||||||
/>
|
}}
|
||||||
</Tooltip>
|
/>
|
||||||
</div>
|
</Tooltip>
|
||||||
|
<Tooltip title={t("addDomain")}>
|
||||||
{/* Bottom toggle icon */}
|
<Button
|
||||||
<div
|
type="text"
|
||||||
className={`${styles.bottomToggleIcon} ${isHoveringToggle ? styles.bottomToggleVisible : ""}`}
|
size="small"
|
||||||
onClick={onToggleCollapse}
|
icon={<Plus size={16} />}
|
||||||
onMouseEnter={() => setIsHoveringToggle(true)}
|
onClick={(e) => {
|
||||||
onMouseLeave={() => setIsHoveringToggle(false)}
|
e.stopPropagation();
|
||||||
>
|
handleAdd();
|
||||||
<ChevronDown size={16} />
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DomainForm
|
<DomainForm
|
||||||
@@ -237,12 +209,23 @@ const DomainManager: React.FC<DomainManagerProps> = ({
|
|||||||
|
|
||||||
// Expanded view - full list
|
// Expanded view - full list
|
||||||
return (
|
return (
|
||||||
<div className={styles.container} style={{ position: "relative" }}>
|
<div className={styles.container}>
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<h2 className={styles.title}>{t("domainManagement")}</h2>
|
<h2 className={styles.title}>{t("domainManagement")}</h2>
|
||||||
<div className={styles.actions}>
|
<div className={styles.actions}>
|
||||||
<Button type="primary" icon={<Plus size={16} />} onClick={handleAdd}>
|
<Tooltip title={t("collapse")}>
|
||||||
</Button>
|
<Button
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
icon={<ChevronUp size={16} />}
|
||||||
|
onClick={onToggleCollapse}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
icon={<Plus size={16} />}
|
||||||
|
onClick={handleAdd}
|
||||||
|
></Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -252,25 +235,12 @@ const DomainManager: React.FC<DomainManagerProps> = ({
|
|||||||
<Spin size="large" />
|
<Spin size="large" />
|
||||||
</div>
|
</div>
|
||||||
) : domains.length === 0 ? (
|
) : domains.length === 0 ? (
|
||||||
<Empty
|
<Empty description={t("noDomainConfig")}></Empty>
|
||||||
description={t("noDomainConfig")}
|
|
||||||
>
|
|
||||||
</Empty>
|
|
||||||
) : (
|
) : (
|
||||||
<DomainList onEdit={handleEdit} />
|
<DomainList onEdit={handleEdit} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bottom toggle icon */}
|
|
||||||
<div
|
|
||||||
className={`${styles.bottomToggleIcon} ${isHoveringToggle ? styles.bottomToggleVisible : ""}`}
|
|
||||||
onClick={onToggleCollapse}
|
|
||||||
onMouseEnter={() => setIsHoveringToggle(true)}
|
|
||||||
onMouseLeave={() => setIsHoveringToggle(false)}
|
|
||||||
>
|
|
||||||
<ChevronUp size={16} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<DomainForm
|
<DomainForm
|
||||||
open={formOpen}
|
open={formOpen}
|
||||||
onClose={handleCloseForm}
|
onClose={handleCloseForm}
|
||||||
|
|||||||
@@ -57,5 +57,7 @@
|
|||||||
"createConnectionFailed": "Connection failed during creation",
|
"createConnectionFailed": "Connection failed during creation",
|
||||||
"domainDuplicate": "This domain and username combination already exists",
|
"domainDuplicate": "This domain and username combination already exists",
|
||||||
"createFailed": "Creation failed",
|
"createFailed": "Creation failed",
|
||||||
"updateFailed": "Update failed"
|
"updateFailed": "Update failed",
|
||||||
|
"expand": "Expand",
|
||||||
|
"collapse": "Collapse"
|
||||||
}
|
}
|
||||||
@@ -57,5 +57,7 @@
|
|||||||
"createConnectionFailed": "接続に失敗しました",
|
"createConnectionFailed": "接続に失敗しました",
|
||||||
"domainDuplicate": "このドメインとユーザー名の組み合わせは既に存在します",
|
"domainDuplicate": "このドメインとユーザー名の組み合わせは既に存在します",
|
||||||
"createFailed": "作成に失敗しました",
|
"createFailed": "作成に失敗しました",
|
||||||
"updateFailed": "更新に失敗しました"
|
"updateFailed": "更新に失敗しました",
|
||||||
|
"expand": "展開",
|
||||||
|
"collapse": "折りたたむ"
|
||||||
}
|
}
|
||||||
@@ -57,5 +57,7 @@
|
|||||||
"createConnectionFailed": "创建时连接失败",
|
"createConnectionFailed": "创建时连接失败",
|
||||||
"domainDuplicate": "该域名和用户名组合已存在",
|
"domainDuplicate": "该域名和用户名组合已存在",
|
||||||
"createFailed": "创建失败",
|
"createFailed": "创建失败",
|
||||||
"updateFailed": "更新失败"
|
"updateFailed": "更新失败",
|
||||||
|
"expand": "展开",
|
||||||
|
"collapse": "折叠"
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user