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:
2026-03-17 09:26:39 +08:00
parent 8b096fcf53
commit 95acfd2b3b
4 changed files with 61 additions and 85 deletions

View File

@@ -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}

View File

@@ -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"
}

View File

@@ -57,5 +57,7 @@
"createConnectionFailed": "接続に失敗しました", "createConnectionFailed": "接続に失敗しました",
"domainDuplicate": "このドメインとユーザー名の組み合わせは既に存在します", "domainDuplicate": "このドメインとユーザー名の組み合わせは既に存在します",
"createFailed": "作成に失敗しました", "createFailed": "作成に失敗しました",
"updateFailed": "更新に失敗しました" "updateFailed": "更新に失敗しました",
} "expand": "展開",
"collapse": "折りたたむ"
}

View File

@@ -57,5 +57,7 @@
"createConnectionFailed": "创建时连接失败", "createConnectionFailed": "创建时连接失败",
"domainDuplicate": "该域名和用户名组合已存在", "domainDuplicate": "该域名和用户名组合已存在",
"createFailed": "创建失败", "createFailed": "创建失败",
"updateFailed": "更新失败" "updateFailed": "更新失败",
} "expand": "展开",
"collapse": "折叠"
}