refactory: 结构和css in js

This commit is contained in:
zhaoyupeng 2024-10-09 18:01:55 +08:00
parent 9079a82434
commit efe7800b59
13 changed files with 318 additions and 226 deletions

View File

@ -6,7 +6,7 @@
<title>nf-disk</title> <title>nf-disk</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root" style="height: 100%"></div>
<script src="./src/main.tsx" type="module"></script> <script src="./src/main.tsx" type="module"></script>
</body> </body>
</html> </html>

View File

@ -1,28 +0,0 @@
div.connection-container {
display: flex;
height: 100vh;
width: 100%;
justify-content: center;
align-items: center;
}
div.connection-form {
max-width: 700px;
min-width: 500px;
height: 100%;
display: flex;
flex: 1;
flex-direction: column;
justify-content: center;
align-items: center;
}
div.connection-form-field {
width: 100%;
margin-top: 20px;
}
div.connection-form-field-actions {
display: flex;
margin-top: 50px;
}

View File

@ -0,0 +1,150 @@
import {Button, Input, makeStyles, MenuItem, MenuList, mergeClasses, tokens, Tooltip} from "@fluentui/react-components"
import {DatabaseLinkRegular, DismissRegular} from "@fluentui/react-icons";
import {useState} from "react";
import {Connection} from "../../interfaces/connection";
import {useToast} from "../../message";
import {Dial} from "../../api";
import {useStoreConnection} from "../../store/connection";
import {useStoreBucket} from "../../store/bucket";
const useStyles = makeStyles({
list: {
display: "flex",
flexDirection: "row",
height: "100%",
},
content: {
height: "100%",
minWidth: "22rem",
width: "25rem",
display: "flex",
flexDirection: "column",
},
filter: {
height: "4rem",
width: "100%",
display: "flex",
alignItems: "center",
},
filter_input: {
width: "100%",
marginLeft: "0.5rem",
marginRight: "0.5rem",
},
items: {
height: "100%",
width: "100%",
},
items_one: {
marginLeft: "0.5rem",
marginRight: "0.5rem",
"&:hover":{
color: tokens.colorNeutralForeground2BrandPressed,
},
"&.active": {
color: tokens.colorNeutralForeground2BrandPressed,
fontWeight: "bold",
},
"& > span": {
display: "flex",
},
},
items_disconn: {
marginLeft: "auto",
},
slider: {
height: '100%', width: '1px',
// todo: resize
// cursor: 'ew-resize',
'& > div': {
height: '100%', width: '1px',
backgroundColor: 'lightgray',
},
},
})
export function ConnectionList() {
const styles = useStyles()
const {dispatchMessage} = useToast();
const {conn_list, conn_update} = useStoreConnection();
const [conn_filter, set_conn_filter] = useState<string>('');
const {bucket_get} = useStoreBucket()
async function handleSelect(item: Connection) {
}
async function handleConnect(item: Connection) {
let res = await Dial('/api/connection/connect', {id: item.id});
if (res.status !== 200) {
dispatchMessage(res.msg, "error")
return
}
conn_update({...item, active: true})
bucket_get(item, true)
}
async function handleDisconnect(item: Connection) {
let res = await Dial('/api/connection/disconnect', {id: item.id})
if (res.status !== 200) {
dispatchMessage(res.msg, "error")
return
}
conn_update({...item, active: false})
}
return (
<div className={styles.list}>
<div className={styles.content}>
<div className={styles.filter}>
<Input
value={conn_filter}
className={styles.filter_input}
contentAfter={
<Button appearance={'transparent'} onClick={async () => {
set_conn_filter('')
}} size="small" icon={<DismissRegular/>}/>
}
placeholder="搜索连接"
onChange={(e) => set_conn_filter(e.target.value)}
/>
</div>
<div className={styles.items}>
<MenuList>
{conn_list.filter(item => item.name.includes(conn_filter)).map(item => {
return <MenuItem
className={item.active? mergeClasses(styles.items_one, "active"):styles.items_one}
onClick={async () => {
await handleSelect(item)
}}
onDoubleClick={async () => {
await handleConnect(item)
}}
icon={<DatabaseLinkRegular />}
key={item.id}>
{item.name}
<Tooltip
content="断开连接"
relationship="label">
<Button
appearance={'transparent'}
size="small"
icon={<DismissRegular/>}
className={styles.items_disconn}
onClick={async () => {
await handleDisconnect(item)
}}/>
</Tooltip>
</MenuItem>
})}
</MenuList>
</div>
</div>
<div className={styles.slider}>
<div></div>
</div>
</div>
)
}

View File

