mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2025-01-20 15:16:42 +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 [activeMenu, setActiveMenu] = useState(pathname);
|
||||||
|
|
||||||
const handleChangeTab = (value: string) => {
|
|
||||||
setActiveMenu(value);
|
|
||||||
navigate(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const headerSetup = useMemo(() => {
|
const headerSetup = useMemo(() => {
|
||||||
return ((routerOptions[pathname] || {}) as RouterOptions).header || {};
|
return ((routerOptions[pathname] || {}) as RouterOptions).header || {};
|
||||||
}, [pathname]);
|
}, [pathname]);
|
||||||
|
@ -90,7 +85,7 @@ const Header: FC = () => {
|
||||||
>
|
>
|
||||||
{!appModeEnable && (
|
{!appModeEnable && (
|
||||||
<div
|
<div
|
||||||
className="vm-header__logo"
|
className="vm-header-logo"
|
||||||
onClick={onClickLogo}
|
onClick={onClickLogo}
|
||||||
style={{ color }}
|
style={{ color }}
|
||||||
>
|
>
|
||||||
|
@ -99,10 +94,10 @@ const Header: FC = () => {
|
||||||
)}
|
)}
|
||||||
<div className="vm-header-nav">
|
<div className="vm-header-nav">
|
||||||
<Tabs
|
<Tabs
|
||||||
|
isNavLink
|
||||||
activeItem={activeMenu}
|
activeItem={activeMenu}
|
||||||
items={routes.filter(r => !r.hide)}
|
items={routes.filter(r => !r.hide)}
|
||||||
color={color}
|
color={color}
|
||||||
onChange={handleChangeTab}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="vm-header__settings">
|
<div className="vm-header__settings">
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
.vm-header {
|
.vm-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
padding: $padding-small $padding-medium;
|
padding: $padding-small $padding-medium;
|
||||||
|
@ -11,15 +12,35 @@
|
||||||
padding: $padding-small 0;
|
padding: $padding-small 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__logo {
|
@media (max-width: 1200px) {
|
||||||
|
gap: $padding-global;
|
||||||
|
|
||||||
|
.vm-tabs {
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-logo {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: flex-start;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 65px;
|
max-width: 65px;
|
||||||
|
min-width: 65px;
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
min-width: 14px;
|
||||||
|
max-width: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
max-width: 65px;
|
||||||
|
min-width: 65px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-nav {
|
&-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 { ReactNode, useEffect } from "react";
|
||||||
import "./style.scss";
|
|
||||||
import classNames from "classnames";
|
|
||||||
import { getCssVariable } from "../../../utils/theme";
|
import { getCssVariable } from "../../../utils/theme";
|
||||||
import useResize from "../../../hooks/useResize";
|
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 {
|
interface TabsProps {
|
||||||
activeItem: string
|
activeItem: string
|
||||||
items: {value: string, label?: string, icon?: ReactNode, className?: string}[]
|
items: TabItemType[]
|
||||||
color?: string
|
color?: string
|
||||||
onChange: (value: string) => void
|
onChange?: (value: string) => void
|
||||||
indicatorPlacement?: "bottom" | "top"
|
indicatorPlacement?: "bottom" | "top"
|
||||||
|
isNavLink?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const Tabs: FC<TabsProps> = ({
|
const Tabs: FC<TabsProps> = ({
|
||||||
|
@ -18,19 +26,16 @@ const Tabs: FC<TabsProps> = ({
|
||||||
items,
|
items,
|
||||||
color = getCssVariable("color-primary"),
|
color = getCssVariable("color-primary"),
|
||||||
onChange,
|
onChange,
|
||||||
indicatorPlacement = "bottom"
|
indicatorPlacement = "bottom",
|
||||||
|
isNavLink,
|
||||||
}) => {
|
}) => {
|
||||||
const windowSize = useResize(document.body);
|
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 [indicatorPosition, setIndicatorPosition] = useState({ left: 0, width: 0, bottom: 0 });
|
||||||
|
|
||||||
const createHandlerClickTab = (value: string) => () => {
|
|
||||||
onChange(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if(activeNavRef.current) {
|
if(activeNavRef.current?.base instanceof HTMLElement) {
|
||||||
const { offsetLeft: left, offsetWidth: width, offsetHeight: height } = activeNavRef.current;
|
const { offsetLeft: left, offsetWidth: width, offsetHeight: height } = activeNavRef.current.base;
|
||||||
const positionTop = indicatorPlacement === "top";
|
const positionTop = indicatorPlacement === "top";
|
||||||
setIndicatorPosition({ left, width, bottom: positionTop ? height - 2 : 0 });
|
setIndicatorPosition({ left, width, bottom: positionTop ? height - 2 : 0 });
|
||||||
}
|
}
|
||||||
|
@ -38,29 +43,15 @@ const Tabs: FC<TabsProps> = ({
|
||||||
|
|
||||||
return <div className="vm-tabs">
|
return <div className="vm-tabs">
|
||||||
{items.map(item => (
|
{items.map(item => (
|
||||||
<div
|
<TabItem
|
||||||
className={classNames({
|
|
||||||
"vm-tabs-item": true,
|
|
||||||
"vm-tabs-item_active": activeItem === item.value,
|
|
||||||
[item.className || ""]: item.className
|
|
||||||
})}
|
|
||||||
ref={activeItem === item.value ? activeNavRef : undefined}
|
|
||||||
key={item.value}
|
key={item.value}
|
||||||
style={{ color: color }}
|
activeItem={activeItem}
|
||||||
onClick={createHandlerClickTab(item.value)}
|
item={item}
|
||||||
>
|
onChange={onChange}
|
||||||
{item.icon && (
|
color={color}
|
||||||
<div
|
activeNavRef={activeNavRef}
|
||||||
className={classNames({
|
isNavLink={isNavLink}
|
||||||
"vm-tabs-item__icon": true,
|
/>
|
||||||
"vm-tabs-item__icon_single": !item.label
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{item.icon}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{item.label}
|
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
<div
|
<div
|
||||||
className="vm-tabs__indicator"
|
className="vm-tabs__indicator"
|
||||||
|
|
|
@ -27,6 +27,10 @@
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
&__icon {
|
&__icon {
|
||||||
display: grid;
|
display: grid;
|
||||||
width: 15px;
|
width: 15px;
|
||||||
|
|
Loading…
Reference in a new issue