mirror of
https://github.com/prymitive/karma
synced 2026-05-11 03:46:48 +00:00
162 lines
5.0 KiB
TypeScript
162 lines
5.0 KiB
TypeScript
import { use, FC, useState, useEffect, useCallback, useRef } from "react";
|
|
|
|
import { reaction } from "mobx";
|
|
import { observer } from "mobx-react-lite";
|
|
|
|
import useDimensions from "react-cool-dimensions";
|
|
|
|
import { useIdleTimer } from "react-idle-timer";
|
|
|
|
import { CSSTransition } from "react-transition-group";
|
|
|
|
import type { AlertStore } from "Stores/AlertStore";
|
|
import type { Settings } from "Stores/Settings";
|
|
import type { SilenceFormStore } from "Stores/SilenceFormStore";
|
|
import { IsMobile } from "Common/Device";
|
|
import { OverviewModal } from "Components/OverviewModal";
|
|
import { MainModal } from "Components/MainModal";
|
|
import SilenceModal from "Components/SilenceModal";
|
|
import AppToasts from "Components/Toast/AppToasts";
|
|
import { ThemeContext } from "Components/Theme";
|
|
import { Fetcher } from "Components/Fetcher";
|
|
import { FilterInput } from "./FilterInput";
|
|
import { MobileIdleTimeout, DesktopIdleTimeout } from "./timeouts";
|
|
|
|
const NavBar: FC<{
|
|
alertStore: AlertStore;
|
|
settingsStore: Settings;
|
|
silenceFormStore: SilenceFormStore;
|
|
fixedTop?: boolean;
|
|
}> = ({ alertStore, settingsStore, silenceFormStore, fixedTop = true }) => {
|
|
const [containerClass, setContainerClass] = useState<string>("visible");
|
|
|
|
const context = use(ThemeContext);
|
|
|
|
const ref = useRef<HTMLElement>(null);
|
|
const { observe, height } = useDimensions({});
|
|
|
|
const updateBodyPaddingTop = useCallback(
|
|
(idle: boolean) => {
|
|
const paddingTop = idle ? 0 : height + 8;
|
|
// eslint-disable-next-line react-compiler/react-compiler -- intentional DOM side-effect, not React state
|
|
document.body.style.paddingTop = `${paddingTop}px`;
|
|
setContainerClass(idle ? "invisible" : "visible");
|
|
|
|
const toggleEvent = new CustomEvent("navbarResize", {
|
|
detail: paddingTop,
|
|
});
|
|
window.dispatchEvent(toggleEvent);
|
|
},
|
|
[height],
|
|
);
|
|
|
|
const onActive = useCallback(() => {
|
|
alertStore.ui.setIsIdle(false);
|
|
}, [alertStore.ui]);
|
|
|
|
const onIdle = useCallback(() => {
|
|
alertStore.ui.setIsIdle(true);
|
|
}, [alertStore.ui]);
|
|
|
|
const { pause, reset } = useIdleTimer({
|
|
timeout: IsMobile() ? MobileIdleTimeout : DesktopIdleTimeout,
|
|
onIdle: onIdle,
|
|
onActive: onActive,
|
|
debounce: 500,
|
|
});
|
|
|
|
useEffect(() => {
|
|
let timer: number;
|
|
if (alertStore.ui.isIdle) {
|
|
timer = window.setTimeout(
|
|
() => updateBodyPaddingTop(true),
|
|
context.animations.duration,
|
|
);
|
|
} else {
|
|
updateBodyPaddingTop(false);
|
|
}
|
|
return () => window.clearTimeout(timer);
|
|
}, [
|
|
height,
|
|
updateBodyPaddingTop,
|
|
alertStore.ui.isIdle,
|
|
context.animations.duration,
|
|
]);
|
|
|
|
useEffect(
|
|
() =>
|
|
reaction(
|
|
() =>
|
|
!settingsStore.filterBarConfig.config.autohide ||
|
|
alertStore.status.paused ||
|
|
alertStore.filters.values.filter((f) => f.applied === false).length >
|
|
0,
|
|
(paused) => (paused ? pause() : reset()),
|
|
{ fireImmediately: true },
|
|
),
|
|
[],
|
|
);
|
|
|
|
const navRef = useRef<HTMLElement>(null);
|
|
|
|
return (
|
|
<div className={`container p-0 m-0 mw-100 ${containerClass}`}>
|
|
<CSSTransition
|
|
classNames="components-animation-navbar"
|
|
in={!alertStore.ui.isIdle}
|
|
timeout={context.animations.duration}
|
|
onEntering={() => {}}
|
|
onExited={() => {}}
|
|
enter
|
|
exit
|
|
nodeRef={navRef}
|
|
>
|
|
<nav
|
|
ref={(el) => {
|
|
observe(el as HTMLElement);
|
|
ref.current = el as HTMLElement;
|
|
navRef.current = el;
|
|
}}
|
|
className={`navbar navbar-expand navbar-dark p-1 bg-primary-transparent d-flex ${
|
|
fixedTop ? "fixed-top" : "w-100"
|
|
} align-items-start`}
|
|
>
|
|
<span className="navbar-nav d-flex flex-row">
|
|
{alertStore.info.timestamp !== "" &&
|
|
alertStore.data.upstreams.instances.length === 0 ? null : (
|
|
<span className="navbar-brand p-0 my-0 mx-2 h1 d-none d-sm-block">
|
|
<OverviewModal alertStore={alertStore} />
|
|
</span>
|
|
)}
|
|
<Fetcher alertStore={alertStore} settingsStore={settingsStore} />
|
|
</span>
|
|
{alertStore.info.timestamp !== "" &&
|
|
alertStore.data.upstreams.instances.length === 0 ? null : (
|
|
<FilterInput
|
|
alertStore={alertStore}
|
|
settingsStore={settingsStore}
|
|
/>
|
|
)}
|
|
{alertStore.info.timestamp !== "" &&
|
|
alertStore.data.upstreams.instances.length === 0 ? null : (
|
|
<ul className="navbar-nav flex-wrap flex-shrink-1 ms-1">
|
|
<AppToasts alertStore={alertStore} />
|
|
<SilenceModal
|
|
alertStore={alertStore}
|
|
silenceFormStore={silenceFormStore}
|
|
settingsStore={settingsStore}
|
|
/>
|
|
<MainModal
|
|
alertStore={alertStore}
|
|
settingsStore={settingsStore}
|
|
/>
|
|
</ul>
|
|
)}
|
|
</nav>
|
|
</CSSTransition>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default observer(NavBar);
|