diff --git a/app/vmui/packages/vmui/index.html b/app/vmui/packages/vmui/index.html index 314f336e03..8bd3a787ff 100644 --- a/app/vmui/packages/vmui/index.html +++ b/app/vmui/packages/vmui/index.html @@ -2,9 +2,9 @@ - - - + + + diff --git a/app/vmui/packages/vmui/public/favicon.svg b/app/vmui/packages/vmui/src/assets/favicon.svg similarity index 100% rename from app/vmui/packages/vmui/public/favicon.svg rename to app/vmui/packages/vmui/src/assets/favicon.svg diff --git a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/BrowserTabController/BrowserTabController.tsx b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/BrowserTabController/BrowserTabController.tsx new file mode 100644 index 0000000000..45ee33caeb --- /dev/null +++ b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/BrowserTabController/BrowserTabController.tsx @@ -0,0 +1,61 @@ +import { FC, useMemo } from "preact/compat"; +import "./style.scss"; +import { createFaviconUrl } from "../../../../utils/favicon"; +import { faviconColors } from "../../../../constants/faviconsColors"; +import classNames from "classnames"; +import { useBrowserTabSync } from "./hooks/useBrowserTabSync"; +import { CloseIcon } from "../../../Main/Icons"; + +const BrowserTabController: FC = () => { + const { faviconColor, changeFaviconColor } = useBrowserTabSync(); + + const faviconUrl = useMemo(() => { + return createFaviconUrl(faviconColor); + }, [faviconColor]); + + const createHandlerClick = (color?: string) => () => { + changeFaviconColor(color); + }; + + return ( +
+

Favicon color

+ +
+
+ + + {faviconColors.map(color => ( +
+ + Favicon preview +
+
+ ); +}; + +export default BrowserTabController; diff --git a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/BrowserTabController/hooks/useBrowserTabSync.ts b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/BrowserTabController/hooks/useBrowserTabSync.ts new file mode 100644 index 0000000000..6c32ab2540 --- /dev/null +++ b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/BrowserTabController/hooks/useBrowserTabSync.ts @@ -0,0 +1,48 @@ +import useEventListener from "../../../../../hooks/useEventListener"; +import { useEffect, useState } from "preact/compat"; +import { getFromStorage, saveToStorage } from "../../../../../utils/storage"; +import { getFaviconStorageKey, updateFaviconColor } from "../../../../../utils/favicon"; + +const FAVICON_STORAGE_KEY = "FAVICON_COLOR"; + +const storageKey = getFaviconStorageKey(); + +const getColorsFromStorage = () => { + return getFromStorage(FAVICON_STORAGE_KEY) as Record | null; +}; + +export const useBrowserTabSync = () => { + const [faviconColor, setFaviconColor] = useState(() => getColorsFromStorage()?.[storageKey]); + + const handleUpdateColor = () => { + const colorsFromStorage = getColorsFromStorage(); + const color = colorsFromStorage?.[storageKey]; + setFaviconColor(color); + }; + + const changeFaviconColor = (color?: string) => { + const { [storageKey]: _, ...colorsFromStorage } = getColorsFromStorage() || {}; + const nextColors = { ...colorsFromStorage }; + + if (color) { + nextColors[storageKey] = color; + } + + saveToStorage(FAVICON_STORAGE_KEY, nextColors); + }; + + useEffect(() => { + handleUpdateColor(); + }, []); + + useEffect(() => { + updateFaviconColor(faviconColor); + }, [faviconColor]); + + useEventListener("storage", handleUpdateColor); + + return { + faviconColor, + changeFaviconColor, + }; +}; diff --git a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/BrowserTabController/style.scss b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/BrowserTabController/style.scss new file mode 100644 index 0000000000..1449cda26d --- /dev/null +++ b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/BrowserTabController/style.scss @@ -0,0 +1,77 @@ +@use "src/styles/variables" as *; + +$color-item-size: 28px; +$outline-width: 2px; +$outline-offset: 2px; +$outline-space: $outline-width + $outline-offset; + +.vm-browser-tab-controller { + .vm-server-configurator__title { + padding-bottom: 2px; + } + + &-palette { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: calc($padding-large * 2); + + &-list { + display: flex; + align-items: center; + justify-content: flex-start; + flex-wrap: wrap; + gap: $padding-small; + + &__item { + width: $color-item-size; + height: $color-item-size; + aspect-ratio: 1; + border-radius: 50%; + background-color: currentColor; + cursor: pointer; + transition-property: transform; + transition-duration: 0.15s; + transition-timing-function: linear; + + &:hover { + transform: scale(1.1); + } + + &:focus-visible { + transform: scale(1.1); + } + + &_reset { + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border: $border-divider; + color: $color-text-disabled; + padding: calc($padding-small / 2); + } + + &_selected { + width: $color-item-size - 2 * $outline-space; + height: $color-item-size - 2 * $outline-space; + margin: $outline-space; + outline: $outline-width solid currentColor; + outline-offset: $outline-offset; + pointer-events: none; + } + } + + &__input { + position: absolute; + z-index: -1; + opacity: 0; + } + } + + &__preview { + width: $color-item-size; + height: auto; + } + } +} diff --git a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/GlobalSettings.tsx b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/GlobalSettings.tsx index 6f339f4f13..6a07df8595 100644 --- a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/GlobalSettings.tsx +++ b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/GlobalSettings.tsx @@ -1,4 +1,4 @@ -import { forwardRef, useImperativeHandle, useRef } from "preact/compat"; +import { forwardRef, useImperativeHandle } from "preact/compat"; import { ArrowDownIcon, SettingsIcon } from "../../Main/Icons"; import Button from "../../Main/Button/Button"; import Modal from "../../Main/Modal/Modal"; @@ -11,6 +11,7 @@ import ThemeControl from "../ThemeControl/ThemeControl"; import useDeviceDetect from "../../../hooks/useDeviceDetect"; import useBoolean from "../../../hooks/useBoolean"; import QueryTimeOverride from "./QueryTimeOverride/QueryTimeOverride"; +import BrowserTabController from "./BrowserTabController/BrowserTabController"; const title = "Settings"; @@ -27,27 +28,16 @@ const GlobalSettings = forwardRef((_, ref) => { const appModeEnable = getAppModeEnable(); - const serverSettingRef = useRef(null); - const limitsSettingRef = useRef(null); - const timezoneSettingRef = useRef(null); - const { value: open, setTrue: handleOpen, setFalse: handleClose, } = useBoolean(false); - const handleApply = () => { - serverSettingRef.current && serverSettingRef.current.handleApply(); - limitsSettingRef.current && limitsSettingRef.current.handleApply(); - timezoneSettingRef.current && timezoneSettingRef.current.handleApply(); - handleClose(); - }; - const controls = [ { show: true, - component: + component: }, { show: true, @@ -56,7 +46,11 @@ const GlobalSettings = forwardRef((_, ref) => { { show: !appModeEnable, component: - } + }, + { + show: true, + component: + }, ].filter(control => control.show); useImperativeHandle(ref, () => ({ @@ -108,22 +102,6 @@ const GlobalSettings = forwardRef((_, ref) => { {control.component} ))} -
- - -
)} diff --git a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/QueryTimeOverride/QueryTimeOverride.tsx b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/QueryTimeOverride/QueryTimeOverride.tsx index 2c8b29711e..2fb23c98d8 100644 --- a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/QueryTimeOverride/QueryTimeOverride.tsx +++ b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/QueryTimeOverride/QueryTimeOverride.tsx @@ -2,6 +2,7 @@ import { FC } from "preact/compat"; import Switch from "../../../Main/Switch/Switch"; import { getFromStorage, saveToStorage } from "../../../../utils/storage"; import { useLocalStorageBoolean } from "../../../../hooks/useLocalStorageBoolean"; +import "./style.scss"; const key = "LOGS_OVERRIDE_TIME"; const defaultValue = true; @@ -21,17 +22,25 @@ const QueryTimeOverride: FC = () => { }; return ( -
-
- Query time override -
+
} /> +
+ If the query contains a time filter, it overrides the selected time range. +
); }; export default QueryTimeOverride; + +const OverrideTimeLabel: FC = () => ( +

+ Override time picker with query time filter +

+); diff --git a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/QueryTimeOverride/style.scss b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/QueryTimeOverride/style.scss new file mode 100644 index 0000000000..972ff0c18e --- /dev/null +++ b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/QueryTimeOverride/style.scss @@ -0,0 +1,19 @@ +@use "src/styles/variables" as *; + +.vm-time-override-controller { + background-color: $color-hover-black; + border-radius: $border-radius-medium; + padding: $padding-large; + border: $border-divider; + + .vm-server-configurator__title { + margin: 0; + } + + &__description { + padding-top: $padding-global; + font-size: $font-size-small; + line-height: 1.3; + text-wrap: pretty; + } +} diff --git a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/Timezones/TimezonesPicker.tsx b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/Timezones/TimezonesPicker.tsx index 16992bd8ff..2149cb7513 100644 --- a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/Timezones/TimezonesPicker.tsx +++ b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/Timezones/TimezonesPicker.tsx @@ -1,4 +1,4 @@ -import { FC, forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from "preact/compat"; +import { FC, useEffect, useMemo, useRef, useState } from "preact/compat"; import { getUTCByTimezone } from "../../../../utils/time"; import { ArrowDropDownIcon } from "../../../Main/Icons"; import classNames from "classnames"; @@ -10,7 +10,7 @@ import TimezonesList from "./TimezonesList"; import Popper from "../../../Main/Popper/Popper"; import useDeviceDetect from "../../../../hooks/useDeviceDetect"; -const TimezonesPicker: FC = forwardRef((_props, ref) => { +const TimezonesPicker: FC = () => { const { isMobile } = useDeviceDetect(); const { timezone: stateTimezone } = useTimeState(); const timeDispatch = useTimeDispatch(); @@ -30,7 +30,7 @@ const TimezonesPicker: FC = forwardRef((_props, ref) => { }), [timezone]); const handleSetTimezone = (tz: Timezone) => { - setTimezone(tz.region); + timeDispatch({ type: "SET_TIMEZONE", payload: tz.region }); handleCloseList(); }; @@ -38,12 +38,6 @@ const TimezonesPicker: FC = forwardRef((_props, ref) => { setTimezone(stateTimezone); }, [stateTimezone]); - useImperativeHandle(ref, () => ({ - handleApply: () => { - timeDispatch({ type: "SET_TIMEZONE", payload: timezone }); - } - }), [timezone]); - return (
@@ -77,6 +71,6 @@ const TimezonesPicker: FC = forwardRef((_props, ref) => {
); -}); +}; export default TimezonesPicker; diff --git a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/Timezones/style.scss b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/Timezones/style.scss index e0d6f4e742..774edd634d 100644 --- a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/Timezones/style.scss +++ b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/Timezones/style.scss @@ -16,6 +16,7 @@ } &__title { + flex-grow: 1; display: flex; align-items: center; gap: $padding-small; @@ -34,6 +35,7 @@ background-color: $color-hover-black; padding: calc($padding-small/2); border-radius: $border-radius-small; + font-size: $font-size-small; } &__icon { diff --git a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/style.scss b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/style.scss index 57244eac6d..6c9516b0a6 100644 --- a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/style.scss +++ b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/style.scss @@ -4,9 +4,10 @@ display: flex; flex-direction: column; align-items: center; - gap: $padding-large; + gap: calc($padding-global * 2); width: 600px; - padding-bottom: $padding-medium; + padding-inline: $padding-large; + padding-bottom: calc($padding-large * 2); &_mobile { grid-auto-rows: min-content; @@ -55,17 +56,4 @@ margin-top: $padding-small; } } - - &-footer { - display: flex; - align-items: center; - justify-content: flex-end; - gap: $padding-small; - width: 100%; - } - - &_mobile &-footer { - display: grid; - grid-template-columns: 1fr 1fr; - } } 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 90099f2d89..cb137a445e 100644 --- a/app/vmui/packages/vmui/src/components/Configurators/ThemeControl/ThemeControl.tsx +++ b/app/vmui/packages/vmui/src/components/Configurators/ThemeControl/ThemeControl.tsx @@ -5,8 +5,20 @@ import useDeviceDetect from "../../../hooks/useDeviceDetect"; import classNames from "classnames"; import { FC } from "preact/compat"; import { useAppDispatch, useAppState } from "../../../state/common/StateContext"; +import { DarkIcon, LightIcon, SystemIcon } from "../../Main/Icons"; + +const themeIcons = { + [Theme.system]: , + [Theme.light]: , + [Theme.dark]: , +}; + +const options = Object.values(Theme).map(value => ({ + title: value, + value, + icon: themeIcons[value], +})); -const options = Object.values(Theme).map(value => ({ title: value, value })); const ThemeControl: FC = () => { const { isMobile } = useDeviceDetect(); const dispatch = useAppDispatch(); @@ -25,13 +37,14 @@ const ThemeControl: FC = () => { })} >
- Theme preferences + Theme
( /> ); + +export const SystemIcon = () => ( + + + +); + +export const LightIcon = () => ( + + + + + + + + + + + +); + +export const DarkIcon = () => ( + + + +); diff --git a/app/vmui/packages/vmui/src/components/Main/Switch/Switch.tsx b/app/vmui/packages/vmui/src/components/Main/Switch/Switch.tsx index eb15df28fd..8f3e836b07 100644 --- a/app/vmui/packages/vmui/src/components/Main/Switch/Switch.tsx +++ b/app/vmui/packages/vmui/src/components/Main/Switch/Switch.tsx @@ -4,7 +4,7 @@ import "./style.scss"; interface SwitchProps { value: boolean - color?: "primary" | "secondary" | "error" + color?: "primary" | "secondary" | "error" | "neutral" disabled?: boolean label?: string | ReactNode fullWidth?: boolean diff --git a/app/vmui/packages/vmui/src/components/Main/Switch/style.scss b/app/vmui/packages/vmui/src/components/Main/Switch/style.scss index f18686b452..1773d6a51f 100644 --- a/app/vmui/packages/vmui/src/components/Main/Switch/style.scss +++ b/app/vmui/packages/vmui/src/components/Main/Switch/style.scss @@ -32,6 +32,10 @@ $switch-border-radius: $switch-handle-size + ($switch-padding * 2); background-color: $color-secondary; } + &_neutral_active &-track { + background-color: $color-text; + } + &_primary_active &-track { background-color: $color-primary; } diff --git a/app/vmui/packages/vmui/src/components/Main/Toggle/Toggle.tsx b/app/vmui/packages/vmui/src/components/Main/Toggle/Toggle.tsx index 786641a44a..65b1841cf7 100644 --- a/app/vmui/packages/vmui/src/components/Main/Toggle/Toggle.tsx +++ b/app/vmui/packages/vmui/src/components/Main/Toggle/Toggle.tsx @@ -3,19 +3,19 @@ import classNames from "classnames"; import "./style.scss"; interface ToggleProps { - options: {value: string, title?: string, icon?: ReactNode}[] + options: { value: string, title?: string, icon?: ReactNode }[] value: string onChange: (val: string) => void label?: string + size?: "medium" | "large" } -const Toggle: FC = ({ options, value, label, onChange }) => { +const Toggle: FC = ({ options, value, label, size = "medium", onChange }) => { const activeRef = useRef(null); const [position, setPosition] = useState({ width: "0px", left: "0px", - borderRadius: "0px" }); const createHandlerChange = (value: string) => () => { @@ -27,7 +27,6 @@ const Toggle: FC = ({ options, value, label, onChange }) => { setPosition({ width: "0px", left: "0px", - borderRadius: "0px" }); return; } @@ -36,13 +35,9 @@ const Toggle: FC = ({ options, value, label, onChange }) => { 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)) { @@ -51,11 +46,16 @@ const Toggle: FC = ({ options, value, label, onChange }) => { } - setPosition({ width: `${width}px`, left: `${left}px`, borderRadius }); + setPosition({ width: `${width}px`, left: `${left}px` }); }, [activeRef, value, options]); return ( -
+
{label && (