refactory: 结构和css in js
This commit is contained in:
parent
9079a82434
commit
efe7800b59
@ -6,7 +6,7 @@
|
||||
<title>nf-disk</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<div id="root" style="height: 100%"></div>
|
||||
<script src="./src/main.tsx" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -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;
|
||||
}
|
150
frontend/src/component/connection/list.tsx
Normal file
150
frontend/src/component/connection/list.tsx
Normal 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>
|
||||
)
|
||||
}
|
40
frontend/src/component/file/bucket.tsx
Normal file
40
frontend/src/component/file/bucket.tsx
Normal 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>
|
||||
}
|
21
frontend/src/component/file/content.tsx
Normal file
21
frontend/src/component/file/content.tsx
Normal 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>
|
||||
}
|
14
frontend/src/component/file/path.tsx
Normal file
14
frontend/src/component/file/path.tsx
Normal 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>
|
||||
}
|
34
frontend/src/component/home/body.tsx
Normal file
34
frontend/src/component/home/body.tsx
Normal 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>
|
||||
}
|
3
frontend/src/component/home/footer.tsx
Normal file
3
frontend/src/component/home/footer.tsx
Normal file
@ -0,0 +1,3 @@
|
||||
export function Footer() {
|
||||
return <div></div>
|
||||
}
|
37
frontend/src/component/home/header.tsx
Normal file
37
frontend/src/component/home/header.tsx
Normal 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>
|
||||
}
|
@ -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);
|
||||
}
|
@ -1,148 +1,26 @@
|
||||
import {useEffect, useState} from 'react';
|
||||
import './home.css';
|
||||
import {
|
||||
Button, Dialog, DialogTrigger, makeStyles, mergeClasses, MenuItem, MenuList, tokens, Tooltip,
|
||||
} 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";
|
||||
import {Header} from "./header";
|
||||
import {Body} from "./body";
|
||||
import {makeStyles} from "@fluentui/react-components";
|
||||
import {Footer} from "./footer";
|
||||
|
||||
const useMenuListContainerStyles = makeStyles({
|
||||
const useStyles = makeStyles({
|
||||
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%',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginLeft: 'auto',
|
||||
border: 'none',
|
||||
background: 'transparent',
|
||||
"&:hover": {
|
||||
color: tokens.colorNeutralForeground2BrandHover,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
flexDirection: 'column',
|
||||
},
|
||||
})
|
||||
|
||||
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(() => {
|
||||
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})
|
||||
}
|
||||
const styles = useStyles()
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<div className="header">
|
||||
<Dialog
|
||||
open={openCreate}
|
||||
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 className={styles.container}>
|
||||
<Header />
|
||||
<Body />
|
||||
<Footer/>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -15,7 +15,7 @@ const router = createBrowserRouter([
|
||||
])
|
||||
|
||||
root.render(
|
||||
<FluentProvider theme={webLightTheme}>
|
||||
<FluentProvider theme={webLightTheme} style={{height: '100%'}}>
|
||||
<ToastProvider>
|
||||
<RouterProvider router={router}/>
|
||||
</ToastProvider>
|
||||
|
@ -1,3 +1,6 @@
|
||||
:root {
|
||||
font-size: 10px;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
color: white;
|
||||
|
Loading…
x
Reference in New Issue
Block a user