diff --git a/app/vmui/packages/vmui/src/App.tsx b/app/vmui/packages/vmui/src/App.tsx index 2f700f37f..331fb267c 100644 --- a/app/vmui/packages/vmui/src/App.tsx +++ b/app/vmui/packages/vmui/src/App.tsx @@ -8,56 +8,57 @@ import DashboardsLayout from "./pages/PredefinedPanels"; import CardinalityPanel from "./pages/CardinalityPanel"; import TopQueries from "./pages/TopQueries"; import ThemeProvider from "./components/Main/ThemeProvider/ThemeProvider"; -import Spinner from "./components/Main/Spinner/Spinner"; import TracePage from "./pages/TracePage"; import ExploreMetrics from "./pages/ExploreMetrics"; import PreviewIcons from "./components/Main/Icons/PreviewIcons"; const App: FC = () => { - const [loadingTheme, setLoadingTheme] = useState(true); + const [loadedTheme, setLoadedTheme] = useState(false); return <> - {loadingTheme && } - - - + - - } - > - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - - + <> + + {loadedTheme && ( + + } + > + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + + + )} + > >; diff --git a/app/vmui/packages/vmui/src/components/Chart/BarChart/BarChart.tsx b/app/vmui/packages/vmui/src/components/Chart/BarChart/BarChart.tsx index ef2d5ad36..6f1a7a465 100644 --- a/app/vmui/packages/vmui/src/components/Chart/BarChart/BarChart.tsx +++ b/app/vmui/packages/vmui/src/components/Chart/BarChart/BarChart.tsx @@ -9,7 +9,7 @@ const BarChart: FC = ({ data, container, configs }) => { - const { darkTheme } = useAppState(); + const { isDarkTheme } = useAppState(); const uPlotRef = useRef(null); const [uPlotInst, setUPlotInst] = useState(); @@ -30,7 +30,7 @@ const BarChart: FC = ({ const u = new uPlot(options, data, uPlotRef.current); setUPlotInst(u); return u.destroy; - }, [uPlotRef.current, layoutSize, darkTheme]); + }, [uPlotRef.current, layoutSize, isDarkTheme]); useEffect(() => updateChart(), [data]); diff --git a/app/vmui/packages/vmui/src/components/Chart/LineChart/LineChart.tsx b/app/vmui/packages/vmui/src/components/Chart/LineChart/LineChart.tsx index af02f6850..c9d008f9c 100644 --- a/app/vmui/packages/vmui/src/components/Chart/LineChart/LineChart.tsx +++ b/app/vmui/packages/vmui/src/components/Chart/LineChart/LineChart.tsx @@ -48,7 +48,7 @@ const LineChart: FC = ({ container, height }) => { - const { darkTheme } = useAppState(); + const { isDarkTheme } = useAppState(); const uPlotRef = useRef(null); const [isPanning, setPanning] = useState(false); @@ -225,7 +225,7 @@ const LineChart: FC = ({ setUPlotInst(u); setXRange({ min: period.start, max: period.end }); return u.destroy; - }, [uPlotRef.current, series, layoutSize, height, darkTheme]); + }, [uPlotRef.current, series, layoutSize, height, isDarkTheme]); useEffect(() => { window.addEventListener("keydown", handleKeyDown); diff --git a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/TenantsConfiguration/TenantsConfiguration.tsx b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/TenantsConfiguration/TenantsConfiguration.tsx index bdcc7431d..2023aedf3 100644 --- a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/TenantsConfiguration/TenantsConfiguration.tsx +++ b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/TenantsConfiguration/TenantsConfiguration.tsx @@ -3,7 +3,6 @@ import { useAppDispatch, useAppState } from "../../../../state/common/StateConte import { useTimeDispatch } from "../../../../state/time/TimeStateContext"; import { ArrowDownIcon, StorageIcons } from "../../../Main/Icons"; import Button from "../../../Main/Button/Button"; -import { useFetchAccountIds } from "./hooks/useFetchAccountIds"; import "./style.scss"; import { replaceTenantId } from "../../../../utils/default-server-url"; import classNames from "classnames"; @@ -11,13 +10,12 @@ import Popper from "../../../Main/Popper/Popper"; import { getAppModeEnable } from "../../../../utils/app-mode"; import Tooltip from "../../../Main/Tooltip/Tooltip"; -const TenantsConfiguration: FC = () => { +const TenantsConfiguration: FC<{accountIds: string[]}> = ({ accountIds }) => { const appModeEnable = getAppModeEnable(); const { tenantId: tenantIdState, serverUrl } = useAppState(); const dispatch = useAppDispatch(); const timeDispatch = useTimeDispatch(); - const { accountIds } = useFetchAccountIds(); const [openOptions, setOpenOptions] = useState(false); const optionsButtonRef = useRef(null); @@ -46,7 +44,6 @@ const TenantsConfiguration: FC = () => { if (serverUrl) { const updateServerUrl = replaceTenantId(serverUrl, tenant); if (updateServerUrl === serverUrl) return; - console.log("SET_SERVER", updateServerUrl); dispatch({ type: "SET_SERVER", payload: updateServerUrl }); timeDispatch({ type: "RUN_QUERY" }); } diff --git a/app/vmui/packages/vmui/src/components/Configurators/ThemeControl/ThemeControl.tsx b/app/vmui/packages/vmui/src/components/Configurators/ThemeControl/ThemeControl.tsx index 76b48c5b7..e43cb828a 100644 --- a/app/vmui/packages/vmui/src/components/Configurators/ThemeControl/ThemeControl.tsx +++ b/app/vmui/packages/vmui/src/components/Configurators/ThemeControl/ThemeControl.tsx @@ -1,19 +1,16 @@ import React from "react"; import "./style.scss"; -import classNames from "classnames"; import { useAppDispatch, useAppState } from "../../../state/common/StateContext"; +import { Theme } from "../../../types"; +import Toggle from "../../Main/Toggle/Toggle"; -const options = [ - { title: "Light", value: false }, - { title: "Dark", value: true } -]; - +const options = Object.values(Theme).map(value => ({ title: value, value })); const ThemeControl = () => { - const { darkTheme } = useAppState(); + const { theme } = useAppState(); const dispatch = useAppDispatch(); - const createHandlerClickItem = (value: boolean) => () => { - dispatch({ type: "SET_DARK_THEME", payload: value }); + const handleClickItem = (value: string) => { + dispatch({ type: "SET_THEME", payload: value as Theme }); }; return ( @@ -21,21 +18,12 @@ const ThemeControl = () => { Theme preferences - - + - {options.map(item => ( - {item.title} - ))} ); diff --git a/app/vmui/packages/vmui/src/components/Configurators/ThemeControl/style.scss b/app/vmui/packages/vmui/src/components/Configurators/ThemeControl/style.scss index 325b86d6b..9b602e41b 100644 --- a/app/vmui/packages/vmui/src/components/Configurators/ThemeControl/style.scss +++ b/app/vmui/packages/vmui/src/components/Configurators/ThemeControl/style.scss @@ -1,44 +1,10 @@ @use "src/styles/variables" as *; .vm-theme-control { - &-options { - position: relative; + + &__toggle { display: inline-flex; - border: $border-divider; - border-radius: $border-radius-medium; - overflow: hidden; - - &__item { - position: relative; - padding: $padding-small $padding-global; - border-right: $border-divider; - color: $color-text; - cursor: pointer; - z-index: 2; - transition: color 200ms ease-out; - user-select: none; - - &:last-child { - border: none; - } - - &_active { - color: $color-primary-text; - } - - &:hover { - box-shadow: $box-shadow-popper; - } - } - - &__highlight { - position: absolute; - top: 0; - left: 0; - background-color: $color-primary; - height: 100%; - width: 50%; - transition: left 150ms ease-in; - } + min-width: 300px; + text-transform: capitalize; } } diff --git a/app/vmui/packages/vmui/src/components/Configurators/TimeRangeSettings/TimeSelector/TimeSelector.tsx b/app/vmui/packages/vmui/src/components/Configurators/TimeRangeSettings/TimeSelector/TimeSelector.tsx index 6eac16559..1df39f12d 100644 --- a/app/vmui/packages/vmui/src/components/Configurators/TimeRangeSettings/TimeSelector/TimeSelector.tsx +++ b/app/vmui/packages/vmui/src/components/Configurators/TimeRangeSettings/TimeSelector/TimeSelector.tsx @@ -17,7 +17,7 @@ import classNames from "classnames"; import { useAppState } from "../../../../state/common/StateContext"; export const TimeSelector: FC = () => { - const { darkTheme } = useAppState(); + const { isDarkTheme } = useAppState(); const wrapperRef = useRef(null); const documentSize = useResize(document.body); const displayFullDate = useMemo(() => documentSize.width > 1280, [documentSize]); @@ -150,7 +150,7 @@ export const TimeSelector: FC = () => { { - const { darkTheme } = useAppState(); + const { isDarkTheme } = useAppState(); const appModeEnable = getAppModeEnable(); + const { accountIds } = useFetchAccountIds(); const primaryColor = useMemo(() => { - const variable = darkTheme ? "color-background-block" : "color-primary"; + const variable = isDarkTheme ? "color-background-block" : "color-primary"; return getCssVariable(variable); - }, [darkTheme]); + }, [isDarkTheme]); const { background, color } = useMemo(() => { const { headerStyles: { @@ -52,7 +54,7 @@ const Header: FC = () => { className={classNames({ "vm-header": true, "vm-header_app": appModeEnable, - "vm-header_dark": darkTheme + "vm-header_dark": isDarkTheme })} style={{ background, color }} > @@ -70,7 +72,7 @@ const Header: FC = () => { background={background} /> - {headerSetup?.tenant && } + {headerSetup?.tenant && } {headerSetup?.stepControl && } {headerSetup?.timeSelector && } {headerSetup?.cardinalityDatePicker && } diff --git a/app/vmui/packages/vmui/src/components/Main/Alert/Alert.tsx b/app/vmui/packages/vmui/src/components/Main/Alert/Alert.tsx index 71fa05b6f..cdf8db49c 100644 --- a/app/vmui/packages/vmui/src/components/Main/Alert/Alert.tsx +++ b/app/vmui/packages/vmui/src/components/Main/Alert/Alert.tsx @@ -20,14 +20,14 @@ const icons = { const Alert: FC = ({ variant, children }) => { - const { darkTheme } = useAppState(); + const { isDarkTheme } = useAppState(); return ( {icons[variant || "info"]} diff --git a/app/vmui/packages/vmui/src/components/Main/DatePicker/TImePicker/TimePicker.tsx b/app/vmui/packages/vmui/src/components/Main/DatePicker/TImePicker/TimePicker.tsx index 31cb91799..ab165a669 100644 --- a/app/vmui/packages/vmui/src/components/Main/DatePicker/TImePicker/TimePicker.tsx +++ b/app/vmui/packages/vmui/src/components/Main/DatePicker/TImePicker/TimePicker.tsx @@ -14,7 +14,7 @@ enum TimeUnits { hour, minutes, seconds } const TimePicker: FC= ({ selectDate, onChangeTime, onClose }) => { - const { darkTheme } = useAppState(); + const { isDarkTheme } = useAppState(); const [activeField, setActiveField] = useState(TimeUnits.hour); const [hours, setHours] = useState(selectDate.format("HH")); @@ -159,7 +159,7 @@ const TimePicker: FC= ({ selectDate, onChangeTime, onCl = ({ autofocus, onChange }) => { - const { darkTheme } = useAppState(); + const { isDarkTheme } = useAppState(); const [search, setSearch] = useState(""); const autocompleteAnchorEl = useRef(null); @@ -111,7 +111,7 @@ const Select: FC = ({ = ({ containerStyles = {}, message }) => ( - - - - +const Spinner: FC = ({ containerStyles = {}, message }) => { + const { isDarkTheme } = useAppState(); + + return ( + + + + + + {message && {message}} - {message && {message}} - -); + ); +}; export default Spinner; diff --git a/app/vmui/packages/vmui/src/components/Main/TextField/TextField.tsx b/app/vmui/packages/vmui/src/components/Main/TextField/TextField.tsx index a1b1f9569..cbfccacbf 100644 --- a/app/vmui/packages/vmui/src/components/Main/TextField/TextField.tsx +++ b/app/vmui/packages/vmui/src/components/Main/TextField/TextField.tsx @@ -39,7 +39,7 @@ const TextField: FC = ({ onFocus, onBlur }) => { - const { darkTheme } = useAppState(); + const { isDarkTheme } = useAppState(); const inputRef = useRef(null); const textareaRef = useRef(null); @@ -83,7 +83,7 @@ const TextField: FC = ({ className={classNames({ "vm-text-field": true, "vm-text-field_textarea": type === "textarea", - "vm-text-field_dark": darkTheme + "vm-text-field_dark": isDarkTheme })} data-replicated-value={value} > diff --git a/app/vmui/packages/vmui/src/components/Main/ThemeProvider/ThemeProvider.ts b/app/vmui/packages/vmui/src/components/Main/ThemeProvider/ThemeProvider.ts index 95f5a96e2..1de5cdfa4 100644 --- a/app/vmui/packages/vmui/src/components/Main/ThemeProvider/ThemeProvider.ts +++ b/app/vmui/packages/vmui/src/components/Main/ThemeProvider/ThemeProvider.ts @@ -1,12 +1,15 @@ -import { FC, useEffect } from "preact/compat"; +import { FC, useEffect, useState } from "preact/compat"; import { getContrastColor } from "../../../utils/color"; -import { getCssVariable, setCssVariable } from "../../../utils/theme"; +import { getCssVariable, isSystemDark, setCssVariable } from "../../../utils/theme"; import { AppParams, getAppModeParams } from "../../../utils/app-mode"; import { getFromStorage } from "../../../utils/storage"; import { darkPalette, lightPalette } from "../../../constants/palette"; +import { Theme } from "../../../types"; +import { useAppDispatch, useAppState } from "../../../state/common/StateContext"; +import useSystemTheme from "../../../hooks/useSystemTheme"; -interface StyleVariablesProps { - setLoadingTheme: (val: boolean) => void +interface ThemeProviderProps { + onLoaded: (val: boolean) => void } const colorVariables = [ @@ -18,9 +21,18 @@ const colorVariables = [ "success", ]; -export const ThemeProvider: FC = ({ setLoadingTheme }) => { +export const ThemeProvider: FC = ({ onLoaded }) => { - const { palette = {} } = getAppModeParams(); + const { palette: paletteAppMode = {} } = getAppModeParams(); + const { theme } = useAppState(); + const isDarkTheme = useSystemTheme(); + const dispatch = useAppDispatch(); + + const [palette, setPalette] = useState({ + [Theme.dark]: darkPalette, + [Theme.light]: lightPalette, + [Theme.system]: isSystemDark() ? darkPalette : lightPalette + }); const setScrollbarSize = () => { const { innerWidth, innerHeight } = window; @@ -30,16 +42,21 @@ export const ThemeProvider: FC = ({ setLoadingTheme }) => { }; const setContrastText = () => { - colorVariables.forEach(variable => { + colorVariables.forEach((variable, i) => { const color = getCssVariable(`color-${variable}`); const text = getContrastColor(color); setCssVariable(`${variable}-text`, text); + + if (i === colorVariables.length - 1) { + dispatch({ type: "SET_DARK_THEME" }); + onLoaded(true); + } }); }; const setAppModePalette = () => { colorVariables.forEach(variable => { - const colorFromAppMode = palette[variable as keyof AppParams["palette"]]; + const colorFromAppMode = paletteAppMode[variable as keyof AppParams["palette"]]; if (colorFromAppMode) setCssVariable(`color-${variable}`, colorFromAppMode); }); @@ -47,25 +64,33 @@ export const ThemeProvider: FC = ({ setLoadingTheme }) => { }; const setTheme = () => { - const darkTheme = getFromStorage("DARK_THEME"); - const palette = darkTheme ? darkPalette : lightPalette; - Object.entries(palette).forEach(([variable, value]) => { + const theme = (getFromStorage("THEME") || Theme.system) as Theme; + const result = palette[theme]; + Object.entries(result).forEach(([variable, value]) => { setCssVariable(variable, value); }); setContrastText(); }; + const updatePalette = () => { + const newSystemPalette = isSystemDark() ? darkPalette : lightPalette; + if (palette[Theme.system] === newSystemPalette) { + setTheme(); + return; + } + setPalette(prev => ({ + ...prev, + [Theme.system]: newSystemPalette + })); + }; + useEffect(() => { setAppModePalette(); setScrollbarSize(); setTheme(); - setLoadingTheme(false); + }, [palette]); - window.addEventListener("storage", setTheme); - return () => { - window.removeEventListener("storage", setTheme); - }; - }, []); + useEffect(updatePalette, [theme, isDarkTheme]); return null; }; diff --git a/app/vmui/packages/vmui/src/components/Main/Toggle/Toggle.tsx b/app/vmui/packages/vmui/src/components/Main/Toggle/Toggle.tsx new file mode 100644 index 000000000..b9849bf2f --- /dev/null +++ b/app/vmui/packages/vmui/src/components/Main/Toggle/Toggle.tsx @@ -0,0 +1,94 @@ +import React, { FC, useEffect, useRef, useState } from "preact/compat"; +import classNames from "classnames"; +import { ReactNode } from "react"; +import "./style.scss"; + +interface ToggleProps { + options: {value: string, title?: string, icon?: ReactNode}[] + value: string + onChange: (val: string) => void + label?: string +} + +const Toggle: FC = ({ options, value, label, onChange }) => { + + const activeRef = useRef(null); + const [position, setPosition] = useState({ + width: "0px", + left: "0px", + borderRadius: "0px" + }); + + const createHandlerChange = (value: string) => () => { + onChange(value); + }; + + useEffect(() => { + if (!activeRef.current) { + setPosition({ + width: "0px", + left: "0px", + borderRadius: "0px" + }); + return; + } + const index = options.findIndex(o => o.value === value); + const { width: widthRect } = activeRef.current.getBoundingClientRect(); + + let width = widthRect; + let left = index * width; + let borderRadius = "0"; + if (index === 0) borderRadius = "16px 0 0 16px"; + + if (index === options.length - 1) { + borderRadius = "10px"; + left -= 1; + borderRadius = "0 16px 16px 0"; + } + + if (index !== 0 && (index !== options.length - 1)) { + width += 1; + left -= 1; + } + + + setPosition({ width: `${width}px`, left: `${left}px`, borderRadius }); + }, [activeRef, value, options]); + + return ( + + {label && ( + + {label} + + )} + + {position.borderRadius && } + {options.map((option, i) => ( + + {option.icon} + {option.title} + + ))} + + + ); +}; + +export default Toggle; diff --git a/app/vmui/packages/vmui/src/components/Main/Toggle/style.scss b/app/vmui/packages/vmui/src/components/Main/Toggle/style.scss new file mode 100644 index 000000000..ef78c3db0 --- /dev/null +++ b/app/vmui/packages/vmui/src/components/Main/Toggle/style.scss @@ -0,0 +1,81 @@ +@use "src/styles/variables" as *; + +.vm-toggles { + position: relative; + display: grid; + gap: 3px; + width: 100%; + + &__label { + padding: 0 $padding-global; + color: $color-text-secondary; + font-size: $font-size-small; + line-height: 1; + } + + &-group { + position: relative; + display: grid; + width: 100%; + align-items: center; + justify-content: center; + overflow: hidden; + + &-item { + position: relative; + display: grid; + align-items: center; + justify-content: center; + padding: $padding-small; + border-right: $border-divider; + border-top: $border-divider; + border-bottom: $border-divider; + font-size: $font-size-small; + color: $color-text-secondary; + font-weight: bold; + cursor: pointer; + text-align: center; + transition: color 150ms ease-in; + z-index: 2; + user-select: none; + + &_first { + border-radius: 16px 0 0 16px; + border-left: $border-divider + } + + &:last-child { + border-radius: 0 16px 16px 0; + border-left: none; + } + + &_icon { + grid-template-columns: 14px auto; + gap: 4px; + } + + &:hover { + color: $color-primary; + } + + &_active { + color: $color-primary; + border-color: transparent; + + &:hover { + background-color: transparent; + } + } + } + + &__highlight { + position: absolute; + top: 0; + height: 100%; + background-color: rgba($color-primary, 0.08); + border: 1px solid $color-primary; + transition: left 200ms cubic-bezier(0.280, 0.840, 0.420, 1), border-radius 200ms linear; + z-index: 1; + } + } +} diff --git a/app/vmui/packages/vmui/src/components/TraceQuery/NestedNav/NestedNav.tsx b/app/vmui/packages/vmui/src/components/TraceQuery/NestedNav/NestedNav.tsx index d3deff218..909142ec5 100644 --- a/app/vmui/packages/vmui/src/components/TraceQuery/NestedNav/NestedNav.tsx +++ b/app/vmui/packages/vmui/src/components/TraceQuery/NestedNav/NestedNav.tsx @@ -16,7 +16,7 @@ interface OpenLevels { } const NestedNav: FC = ({ trace, totalMsec }) => { - const { darkTheme } = useAppState(); + const { isDarkTheme } = useAppState(); const [openLevels, setOpenLevels] = useState({} as OpenLevels); const handleListClick = (level: number) => () => { @@ -31,7 +31,7 @@ const NestedNav: FC = ({ trace, totalMsec }) => { { + const [isDarkTheme, setIsDarkTheme] = useState(isSystemDark()); + + const mqListener = ((e: MediaQueryListEvent) => { + setIsDarkTheme(e.matches); + }); + + useEffect(() => { + const darkThemeMq = window.matchMedia("(prefers-color-scheme: dark)"); + darkThemeMq.addEventListener("change", mqListener); + return () => darkThemeMq.removeEventListener("change", mqListener); + }, []); + + return isDarkTheme; +}; + +export default useThemeDetector; diff --git a/app/vmui/packages/vmui/src/pages/CardinalityPanel/CardinalityConfigurator/CardinalityConfigurator.tsx b/app/vmui/packages/vmui/src/pages/CardinalityPanel/CardinalityConfigurator/CardinalityConfigurator.tsx index b779b064d..49adfceda 100644 --- a/app/vmui/packages/vmui/src/pages/CardinalityPanel/CardinalityConfigurator/CardinalityConfigurator.tsx +++ b/app/vmui/packages/vmui/src/pages/CardinalityPanel/CardinalityConfigurator/CardinalityConfigurator.tsx @@ -1,14 +1,13 @@ -import React, { FC } from "react"; +import React, { FC, useMemo } from "react"; import QueryEditor from "../../../components/Configurators/QueryEditor/QueryEditor"; import { useFetchQueryOptions } from "../../../hooks/useFetchQueryOptions"; import { ErrorTypes } from "../../../types"; import { useQueryDispatch, useQueryState } from "../../../state/query/QueryStateContext"; import Switch from "../../../components/Main/Switch/Switch"; -import { PlayIcon, QuestionIcon } from "../../../components/Main/Icons"; +import { InfoIcon, PlayIcon, QuestionIcon, WikiIcon } from "../../../components/Main/Icons"; import Button from "../../../components/Main/Button/Button"; import TextField from "../../../components/Main/TextField/TextField"; import "./style.scss"; -import { useMemo } from "preact/compat"; import Tooltip from "../../../components/Main/Tooltip/Tooltip"; export interface CardinalityConfiguratorProps { @@ -65,7 +64,7 @@ const CardinalityConfigurator: FC = ({ = ({ type="text" value={focusLabel || ""} onChange={onFocusLabelChange} + endIcon={( + + To identify values with the highest number of series for the selected label. + Adds a table showing the series with the highest number of series. + + )} + > + + + )} /> - + = ({ Show top {topN} entries per table. - - } - /> - + + Documentation + + + + Example of using } diff --git a/app/vmui/packages/vmui/src/pages/CardinalityPanel/CardinalityConfigurator/style.scss b/app/vmui/packages/vmui/src/pages/CardinalityPanel/CardinalityConfigurator/style.scss index 356529c46..2b8c2b82c 100644 --- a/app/vmui/packages/vmui/src/pages/CardinalityPanel/CardinalityConfigurator/style.scss +++ b/app/vmui/packages/vmui/src/pages/CardinalityPanel/CardinalityConfigurator/style.scss @@ -16,20 +16,23 @@ } } + &-additional { + display: flex; + align-items: center; + } + &-bottom { - display: grid; - grid-template-columns: 1fr auto auto; - align-items: flex-end; - gap: $padding-small; + display: flex; + align-items: center; + gap: $padding-global; &__info { + flex-grow: 1; font-size: $font-size; } - &__docs { - display: flex; - align-items: center; - justify-content: center; + a { + color: $color-text-secondary; } } } diff --git a/app/vmui/packages/vmui/src/pages/CardinalityPanel/index.tsx b/app/vmui/packages/vmui/src/pages/CardinalityPanel/index.tsx index ca1fad605..dcc237501 100644 --- a/app/vmui/packages/vmui/src/pages/CardinalityPanel/index.tsx +++ b/app/vmui/packages/vmui/src/pages/CardinalityPanel/index.tsx @@ -31,10 +31,6 @@ const Index: FC = () => { cardinalityDispatch({ type: "RUN_QUERY" }); }; - const onSetQuery = (query: string) => { - setQuery(query); - }; - const onSetHistory = (step: number) => { const newIndexHistory = queryHistoryIndex + step; if (newIndexHistory < 0 || newIndexHistory >= queryHistory.length) return; @@ -86,7 +82,7 @@ const Index: FC = () => { totalLabelValuePairs={tsdbStatusData.totalLabelValuePairs} focusLabel={focusLabel} onRunQuery={onRunQuery} - onSetQuery={onSetQuery} + onSetQuery={setQuery} onSetHistory={onSetHistory} onTopNChange={onTopNChange} onFocusLabelChange={onFocusLabelChange} diff --git a/app/vmui/packages/vmui/src/router/index.ts b/app/vmui/packages/vmui/src/router/index.ts index becc31ad2..b68ace2da 100644 --- a/app/vmui/packages/vmui/src/router/index.ts +++ b/app/vmui/packages/vmui/src/router/index.ts @@ -8,16 +8,18 @@ const router = { icons: "/icons" }; +export interface RouterOptionsHeader { + tenant?: boolean, + stepControl?: boolean, + timeSelector?: boolean, + executionControls?: boolean, + globalSettings?: boolean, + cardinalityDatePicker?: boolean +} + export interface RouterOptions { title?: string, - header: { - tenant?: boolean, - stepControl?: boolean, - timeSelector?: boolean, - executionControls?: boolean, - globalSettings?: boolean, - cardinalityDatePicker?: boolean - } + header: RouterOptionsHeader } const routerOptionsDefault = { diff --git a/app/vmui/packages/vmui/src/state/common/reducer.ts b/app/vmui/packages/vmui/src/state/common/reducer.ts index 6e91978e9..a03b74920 100644 --- a/app/vmui/packages/vmui/src/state/common/reducer.ts +++ b/app/vmui/packages/vmui/src/state/common/reducer.ts @@ -1,24 +1,29 @@ import { getDefaultServer } from "../../utils/default-server-url"; import { getQueryStringValue } from "../../utils/query-string"; import { getFromStorage, saveToStorage } from "../../utils/storage"; +import { Theme } from "../../types"; +import { isDarkTheme } from "../../utils/theme"; export interface AppState { serverUrl: string; tenantId: string; - darkTheme: boolean + theme: Theme; + isDarkTheme: boolean | null; } export type Action = | { type: "SET_SERVER", payload: string } - | { type: "SET_DARK_THEME", payload: boolean } + | { type: "SET_THEME", payload: Theme } | { type: "SET_TENANT_ID", payload: string } + | { type: "SET_DARK_THEME" } const tenantId = getQueryStringValue("g0.tenantID", "") as string; export const initialState: AppState = { serverUrl: getDefaultServer(tenantId), tenantId, - darkTheme: !!getFromStorage("DARK_THEME") + theme: (getFromStorage("THEME") || Theme.system) as Theme, + isDarkTheme: null }; export function reducer(state: AppState, action: Action): AppState { @@ -33,11 +38,16 @@ export function reducer(state: AppState, action: Action): AppState { ...state, tenantId: action.payload }; - case "SET_DARK_THEME": - saveToStorage("DARK_THEME", action.payload); + case "SET_THEME": + saveToStorage("THEME", action.payload); return { ...state, - darkTheme: action.payload + theme: action.payload, + }; + case "SET_DARK_THEME": + return { + ...state, + isDarkTheme: isDarkTheme(state.theme) }; default: throw new Error(); diff --git a/app/vmui/packages/vmui/src/styles/components/link.scss b/app/vmui/packages/vmui/src/styles/components/link.scss index faf94d64a..fc5f2af5d 100644 --- a/app/vmui/packages/vmui/src/styles/components/link.scss +++ b/app/vmui/packages/vmui/src/styles/components/link.scss @@ -8,6 +8,14 @@ color: $color-primary; } + &_with-icon { + display: grid; + align-items: center; + justify-content: center; + gap: 6px; + grid-template-columns: 14px auto; + } + &:hover { color: $color-primary; text-decoration: underline; diff --git a/app/vmui/packages/vmui/src/types/index.ts b/app/vmui/packages/vmui/src/types/index.ts index 3f179d8c8..76af3b6e8 100644 --- a/app/vmui/packages/vmui/src/types/index.ts +++ b/app/vmui/packages/vmui/src/types/index.ts @@ -119,3 +119,9 @@ export interface GraphSize { isDefault?: boolean, height: () => number } + +export enum Theme { + system = "system", + light = "light", + dark = "dark", +} diff --git a/app/vmui/packages/vmui/src/utils/storage.ts b/app/vmui/packages/vmui/src/utils/storage.ts index f9c77029b..e4fafeaeb 100644 --- a/app/vmui/packages/vmui/src/utils/storage.ts +++ b/app/vmui/packages/vmui/src/utils/storage.ts @@ -7,7 +7,7 @@ export type StorageKeys = "BASIC_AUTH_DATA" | "SERIES_LIMITS" | "TABLE_COMPACT" | "TIMEZONE" - | "DARK_THEME" + | "THEME" export const saveToStorage = (key: StorageKeys, value: string | boolean | Record): void => { if (value) { diff --git a/app/vmui/packages/vmui/src/utils/theme.ts b/app/vmui/packages/vmui/src/utils/theme.ts index f4b1bcd68..fa1659cdb 100644 --- a/app/vmui/packages/vmui/src/utils/theme.ts +++ b/app/vmui/packages/vmui/src/utils/theme.ts @@ -1,3 +1,5 @@ +import { Theme } from "../types"; + export const getCssVariable = (variable: string) => { return getComputedStyle(document.documentElement).getPropertyValue(`--${variable}`); }; @@ -5,3 +7,8 @@ export const getCssVariable = (variable: string) => { export const setCssVariable = (variable: string, value: string) => { document.documentElement.style.setProperty(`--${variable}`, value); }; + +export const isSystemDark = () => window.matchMedia("(prefers-color-scheme: dark)").matches; + +export const isDarkTheme = (theme: Theme) => (theme === Theme.system && isSystemDark()) || theme === Theme.dark; +
To identify values with the highest number of series for the selected label.
Adds a table showing the series with the highest number of series.