@ -0,0 +1,40 @@
import {makeStyles, MenuItem, MenuList, tokens} from "@fluentui/react-components";
import {useStoreBucket} from "../../store/bucket";
import {ArchiveRegular} from "@fluentui/react-icons";
const useStyles = makeStyles({
buckets: {
height: "100%",
width: "100%",
},
bucket_items: {
width: "100%",
},
bucket_item: {
width: "100%",
"&:first-child": {
marginTop: "0.5rem",
},
"&:hover": {
color: tokens.colorNeutralForeground2BrandPressed,
},
"& > span:nth-child(2)": {
maxWidth: '100% !important',
},
},
})
export function Bucket() {
const styles = useStyles();
const {bucket_list} = useStoreBucket()
return <div className={styles.buckets}>
<MenuList className={styles.bucket_items}>
{bucket_list.map(((item, idx) => {
return <MenuItem className={styles.bucket_item} icon={<ArchiveRegular/>} style={{width: '100%'}}
key={idx}>{item.name}</MenuItem>
}))}
</MenuList>
</div>
}

View File

@ -0,0 +1,21 @@
import {Path} from "./path";
import {Bucket} from "./bucket";
import {makeStyles} from "@fluentui/react-components";
const useStyles = makeStyles({
content: {
flex: '1',
display: "flex",
flexDirection: 'column',
height: "100%",
width: "100%",
},
})
export function Content() {
const styles = useStyles()
return <div className={styles.content}>
<Path />
<Bucket />
</div>
}

View File

@ -0,0 +1,14 @@
import {makeStyles} from "@fluentui/react-components";
const useStyles = makeStyles({
path: {
height: '4rem',
width: '100%',
borderBottom: '1px solid lightgray',
},
})
export function Path() {
const styles = useStyles()
return <div className={styles.path}></div>
}

View File

@ -0,0 +1,34 @@
import {Button, Input, makeStyles, MenuItem, MenuList, mergeClasses, tokens, Tooltip} from "@fluentui/react-components";
import {DismissRegular} from "@fluentui/react-icons";
import {useEffect, useState} from "react";
import {Connection} from "../../interfaces/connection";
import {useStoreBucket} from "../../store/bucket";
import {useStoreConnection} from "../../store/connection";
import {Dial} from "../../api";
import {useToast} from "../../message";
import {ConnectionList} from "../connection/list";
import {Content} from "../file/content";
const useStyles = makeStyles({
body: {
display: "flex",
flexDirection: 'row',
width: "100%",
flex: '1',
},
})
export function Body() {
const styles = useStyles();
const {conn_get} = useStoreConnection();
useEffect(() => {
conn_get()
}, []);
return <div className={styles.body}>
<ConnectionList/>
<Content />
</div>
}

View File

@ -0,0 +1,3 @@
export function Footer() {
return <div></div>
}

View File

@ -0,0 +1,37 @@
import {Button, Dialog, DialogTrigger, makeStyles} from "@fluentui/react-components";
import {ConnectionCreate} from "../connection/new";
import {CloudAddFilled} from "@fluentui/react-icons";
import {useState} from "react";
const useStyles = makeStyles({
header: {
height: "5rem",
width: "100%",
display: 'flex',
alignItems: "center",
borderBottom: "1px solid lightgray",
},
button_new_connection: {
margin: '0.5rem',
},
})
export function Header() {
const styles = useStyles();
const [openCreate, setOpenCreate] = useState(false);
return <div className={styles.header}>
<div className={styles.button_new_connection}>
<Dialog
open={openCreate}
onOpenChange={(event, data) => setOpenCreate(data.open)}>
<DialogTrigger disableButtonEnhancement>
<Button appearance="primary" icon={<CloudAddFilled/>}>
</Button>
</DialogTrigger>
<ConnectionCreate openFn={setOpenCreate}/>
</Dialog>
</div>
</div>
}

View File

@ -1,60 +0,0 @@
div.container {
height: 100vh;
width: 100vw;
display: flex;
flex-direction: column;
}
div.header {
height: 50px;
border-bottom: 1px solid lightgray;
display: flex;
align-items: center;
padding: 0 8px;
}
div.body {
display: flex;
flex: 1;
width: 100%;
height: 100%;
}
div.body div.body-connections {
width: 200px;
border-right: 1px solid lightgray;
height: 100%;
}
div.body-connections-search {
display: flex;
height: 30px;
}
input.body-connections-search-input {
border: none;
border-bottom: 1px solid lightgray;
width: calc(100% - 4px);
height: 30px;
outline: none;
text-indent: 5px;
}
div.body-connections-search-dismiss {
border: none;
background: none;
outline: none;
border-bottom: 1px solid lightgray;
height: 32px;
width: 32px;
display: flex;
justify-content: center;
align-items: center;
}
div.body-connections-list {
height: 100%;
}
div.body-connections-list-item.active {
color: var(--colorNeutralForeground2BrandSelected);
}

View File

