mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2025-01-10 15:14:09 +00:00
feat: make nav menu as links (#3646)
This commit is contained in:
parent
5815a98957
commit
e37bac07a0
6 changed files with 141 additions and 44 deletions
|
@ -58,11 +58,6 @@ const Header: FC = () => {
|
|||
|
||||
const [activeMenu, setActiveMenu] = useState(pathname);
|
||||
|
||||
const handleChangeTab = (value: string) => {
|
||||
setActiveMenu(value);
|
||||
navigate(value);
|
||||
};
|
||||
|
||||
const headerSetup = useMemo(() => {
|
||||
return ((routerOptions[pathname] || {}) as RouterOptions).header || {};
|
||||
}, [pathname]);
|
||||
|
@ -90,7 +85,7 @@ const Header: FC = () => {
|
|||
>
|
||||
{!appModeEnable && (
|
||||
<div
|
||||
className="vm-header__logo"
|
||||
className="vm-header-logo"
|
||||
onClick={onClickLogo}
|
||||
style={{ color }}
|
||||
>
|
||||
|
@ -99,10 +94,10 @@ const Header: FC = () => {
|
|||
)}
|
||||
<div className="vm-header-nav">
|
||||
<Tabs
|
||||
isNavLink
|
||||
activeItem={activeMenu}
|
||||
items={routes.filter(r => !r.hide)}
|
||||
color={color}
|
||||
onChange={handleChangeTab}
|
||||
/>
|
||||
</div>
|
||||
<div className="vm-header__settings">
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
.vm-header {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: $padding-small $padding-medium;
|
||||
|
@ -11,15 +12,35 @@
|
|||
padding: $padding-small 0;
|
||||
}
|
||||
|
||||
&__logo {
|
||||
@media (max-width: 1200px) {
|
||||
gap: $padding-global;
|
||||
|
||||
.vm-tabs {
|
||||
gap: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-logo {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
justify-content: flex-start;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
max-width: 65px;
|
||||
min-width: 65px;
|
||||
margin-bottom: 2px;
|
||||
overflow: hidden;
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
min-width: 14px;
|
||||
max-width: 14px;
|
||||
}
|
||||
|
||||
svg {
|
||||
max-width: 65px;
|
||||
min-width: 65px;
|
||||
}
|
||||
}
|
||||
|
||||
&-nav {
|
||||
|
|
57
app/vmui/packages/vmui/src/components/Main/Tabs/TabItem.tsx
Normal file
57
app/vmui/packages/vmui/src/components/Main/Tabs/TabItem.tsx
Normal file
|
@ -0,0 +1,57 @@
|
|||
import React, { Component, FC, Ref } from "preact/compat";
|
||||
import classNames from "classnames";
|
||||
import { getCssVariable } from "../../../utils/theme";
|
||||
import { TabItemType } from "./Tabs";
|
||||
import TabItemWrapper from "./TabItemWrapper";
|
||||
import "./style.scss";
|
||||
|
||||
interface TabItemProps {
|
||||
activeItem: string
|
||||
item: TabItemType
|
||||
color?: string
|
||||
onChange?: (value: string) => void
|
||||
activeNavRef: Ref<Component>
|
||||
isNavLink?: boolean
|
||||
}
|
||||
|
||||
const TabItem: FC<TabItemProps> = ({
|
||||
activeItem,
|
||||
item,
|
||||
color = getCssVariable("color-primary"),
|
||||
activeNavRef,
|
||||
onChange,
|
||||
isNavLink
|
||||
}) => {
|
||||
const createHandlerClickTab = (value: string) => () => {
|
||||
onChange && onChange(value);
|
||||
};
|
||||
|
||||
return (
|
||||
<TabItemWrapper
|
||||
className={classNames({
|
||||
"vm-tabs-item": true,
|
||||
"vm-tabs-item_active": activeItem === item.value,
|
||||
[item.className || ""]: item.className
|
||||
})}
|
||||
isNavLink={isNavLink}
|
||||
to={item.value}
|
||||
style={{ color: color }}
|
||||
onClick={createHandlerClickTab(item.value)}
|
||||
ref={activeItem === item.value ? activeNavRef : undefined}
|
||||
>
|
||||
{item.icon && (
|
||||
<div
|
||||
className={classNames({
|
||||
"vm-tabs-item__icon": true,
|
||||
"vm-tabs-item__icon_single": !item.label
|
||||
})}
|
||||
>
|
||||
{item.icon}
|
||||
</div>
|
||||
)}
|
||||
{item.label}
|
||||
</TabItemWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default TabItem;
|
|
@ -0,0 +1,29 @@
|
|||
import { ReactNode } from "react";
|
||||
import React, { FC } from "preact/compat";
|
||||
import { NavLink } from "react-router-dom";
|
||||
|
||||
interface TabItemWrapperProps {
|
||||
to: string
|
||||
isNavLink?: boolean
|
||||
className: string
|
||||
style: { color: string }
|
||||
children: ReactNode
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
const TabItemWrapper: FC<TabItemWrapperProps> = ({ to, isNavLink, children, ...props }) => {
|
||||
if (isNavLink) {
|
||||
return (
|
||||
<NavLink
|
||||
to={to}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</NavLink>
|
||||
);
|
||||
}
|
||||
|
||||
return <div {...props}>{children}</div>;
|
||||
};
|
||||
|
||||
export default TabItemWrapper;
|
|
@ -1,16 +1,24 @@
|
|||
import React, { FC, useRef, useState } from "preact/compat";
|
||||
import React, { Component, FC, useRef, useState } from "preact/compat";
|
||||
import { ReactNode, useEffect } from "react";
|
||||
import "./style.scss";
|
||||
import classNames from "classnames";
|
||||
import { getCssVariable } from "../../../utils/theme";
|
||||
import useResize from "../../../hooks/useResize";
|
||||
import TabItem from "./TabItem";
|
||||
import "./style.scss";
|
||||
|
||||
export interface TabItemType {
|
||||
value: string
|
||||
label?: string
|
||||
icon?: ReactNode
|
||||
className?: string
|
||||
}
|
||||
|
||||
interface TabsProps {
|
||||
activeItem: string
|
||||
items: {value: string, label?: string, icon?: ReactNode, className?: string}[]
|
||||
items: TabItemType[]
|
||||
color?: string
|
||||
onChange: (value: string) => void
|
||||
onChange?: (value: string) => void
|
||||
indicatorPlacement?: "bottom" | "top"
|
||||
isNavLink?: boolean
|
||||
}
|
||||
|
||||
const Tabs: FC<TabsProps> = ({
|
||||
|
@ -18,19 +26,16 @@ const Tabs: FC<TabsProps> = ({
|
|||
items,
|
||||
color = getCssVariable("color-primary"),
|
||||
onChange,
|
||||
indicatorPlacement = "bottom"
|
||||
indicatorPlacement = "bottom",
|
||||
isNavLink,
|
||||
}) => {
|
||||
const windowSize = useResize(document.body);
|
||||
const activeNavRef = useRef<HTMLDivElement>(null);
|
||||
const activeNavRef = useRef<Component>(null);
|
||||
const [indicatorPosition, setIndicatorPosition] = useState({ left: 0, width: 0, bottom: 0 });
|
||||
|
||||
const createHandlerClickTab = (value: string) => () => {
|
||||
onChange(value);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if(activeNavRef.current) {
|
||||
const { offsetLeft: left, offsetWidth: width, offsetHeight: height } = activeNavRef.current;
|
||||
if(activeNavRef.current?.base instanceof HTMLElement) {
|
||||
const { offsetLeft: left, offsetWidth: width, offsetHeight: height } = activeNavRef.current.base;
|
||||
const positionTop = indicatorPlacement === "top";
|
||||
setIndicatorPosition({ left, width, bottom: positionTop ? height - 2 : 0 });
|
||||
}
|
||||
|
@ -38,29 +43,15 @@ const Tabs: FC<TabsProps> = ({
|
|||
|
||||
return <div className="vm-tabs">
|
||||
{items.map(item => (
|
||||
<div
|
||||
className={classNames({
|
||||
"vm-tabs-item": true,
|
||||
"vm-tabs-item_active": activeItem === item.value,
|
||||
[item.className || ""]: item.className
|
||||
})}
|
||||
ref={activeItem === item.value ? activeNavRef : undefined}
|
||||
<TabItem
|
||||
key={item.value}
|
||||
style={{ color: color }}
|
||||
onClick={createHandlerClickTab(item.value)}
|
||||
>
|
||||
{item.icon && (
|
||||
<div
|
||||
className={classNames({
|
||||
"vm-tabs-item__icon": true,
|
||||
"vm-tabs-item__icon_single": !item.label
|
||||
})}
|
||||
>
|
||||
{item.icon}
|
||||
</div>
|
||||
)}
|
||||
{item.label}
|
||||
</div>
|
||||
activeItem={activeItem}
|
||||
item={item}
|
||||
onChange={onChange}
|
||||
color={color}
|
||||
activeNavRef={activeNavRef}
|
||||
isNavLink={isNavLink}
|
||||
/>
|
||||
))}
|
||||
<div
|
||||
className="vm-tabs__indicator"
|
||||
|
|
|
@ -27,6 +27,10 @@
|
|||
opacity: 1;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
display: grid;
|
||||
width: 15px;
|
||||
|
|
Loading…
Reference in a new issue