Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
PaddlePaddle
VisualDL
提交
6da5a64d
V
VisualDL
项目概览
PaddlePaddle
/
VisualDL
大约 1 年 前同步成功
通知
88
Star
4655
Fork
642
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
10
列表
看板
标记
里程碑
合并请求
2
Wiki
5
Wiki
分析
仓库
DevOps
项目成员
Pages
V
VisualDL
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
10
Issue
10
列表
看板
标记
里程碑
合并请求
2
合并请求
2
Pages
分析
分析
仓库分析
DevOps
Wiki
5
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
未验证
提交
6da5a64d
编写于
4月 09, 2021
作者:
P
Peter Pan
提交者:
GitHub
4月 09, 2021
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
User interaction improvement (#949)
* feat: show more menu in navbar * feat: able to drag and change run sidebar width
上级
22b159a1
变更
9
隐藏空白更改
内联
并排
Showing
9 changed file
with
406 addition
and
78 deletion
+406
-78
frontend/packages/core/public/locales/en/common.json
frontend/packages/core/public/locales/en/common.json
+3
-0
frontend/packages/core/public/locales/zh/common.json
frontend/packages/core/public/locales/zh/common.json
+3
-0
frontend/packages/core/src/components/Aside.tsx
frontend/packages/core/src/components/Aside.tsx
+102
-6
frontend/packages/core/src/components/Navbar.tsx
frontend/packages/core/src/components/Navbar.tsx
+149
-58
frontend/packages/core/src/components/RunAside.tsx
frontend/packages/core/src/components/RunAside.tsx
+13
-2
frontend/packages/core/src/hooks/useAvailableComponents.ts
frontend/packages/core/src/hooks/useAvailableComponents.ts
+118
-0
frontend/packages/core/src/pages/index.tsx
frontend/packages/core/src/pages/index.tsx
+11
-9
frontend/packages/core/src/routes/index.ts
frontend/packages/core/src/routes/index.ts
+5
-2
frontend/packages/mock/data/components.ts
frontend/packages/mock/data/components.ts
+2
-1
未找到文件。
frontend/packages/core/public/locales/en/common.json
浏览文件 @
6da5a64d
...
...
@@ -9,8 +9,11 @@
"graph"
:
"Graphs"
,
"high-dimensional"
:
"High Dimensional"
,
"histogram"
:
"Histogram"
,
"hyper-parameter"
:
"Hyper Parameters"
,
"image"
:
"Image"
,
"inactive"
:
"Inactive"
,
"loading"
:
"Please wait while loading data"
,
"more"
:
"More"
,
"next-page"
:
"Next Page"
,
"pr-curve"
:
"PR Curve"
,
"previous-page"
:
"Prev Page"
,
...
...
frontend/packages/core/public/locales/zh/common.json
浏览文件 @
6da5a64d
...
...
@@ -9,8 +9,11 @@
"graph"
:
"网络结构"
,
"high-dimensional"
:
"高维数据映射"
,
"histogram"
:
"直方图"
,
"hyper-parameter"
:
"超参可视化"
,
"image"
:
"图像"
,
"inactive"
:
"待使用"
,
"loading"
:
"数据载入中,请稍等"
,
"more"
:
"更多"
,
"next-page"
:
"下一页"
,
"pr-curve"
:
"PR曲线"
,
"previous-page"
:
"上一页"
,
...
...
frontend/packages/core/src/components/Aside.tsx
浏览文件 @
6da5a64d
...
...
@@ -14,8 +14,8 @@
* limitations under the License.
*/
import
React
,
{
FunctionComponent
}
from
'
react
'
;
import
{
WithStyled
,
asideWidth
,
rem
,
size
,
transitionProps
}
from
'
~/utils/style
'
;
import
React
,
{
FunctionComponent
,
useCallback
,
useLayoutEffect
,
useRef
,
useState
}
from
'
react
'
;
import
{
WithStyled
,
asideWidth
,
rem
,
transitionProps
}
from
'
~/utils/style
'
;
import
styled
from
'
styled-components
'
;
...
...
@@ -30,11 +30,16 @@ export const AsideSection = styled.section`
}
`
;
const
Wrapper
=
styled
.
div
<
{
width
?:
string
|
number
}
>
`
${
props
=>
size
(
'
100%
'
,
props
.
width
==
null
?
asideWidth
:
props
.
width
)}
const
Wrapper
=
styled
.
div
.
attrs
<
{
width
:
string
|
number
}
>
(({
width
})
=>
({
style
:
{
width
:
'
number
'
===
typeof
width
?
`
${
width
}
px`
:
width
}
}))
<
{
width
:
string
|
number
}
>
`
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
position: relative;
> .aside-top {
flex: auto;
...
...
@@ -55,16 +60,107 @@ const Wrapper = styled.div<{width?: string | number}>`
box-shadow: 0 -
${
rem
(
5
)}
${
rem
(
16
)}
0 rgba(0, 0, 0, 0.03);
padding:
${
rem
(
20
)}
;
}
> .aside-resize-bar-left,
> .aside-resize-bar-right {
position: absolute;
width:
${
rem
(
8
)}
;
height: 100%;
top: 0;
cursor: col-resize;
user-select: none;
&.aside-resize-bar-left {
left: 0;
}
&.aside-resize-bar-right {
right: 0;
}
}
`
;
type
AsideProps
=
{
width
?:
string
|
number
;
bottom
?:
React
.
ReactNode
;
resizable
?:
'
left
'
|
'
right
'
;
minWidth
?:
number
;
maxWidth
?:
number
;
onResized
?:
(
width
:
number
)
=>
unknown
;
};
const
Aside
:
FunctionComponent
<
AsideProps
&
WithStyled
>
=
({
width
,
bottom
,
className
,
children
})
=>
{
const
Aside
:
FunctionComponent
<
AsideProps
&
WithStyled
>
=
({
width
,
bottom
,
resizable
,
minWidth
,
maxWidth
,
onResized
,
className
,
children
})
=>
{
const
[
sideWidth
,
setSideWidth
]
=
useState
<
NonNullable
<
typeof
width
>>
(
width
??
asideWidth
);
const
ref
=
useRef
<
HTMLDivElement
>
(
null
);
const
resizing
=
useRef
<
boolean
>
(
false
);
const
range
=
useRef
({
min
:
minWidth
??
null
,
max
:
maxWidth
??
null
});
useLayoutEffect
(()
=>
{
range
.
current
.
min
=
minWidth
??
null
;
},
[
minWidth
]);
useLayoutEffect
(()
=>
{
range
.
current
.
max
=
maxWidth
??
null
;
},
[
maxWidth
]);
useLayoutEffect
(()
=>
{
if
(
range
.
current
.
min
==
null
&&
ref
.
current
)
{
const
{
width
}
=
ref
.
current
.
getBoundingClientRect
();
range
.
current
.
min
=
width
;
}
},
[]);
const
mousedown
=
useCallback
(()
=>
{
resizing
.
current
=
true
;
},
[]);
const
mousemove
=
useCallback
(
(
event
:
MouseEvent
)
=>
{
if
(
ref
.
current
&&
resizing
.
current
)
{
const
clientX
=
event
.
clientX
;
const
{
left
,
right
}
=
ref
.
current
.
getBoundingClientRect
();
let
w
=
0
;
if
(
resizable
===
'
left
'
)
{
w
=
Math
.
max
(
range
.
current
.
min
??
0
,
right
-
clientX
);
}
else
if
(
resizable
===
'
right
'
)
{
w
=
Math
.
max
(
range
.
current
.
min
??
0
,
clientX
-
left
);
}
w
=
Math
.
min
(
range
.
current
.
max
??
document
.
body
.
clientWidth
/
2
,
w
);
setSideWidth
(
w
);
}
},
[
resizable
]
);
const
mouseup
=
useCallback
(()
=>
{
resizing
.
current
=
false
;
if
(
ref
.
current
)
{
onResized
?.(
ref
.
current
.
getBoundingClientRect
().
width
);
}
},
[
onResized
]);
useLayoutEffect
(()
=>
{
document
.
addEventListener
(
'
mousemove
'
,
mousemove
);
return
()
=>
document
.
removeEventListener
(
'
mousemove
'
,
mousemove
);
},
[
mousemove
]);
useLayoutEffect
(()
=>
{
document
.
addEventListener
(
'
mouseup
'
,
mouseup
);
return
()
=>
document
.
removeEventListener
(
'
mouseup
'
,
mouseup
);
},
[
mouseup
]);
return
(
<
Wrapper
width
=
{
width
}
className
=
{
className
}
>
<
Wrapper
width
=
{
sideWidth
}
className
=
{
className
}
ref
=
{
ref
}
>
{
resizable
?
<
div
className
=
{
`aside-resize-bar-
${
resizable
}
`
}
onMouseDown
=
{
mousedown
}
></
div
>
:
null
}
<
div
className
=
"aside-top"
>
{
children
}
</
div
>
{
bottom
&&
<
div
className
=
"aside-bottom"
>
{
bottom
}
</
div
>
}
</
Wrapper
>
...
...
frontend/packages/core/src/components/Navbar.tsx
浏览文件 @
6da5a64d
...
...
@@ -14,9 +14,11 @@
* limitations under the License.
*/
// cspell:words cimode
import
{
Link
,
LinkProps
,
useLocation
}
from
'
react-router-dom
'
;
import
React
,
{
FunctionComponent
,
useCallback
,
useEffect
,
useMemo
,
useState
}
from
'
react
'
;
import
{
border
,
borderRadius
,
rem
,
size
,
transitionProps
}
from
'
~/utils/style
'
;
import
{
border
,
borderRadius
,
rem
,
size
,
transitionProps
,
triangle
}
from
'
~/utils/style
'
;
import
Icon
from
'
~/components/Icon
'
;
import
Language
from
'
~/components/Language
'
;
...
...
@@ -28,17 +30,40 @@ import {getApiToken} from '~/utils/fetch';
import
logo
from
'
~/assets/images/logo.svg
'
;
import
queryString
from
'
query-string
'
;
import
styled
from
'
styled-components
'
;
import
use
NavItems
from
'
~/hooks/useNavItem
s
'
;
import
use
AvailableComponents
from
'
~/hooks/useAvailableComponent
s
'
;
import
{
useTranslation
}
from
'
react-i18next
'
;
const
BASE_URI
:
string
=
import
.
meta
.
env
.
SNOWPACK_PUBLIC_BASE_URI
;
const
PUBLIC_PATH
:
string
=
import
.
meta
.
env
.
SNOWPACK_PUBLIC_PATH
;
const
API_TOKEN_KEY
:
string
=
import
.
meta
.
env
.
SNOWPACK_PUBLIC_API_TOKEN_KEY
;
interface
NavbarItemProps
extends
Route
{
const
MAX_ITEM_COUNT_IN_NAVBAR
=
5
;
const
flatten
=
<
T
extends
{
children
?:
T
[]
}
>
(routes: T[]) =>
{
const
result
:
Omit
<
T
,
'
children
'
>
[]
=
[];
routes
.
forEach
(
route
=>
{
if
(
route
.
children
)
{
result
.
push
(...
flatten
(
route
.
children
));
}
else
{
result
.
push
(
route
);
}
});
return
result
;
}
;
interface NavbarItemProps
{
active
:
boolean
;
path
?:
Route
[
'
path
'
];
showDropdownIcon
?:
boolean
;
}
interface NavbarItemType
{
id
:
string
;
cid
?:
string
;
name
:
string
;
active
:
boolean
;
children
?:
({
active
:
boolean
}
&
NonNullable
<
Route
[
'
children
'
]
>
[
number
])[];
path
?:
Route
[
'
path
'
];
children
?:
NavbarItemType
[];
}
function appendApiToken(url: string)
{
...
...
@@ -129,10 +154,27 @@ const NavItem = styled.div<{active?: boolean}>`
$
{
props
=>
border
(
'
bottom
'
,
rem
(
3
),
'
solid
'
,
props
.
active
?
'
var(--navbar-highlight-color)
'
:
'
transparent
'
)}
$
{
transitionProps
(
'
border-bottom
'
)}
text
-
transform
:
uppercase
;
&
.
dropdown
-
icon
{
&
::
after
{
content
:
''
;
display
:
inline
-
block
;
width
:
0
;
height
:
0
;
margin
-
left
:
0.5
rem
;
vertical
-
align
:
middle
;
$
{
triangle
({
pointingDirection
:
'
bottom
'
,
width
:
rem
(
8
),
height
:
rem
(
5
),
foregroundColor
:
'
currentColor
'
})}
}
}
}
`;
const
SubNav
=
styled
.
div
`
const SubNav
Wrapper
= styled.div`
overflow: hidden;
border-radius: $
{
borderRadius
}
;
`;
...
...
@@ -156,38 +198,65 @@ const NavItemChild = styled.div<{active?: boolean}>`
}
`;
const
NavbarLink
:
FunctionComponent
<
{
to
?:
string
}
&
Omit
<
LinkProps
,
'
to
'
>>
=
({
to
,
children
,
...
props
})
=>
{
return
(
<
Link
to
=
{
to
?
appendApiToken
(
to
)
:
''
}
{
...
props
}
>
{
children
}
</
Link
>
);
};
const
NavbarItem
=
React
.
forwardRef
<
HTMLDivElement
,
NavbarItemProps
>
(({
id
,
cid
,
path
,
active
},
ref
)
=>
{
const
{
t
}
=
useTranslation
(
'
common
'
);
const
name
=
useMemo
(()
=>
(
cid
?
`
${
t
(
id
)}
-
${
t
(
cid
)}
`
:
t
(
id
)),
[
t
,
id
,
cid
]);
const NavbarLink: FunctionComponent
<
{
to
?:
string
}
&
Omit
<
LinkProps
,
'to'
>
> = (
{
to
,
children
,
...
props
}
) => (
<
Link
to
=
{
to
?
appendApiToken
(
to
)
:
''
}
{
...
props
}
>
{
children
}
</
Link
>
);
// FIXME: why we need to add children type here... that's weird...
const NavbarItem = React.forwardRef
<
HTMLDivElement
,
NavbarItemProps
&
{
children
?:
React
.
ReactNode
}
>(
(
{
path
,
active
,
showDropdownIcon
,
children
}
, ref) =>
{
if
(
path
)
{
return
(
<
NavItem
active
=
{
active
}
ref
=
{
ref
}
>
<
NavbarLink
to
=
{
path
}
className
=
"nav-link"
>
<
span
className
=
{
`nav-text
${
showDropdownIcon
?
'
dropdown-icon
'
:
''
}
`
}
>
{
children
}
</
span
>
</
NavbarLink
>
</
NavItem
>
);
}
if
(
path
)
{
return
(
<
NavItem
active
=
{
active
}
ref
=
{
ref
}
>
<
NavbarLink
to
=
{
path
}
className
=
"nav-link"
>
<
span
className
=
"nav-text"
>
{
name
}
</
span
>
</
NavbarLink
>
<
span
className
=
{
`nav-text
${
showDropdownIcon
?
'
dropdown-icon
'
:
''
}
`
}
>
{
children
}
</
span
>
</
NavItem
>
);
}
return
(
<
NavItem
active
=
{
active
}
ref
=
{
ref
}
>
<
span
className
=
"nav-text"
>
{
name
}
</
span
>
</
NavItem
>
);
});
);
NavbarItem.displayName = 'NavbarItem';
const SubNav: FunctionComponent
<
{
menu
:
Omit
<
NavbarItemType
,
'
children
'
|
'
cid
'
>
[];
active
?:
boolean
;
path
?:
string
;
showDropdownIcon
?:
boolean
;
}
>
= (
{
menu
,
active
,
path
,
showDropdownIcon
,
children
}
) => (
<
Tippy
placement
=
"bottom-start"
animation
=
"shift-away-subtle"
interactive
arrow
=
{
false
}
offset
=
{
[
0
,
0
]
}
hideOnClick
=
{
false
}
role
=
"menu"
content
=
{
<
SubNavWrapper
>
{
menu
.
map
(
item
=>
(
<
NavItemChild
active
=
{
item
.
active
}
key
=
{
item
.
id
}
>
<
NavbarLink
to
=
{
item
.
path
}
>
{
item
.
name
}
</
NavbarLink
>
</
NavItemChild
>
))
}
</
SubNavWrapper
>
}
>
<
NavbarItem
active
=
{
active
||
false
}
path
=
{
path
}
showDropdownIcon
=
{
showDropdownIcon
}
>
{
children
}
</
NavbarItem
>
</
Tippy
>
);
const Navbar: FunctionComponent = () =>
{
const
{
t
,
i18n
}
=
useTranslation
(
'
common
'
);
const
{
pathname
}
=
useLocation
();
...
...
@@ -202,11 +271,32 @@ const Navbar: FunctionComponent = () => {
const
currentPath
=
useMemo
(()
=>
pathname
.
replace
(
BASE_URI
,
''
),
[
pathname
]);
const
[
navItems
]
=
useNavItems
();
const
[
items
,
setItems
]
=
useState
<
NavbarItemProps
[]
>
([]);
const
[
components
,
inactiveComponents
]
=
useAvailableComponents
();
const
componentsInNavbar
=
useMemo
(()
=>
components
.
slice
(
0
,
MAX_ITEM_COUNT_IN_NAVBAR
),
[
components
]);
const
flattenMoreComponents
=
useMemo
(()
=>
flatten
(
components
.
slice
(
MAX_ITEM_COUNT_IN_NAVBAR
)),
[
components
]);
const
flattenInactiveComponents
=
useMemo
(()
=>
flatten
(
inactiveComponents
),
[
inactiveComponents
]);
const
componentsInMoreMenu
=
useMemo
(
()
=>
flattenMoreComponents
.
map
(
item
=>
({
...
item
,
active
:
currentPath
===
item
.
path
})),
[
currentPath
,
flattenMoreComponents
]
);
const
componentsInInactiveMenu
=
useMemo
(
()
=>
flattenInactiveComponents
.
map
(
item
=>
({
...
item
,
active
:
currentPath
===
item
.
path
})),
[
currentPath
,
flattenInactiveComponents
]
);
const
[
navItemsInNavbar
,
setNavItemsInNavbar
]
=
useState
<
NavbarItemType
[]
>
([]);
useEffect
(()
=>
{
set
Items
(
oldItems
=>
navItems
.
map
(
item
=>
{
set
NavItemsInNavbar
(
oldItems
=>
componentsInNavbar
.
map
(
item
=>
{
const
children
=
item
.
children
?.
map
(
child
=>
({
...
child
,
active
:
child
.
path
===
currentPath
...
...
@@ -217,6 +307,7 @@ const Navbar: FunctionComponent = () => {
return
{
...
item
,
cid
:
child
.
id
,
name
:
child
.
name
,
path
:
currentPath
,
active
:
true
,
children
...
...
@@ -227,6 +318,7 @@ const Navbar: FunctionComponent = () => {
return
{
...
item
,
...
oldItem
,
name
:
item
.
children
?.
find
(
c
=>
c
.
id
===
oldItem
.
cid
)?.
name
??
item
.
name
,
active
:
false
,
children
};
...
...
@@ -240,7 +332,7 @@ const Navbar: FunctionComponent = () => {
};
})
);
},
[
navItems
,
currentPath
]);
},
[
componentsInNavbar
,
currentPath
]);
return
(
<
Nav
>
...
...
@@ -249,36 +341,35 @@ const Navbar: FunctionComponent = () => {
<
img
alt
=
"PaddlePaddle"
src
=
{
PUBLIC_PATH
+
logo
}
/>
<
span
>
VisualDL
</
span
>
</
Logo
>
{
items
.
map
(
item
=>
{
{
navItemsInNavbar
.
map
(
item
=>
{
if
(
item
.
children
)
{
return
(
<
Tippy
placement
=
"bottom-start"
animation
=
"shift-away-subtle"
interactive
arrow
=
{
false
}
offset
=
{
[
0
,
0
]
}
hideOnClick
=
{
false
}
role
=
"menu"
content
=
{
<
SubNav
>
{
item
.
children
.
map
(
child
=>
(
<
NavItemChild
active
=
{
child
.
active
}
key
=
{
child
.
id
}
>
<
NavbarLink
to
=
{
child
.
path
}
>
{
t
(
item
.
id
)
}
-
{
t
(
child
.
id
)
}
</
NavbarLink
>
</
NavItemChild
>
))
}
</
SubNav
>
}
<
SubNav
menu
=
{
item
.
children
}
active
=
{
item
.
active
}
path
=
{
item
.
path
}
key
=
{
item
.
active
?
`
${
item
.
id
}
-activated`
:
item
.
id
}
>
<
NavbarItem
{
...
item
}
/>
</
Tippy
>
{
item
.
name
}
</
SubNav
>
);
}
return
<
NavbarItem
{
...
item
}
key
=
{
item
.
id
}
/>;
return
(
<
NavbarItem
active
=
{
item
.
active
}
path
=
{
item
.
path
}
key
=
{
item
.
id
}
>
{
item
.
name
}
</
NavbarItem
>
);
})
}
{
componentsInMoreMenu
.
length
?
(
<
SubNav
menu
=
{
componentsInMoreMenu
}
showDropdownIcon
>
{
t
(
'
common:more
'
)
}
</
SubNav
>
)
:
null
}
{
componentsInInactiveMenu
.
length
?
(
<
SubNav
menu
=
{
componentsInInactiveMenu
}
showDropdownIcon
>
{
t
(
'
common:inactive
'
)
}
</
SubNav
>
)
:
null
}
</
div
>
<
div
className
=
"right"
>
<
Tippy
...
...
@@ -290,9 +381,9 @@ const Navbar: FunctionComponent = () => {
hideOnClick
=
{
false
}
role
=
"menu"
content
=
{
<
SubNav
>
<
SubNav
Wrapper
>
<
ThemeToggle
/>
</
SubNav
>
</
SubNav
Wrapper
>
}
>
<
NavItem
className
=
"nav-item"
>
...
...
frontend/packages/core/src/components/RunAside.tsx
浏览文件 @
6da5a64d
...
...
@@ -16,7 +16,7 @@
import
Aside
,
{
AsideSection
}
from
'
~/components/Aside
'
;
import
React
,
{
FunctionComponent
,
useCallback
,
useMemo
,
useState
}
from
'
react
'
;
import
{
ellipsis
,
em
,
rem
,
size
}
from
'
~/utils/style
'
;
import
{
asideWidth
,
ellipsis
,
em
,
rem
,
size
}
from
'
~/utils/style
'
;
import
Checkbox
from
'
~/components/Checkbox
'
;
import
Field
from
'
~/components/Field
'
;
...
...
@@ -26,8 +26,11 @@ import RunningToggle from '~/components/RunningToggle';
import
SearchInput
from
'
~/components/SearchInput
'
;
import
styled
from
'
styled-components
'
;
import
uniqBy
from
'
lodash/uniqBy
'
;
import
useLocalStorage
from
'
~/hooks/useLocalStorage
'
;
import
{
useTranslation
}
from
'
react-i18next
'
;
const
SIDE_WIDTH_STORAGE_KEY
=
'
run_aside_width
'
;
const
StyledAside
=
styled
(
Aside
)
`
${
AsideSection
}
.run-section {
flex: auto;
...
...
@@ -137,8 +140,16 @@ const RunAside: FunctionComponent<RunAsideProps> = ({
[
running
,
onToggleRunning
]
);
const
[
width
,
setWidth
]
=
useLocalStorage
(
SIDE_WIDTH_STORAGE_KEY
);
return
(
<
StyledAside
bottom
=
{
bottom
}
>
<
StyledAside
bottom
=
{
bottom
}
resizable
=
"left"
width
=
{
width
==
null
?
asideWidth
:
+
width
}
minWidth
=
{
260
}
onResized
=
{
width
=>
setWidth
(
width
+
''
)
}
>
{
children
}
<
AsideSection
className
=
"run-section"
>
<
Field
className
=
"run-select"
label
=
{
t
(
'
common:select-runs
'
)
}
>
...
...
frontend/packages/core/src/hooks/use
NavItem
s.ts
→
frontend/packages/core/src/hooks/use
AvailableComponent
s.ts
浏览文件 @
6da5a64d
...
...
@@ -14,11 +14,18 @@
* limitations under the License.
*/
import
routes
,
{
Pages
,
Route
}
from
'
~/routes
'
;
import
{
useCallback
,
useEffect
,
useState
}
from
'
react
'
;
import
routes
,
{
Pages
}
from
'
~/routes
'
;
import
{
useCallback
,
useEffect
,
use
Memo
,
use
State
}
from
'
react
'
;
import
type
{
Route
}
from
'
~/routes
'
;
import
ee
from
'
~/utils/event
'
;
import
useRequest
from
'
~/hooks/useRequest
'
;
import
{
useTranslation
}
from
'
react-i18next
'
;
interface
RouteWithName
extends
Route
{
name
:
string
;
children
?:
Pick
<
RouteWithName
,
'
id
'
|
'
path
'
|
'
component
'
|
'
name
'
>
[];
}
export
const
navMap
=
{
scalar
:
Pages
.
Scalar
,
...
...
@@ -29,11 +36,15 @@ export const navMap = {
graph
:
Pages
.
Graph
,
embeddings
:
Pages
.
HighDimensional
,
pr_curve
:
Pages
.
PRCurve
,
roc_curve
:
Pages
.
ROCCurve
roc_curve
:
Pages
.
ROCCurve
,
hyper_parameters
:
Pages
.
HyperParameter
}
as
const
;
const
useNavItems
=
()
=>
{
const
[
components
,
setComponents
]
=
useState
<
Route
[]
>
([]);
const
useAvailableComponents
=
()
=>
{
const
{
t
}
=
useTranslation
(
'
common
'
);
const
[
components
,
setComponents
]
=
useState
<
RouteWithName
[]
>
([]);
const
[
inactiveComponents
,
setInactiveComponents
]
=
useState
<
RouteWithName
[]
>
([]);
const
{
data
,
loading
,
error
,
mutate
}
=
useRequest
<
(
keyof
typeof
navMap
)[]
>
(
'
/components
'
,
{
refreshInterval
:
components
.
length
?
61
*
1000
:
15
*
1000
,
...
...
@@ -53,32 +64,55 @@ const useNavItems = () => {
};
},
[
mutate
]);
const
filterPages
=
useCallback
(
(
pages
:
Route
[])
=>
{
const
items
:
string
[]
=
data
?.
map
(
item
=>
navMap
[
item
])
??
[];
return
pages
.
reduce
<
Route
[]
>
((
m
,
page
)
=>
{
if
(
page
.
children
)
{
const
children
=
filterPages
(
page
.
children
);
if
(
children
.
length
)
{
const
filterRoutes
=
useCallback
(
(
pages
:
Route
[],
filter
:
(
page
:
Route
)
=>
boolean
)
=>
{
const
iterator
=
(
pages
:
Route
[],
parent
?:
Route
)
=>
{
const
parentName
=
parent
?
t
(
parent
.
id
)
+
'
-
'
:
''
;
return
pages
.
reduce
<
RouteWithName
[]
>
((
m
,
page
)
=>
{
const
name
=
parentName
+
t
(
page
.
id
);
if
(
page
.
children
)
{
const
children
=
iterator
(
page
.
children
,
page
);
if
(
children
.
length
)
{
m
.
push
({
...
page
,
name
,
children
:
children
as
RouteWithName
[
'
children
'
]
});
}
}
else
if
(
page
.
visible
!==
false
&&
filter
(
page
))
{
m
.
push
({
...
page
,
children
:
children
as
Route
[
'
children
'
]
name
,
children
:
undefined
});
}
}
else
if
(
page
.
visible
!==
false
&&
items
.
includes
(
page
.
id
))
{
m
.
push
(
page
);
}
return
m
;
},
[]);
return
m
;
},
[]);
};
return
iterator
(
pages
);
},
[
data
]
[
t
]
);
const
legalAvailableComponentIdArray
:
string
[]
=
useMemo
(()
=>
data
?.
map
(
item
=>
navMap
[
item
])
??
[],
[
data
]);
const
findAvailableComponents
=
useCallback
(
(
pages
:
Route
[])
=>
filterRoutes
(
pages
,
page
=>
legalAvailableComponentIdArray
.
includes
(
page
.
id
)),
[
filterRoutes
,
legalAvailableComponentIdArray
]
);
const
findInactiveComponents
=
useCallback
(
(
pages
:
Route
[])
=>
filterRoutes
(
pages
,
page
=>
!
legalAvailableComponentIdArray
.
includes
(
page
.
id
)),
[
filterRoutes
,
legalAvailableComponentIdArray
]
);
useEffect
(()
=>
{
setComponents
(
findAvailableComponents
(
routes
));
},
[
findAvailableComponents
]);
useEffect
(()
=>
{
set
Components
(
filterPage
s
(
routes
));
},
[
fi
lterPage
s
]);
set
InactiveComponents
(
findInactiveComponent
s
(
routes
));
},
[
fi
ndInactiveComponent
s
]);
return
[
components
,
loading
,
error
]
as
const
;
return
[
components
,
inactiveComponents
,
loading
,
error
]
as
const
;
};
export
default
use
NavItem
s
;
export
default
use
AvailableComponent
s
;
frontend/packages/core/src/pages/index.tsx
浏览文件 @
6da5a64d
...
...
@@ -21,7 +21,7 @@ import {useHistory, useLocation} from 'react-router-dom';
import
Error
from
'
~/components/Error
'
;
import
HashLoader
from
'
react-spinners/HashLoader
'
;
import
styled
from
'
styled-components
'
;
import
use
NavItems
from
'
~/hooks/useNavItem
s
'
;
import
use
AvailableComponents
from
'
~/hooks/useAvailableComponent
s
'
;
import
{
useTranslation
}
from
'
react-i18next
'
;
const
CenterWrapper
=
styled
.
div
`
...
...
@@ -40,7 +40,7 @@ const Loading = styled.div`
`
;
const
IndexPage
:
FunctionComponent
=
()
=>
{
const
[
navItems
,
loading
]
=
useNavItem
s
();
const
[
components
,
,
loading
]
=
useAvailableComponent
s
();
const
history
=
useHistory
();
const
{
t
}
=
useTranslation
(
'
common
'
);
...
...
@@ -48,18 +48,20 @@ const IndexPage: FunctionComponent = () => {
const
location
=
useLocation
();
useEffect
(()
=>
{
if
(
navItem
s
.
length
)
{
if
(
navItem
s
[
0
].
path
)
{
history
.
replace
(
navItem
s
[
0
].
path
+
location
.
search
);
}
else
if
(
navItems
[
0
].
children
?.
length
&&
navItem
s
[
0
].
children
[
0
].
path
)
{
history
.
replace
(
navItem
s
[
0
].
children
[
0
].
path
+
location
.
search
);
if
(
component
s
.
length
)
{
if
(
component
s
[
0
].
path
)
{
history
.
replace
(
component
s
[
0
].
path
+
location
.
search
);
}
else
if
(
components
[
0
].
children
?.
length
&&
component
s
[
0
].
children
[
0
].
path
)
{
history
.
replace
(
component
s
[
0
].
children
[
0
].
path
+
location
.
search
);
}
}
else
{
// TODO: no component available, add a error tip
}
},
[
navItem
s
,
history
,
location
.
search
]);
},
[
component
s
,
history
,
location
.
search
]);
return
(
<
CenterWrapper
>
{
loading
||
navItem
s
.
length
?
(
{
loading
||
component
s
.
length
?
(
<
Loading
>
<
HashLoader
size
=
"60px"
color
=
{
primaryColor
}
/>
<
span
>
{
t
(
'
common:loading
'
)
}
</
span
>
...
...
frontend/packages/core/src/routes/index.ts
浏览文件 @
6da5a64d
...
...
@@ -14,7 +14,9 @@
* limitations under the License.
*/
import
React
,
{
FunctionComponent
,
LazyExoticComponent
}
from
'
react
'
;
import
type
{
FunctionComponent
,
LazyExoticComponent
}
from
'
react
'
;
import
React
from
'
react
'
;
export
enum
Pages
{
Scalar
=
'
scalar
'
,
...
...
@@ -25,7 +27,8 @@ export enum Pages {
Graph
=
'
graph
'
,
HighDimensional
=
'
high-dimensional
'
,
PRCurve
=
'
pr-curve
'
,
ROCCurve
=
'
roc-curve
'
ROCCurve
=
'
roc-curve
'
,
HyperParameter
=
'
hyper-parameter
'
}
export
interface
Route
{
...
...
frontend/packages/mock/data/components.ts
浏览文件 @
6da5a64d
...
...
@@ -14,4 +14,5 @@
* limitations under the License.
*/
export
default
[
'
embeddings
'
,
'
scalar
'
,
'
image
'
,
'
audio
'
,
'
text
'
,
'
graph
'
,
'
histogram
'
,
'
pr_curve
'
,
'
roc_curve
'
];
export
default
[
'
embeddings
'
,
'
scalar
'
,
'
image
'
,
'
text
'
,
'
graph
'
,
'
pr_curve
'
,
'
roc_curve
'
];
// export default ['embeddings', 'scalar', 'image', 'audio', 'text', 'graph', 'histogram', 'pr_curve', 'roc_curve'];
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录