@ -1,148 +1,26 @@
import {useEffect, useState} from 'react'; import {Header} from "./header";
import './home.css'; import {Body} from "./body";
import { import {makeStyles} from "@fluentui/react-components";
Button, Dialog, DialogTrigger, makeStyles, mergeClasses, MenuItem, MenuList, tokens, Tooltip, import {Footer} from "./footer";
} from "@fluentui/react-components";
import {
CloudAddFilled, DismissRegular
} from "@fluentui/react-icons";
import {Dial} from "../../api";
import {useToast} from "../../message";
import {Connection} from "../../interfaces/connection";
import {ConnectionCreate} from "../connection/new";
import {useStoreConnection} from "../../store/connection";
import {useStoreBucket} from "../../store/bucket";
const useMenuListContainerStyles = makeStyles({ const useStyles = makeStyles({
container: { container: {
backgroundColor: tokens.colorNeutralBackground1,
flex: 1,
width: "100%",
paddingTop: "4px",
paddingBottom: "4px",
},
item: {
display: 'flex',
height: '100%',
alignItems: 'center',
flexDirection: 'row',
fontSize: '15px',
'& span': {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
},
'&.active': {
color: tokens.colorNeutralForeground2BrandHover,
}
},
item_icon: {
height: '100%', height: '100%',
width: '100%',
display: 'flex', display: 'flex',
justifyContent: 'center', flexDirection: 'column',
alignItems: 'center', },
marginLeft: 'auto', })
border: 'none',
background: 'transparent',
"&:hover": {
color: tokens.colorNeutralForeground2BrandHover,
}
}
});
export function Home() { export function Home() {
const styles = useMenuListContainerStyles();
const {dispatchMessage} = useToast();
const [openCreate, setOpenCreate] = useState(false);
const [conn_filter, set_conn_filter] = useState<string>('');
const {conn_list, conn_get, conn_update} = useStoreConnection();
const {bucket_list, bucket_get} = useStoreBucket()
useEffect(() => { const styles = useStyles()
conn_get()
}, []);
async function handleConnect(item: Connection) {
let res = await Dial('/api/connection/connect', {id: item.id});
if (res.status !== 200) {
dispatchMessage(res.msg, "error")
return
}
conn_update({...item, active: true})
bucket_get(item, true)
}
async function handleDisconnect(item: Connection) {
let res = await Dial('/api/connection/disconnect', {id: item.id})
if (res.status !== 200) {
dispatchMessage(res.msg, "error")
return
}
conn_update({...item, active: false})
}
return ( return (
<div className="container"> <div className={styles.container}>
<div className="header"> <Header />
<Dialog <Body />
open={openCreate} <Footer/>
onOpenChange={(event, data) => setOpenCreate(data.open)}>
<DialogTrigger disableButtonEnhancement>
<Button appearance="primary" icon={<CloudAddFilled/>}>
</Button>
</DialogTrigger>
<ConnectionCreate openFn={setOpenCreate}/>
</Dialog>
</div>
<div className="body">
<div className="body-connections">
<div className="body-connections-search">
<input className="body-connections-search-input" type={"text"} placeholder="搜索连接"
value={conn_filter}
onChange={(e) => set_conn_filter(e.target.value)}/>
<div className="body-connections-search-dismiss" onClick={() => {
set_conn_filter('')
}}>
<DismissRegular/>
</div>
</div>
<div className="body-connections-list">
<div className={styles.container}>
<MenuList>
{conn_list.filter(item => item.name.includes(conn_filter)).map(item => {
return <MenuItem
onDoubleClick={async () => {
await handleConnect(item)
}}
className={item.active ? mergeClasses(styles.item, 'active') : styles.item}
key={item.id}>
{item.name}
<Tooltip content="断开连接" relationship="label">
<Button onClick={async () => {
await handleDisconnect(item)
}} size="small" className={styles.item_icon} icon={<DismissRegular/>}/>
</Tooltip>
</MenuItem>
})}
</MenuList>
</div>
</div>
</div>
<div className="body-content" style={{width:'100%'}}>
<div></div>
<div style={{width:'100%'}}>
<MenuList style={{width:'100%'}}>
{bucket_list.map(((item, idx) => {
return <MenuItem style={{width:'100%'}} key={idx}>{item.name}</MenuItem>
}))}
</MenuList>
</div>
</div>
</div>
<div className="footer"></div>
</div> </div>
) )
} }

View File

@ -15,7 +15,7 @@ const router = createBrowserRouter([
]) ])
root.render( root.render(
<FluentProvider theme={webLightTheme}> <FluentProvider theme={webLightTheme} style={{height: '100%'}}>
<ToastProvider> <ToastProvider>
<RouterProvider router={router}/> <RouterProvider router={router}/>
</ToastProvider> </ToastProvider>

View File

@ -1,3 +1,6 @@
:root {
font-size: 10px;
}
body { body {
margin: 0; margin: 0;
color: white; color: white;