mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2025-01-05 22:32:20 +01:00
vmui: fix input cursor position reset (#6530)
### Describe Your Changes
This PR addresses the issue where the cursor jumps to the end of the
input fields in the modal settings window after each keystroke.
### Before fix:
![ezgif-7-4c69805cea](https://github.com/VictoriaMetrics/VictoriaMetrics/assets/29711459/2e99e833-09e3-4b44-89aa-fc1bd3c4346d)
### Checklist
The following checks are **mandatory**:
- [x] My change adheres [VictoriaMetrics contributing
guidelines](https://docs.victoriametrics.com/contributing/).
(cherry picked from commit e9b71a2883
)
This commit is contained in:
parent
25f3e700a6
commit
904ec020ed
@ -1,22 +1,17 @@
|
|||||||
import React, { FC, useEffect, useState } from "preact/compat";
|
import React, { FC, useRef } from "preact/compat";
|
||||||
import ServerConfigurator from "./ServerConfigurator/ServerConfigurator";
|
import ServerConfigurator from "./ServerConfigurator/ServerConfigurator";
|
||||||
import { useAppDispatch, useAppState } from "../../../state/common/StateContext";
|
|
||||||
import { ArrowDownIcon, SettingsIcon } from "../../Main/Icons";
|
import { ArrowDownIcon, SettingsIcon } from "../../Main/Icons";
|
||||||
import Button from "../../Main/Button/Button";
|
import Button from "../../Main/Button/Button";
|
||||||
import Modal from "../../Main/Modal/Modal";
|
import Modal from "../../Main/Modal/Modal";
|
||||||
import "./style.scss";
|
import "./style.scss";
|
||||||
import Tooltip from "../../Main/Tooltip/Tooltip";
|
import Tooltip from "../../Main/Tooltip/Tooltip";
|
||||||
import LimitsConfigurator from "./LimitsConfigurator/LimitsConfigurator";
|
import LimitsConfigurator from "./LimitsConfigurator/LimitsConfigurator";
|
||||||
import { Theme } from "../../../types";
|
|
||||||
import { useCustomPanelDispatch, useCustomPanelState } from "../../../state/customPanel/CustomPanelStateContext";
|
|
||||||
import { getAppModeEnable } from "../../../utils/app-mode";
|
import { getAppModeEnable } from "../../../utils/app-mode";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import Timezones from "./Timezones/Timezones";
|
import Timezones from "./Timezones/Timezones";
|
||||||
import { useTimeDispatch, useTimeState } from "../../../state/time/TimeStateContext";
|
|
||||||
import ThemeControl from "../ThemeControl/ThemeControl";
|
import ThemeControl from "../ThemeControl/ThemeControl";
|
||||||
import useDeviceDetect from "../../../hooks/useDeviceDetect";
|
import useDeviceDetect from "../../../hooks/useDeviceDetect";
|
||||||
import useBoolean from "../../../hooks/useBoolean";
|
import useBoolean from "../../../hooks/useBoolean";
|
||||||
import { getTenantIdFromUrl } from "../../../utils/tenants";
|
|
||||||
import { AppType } from "../../../types/appType";
|
import { AppType } from "../../../types/appType";
|
||||||
|
|
||||||
const title = "Settings";
|
const title = "Settings";
|
||||||
@ -24,27 +19,18 @@ const title = "Settings";
|
|||||||
const { REACT_APP_TYPE } = process.env;
|
const { REACT_APP_TYPE } = process.env;
|
||||||
const isLogsApp = REACT_APP_TYPE === AppType.logs;
|
const isLogsApp = REACT_APP_TYPE === AppType.logs;
|
||||||
|
|
||||||
|
export interface ChildComponentHandle {
|
||||||
|
handleApply: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
const GlobalSettings: FC = () => {
|
const GlobalSettings: FC = () => {
|
||||||
const { isMobile } = useDeviceDetect();
|
const { isMobile } = useDeviceDetect();
|
||||||
|
|
||||||
const appModeEnable = getAppModeEnable();
|
const appModeEnable = getAppModeEnable();
|
||||||
const { serverUrl: stateServerUrl, theme } = useAppState();
|
|
||||||
const { timezone: stateTimezone, defaultTimezone } = useTimeState();
|
|
||||||
const { seriesLimits } = useCustomPanelState();
|
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const serverSettingRef = useRef<ChildComponentHandle>(null);
|
||||||
const timeDispatch = useTimeDispatch();
|
const limitsSettingRef = useRef<ChildComponentHandle>(null);
|
||||||
const customPanelDispatch = useCustomPanelDispatch();
|
const timezoneSettingRef = useRef<ChildComponentHandle>(null);
|
||||||
|
|
||||||
const [serverUrl, setServerUrl] = useState(stateServerUrl);
|
|
||||||
const [limits, setLimits] = useState(seriesLimits);
|
|
||||||
const [timezone, setTimezone] = useState(stateTimezone);
|
|
||||||
|
|
||||||
const setDefaultsValues = () => {
|
|
||||||
setServerUrl(stateServerUrl);
|
|
||||||
setLimits(seriesLimits);
|
|
||||||
setTimezone(stateTimezone);
|
|
||||||
};
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
value: open,
|
value: open,
|
||||||
@ -52,68 +38,35 @@ const GlobalSettings: FC = () => {
|
|||||||
setFalse: handleClose,
|
setFalse: handleClose,
|
||||||
} = useBoolean(false);
|
} = useBoolean(false);
|
||||||
|
|
||||||
const handleCloseAndReset = () => {
|
const handleApply = () => {
|
||||||
handleClose();
|
serverSettingRef.current && serverSettingRef.current.handleApply();
|
||||||
setDefaultsValues();
|
limitsSettingRef.current && limitsSettingRef.current.handleApply();
|
||||||
};
|
timezoneSettingRef.current && timezoneSettingRef.current.handleApply();
|
||||||
|
|
||||||
const handleChangeTheme = (value: Theme) => {
|
|
||||||
dispatch({ type: "SET_THEME", payload: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlerApply = () => {
|
|
||||||
const tenantIdFromUrl = getTenantIdFromUrl(serverUrl);
|
|
||||||
if (tenantIdFromUrl !== "") {
|
|
||||||
dispatch({ type: "SET_TENANT_ID", payload: tenantIdFromUrl });
|
|
||||||
}
|
|
||||||
dispatch({ type: "SET_SERVER", payload: serverUrl });
|
|
||||||
timeDispatch({ type: "SET_TIMEZONE", payload: timezone });
|
|
||||||
customPanelDispatch({ type: "SET_SERIES_LIMITS", payload: limits });
|
|
||||||
handleClose();
|
handleClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// the tenant selector can change the serverUrl
|
|
||||||
if (stateServerUrl === serverUrl) return;
|
|
||||||
setServerUrl(stateServerUrl);
|
|
||||||
}, [stateServerUrl]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTimezone(stateTimezone);
|
|
||||||
}, [stateTimezone]);
|
|
||||||
|
|
||||||
const controls = [
|
const controls = [
|
||||||
{
|
{
|
||||||
show: !appModeEnable && !isLogsApp,
|
show: !appModeEnable && !isLogsApp,
|
||||||
component: <ServerConfigurator
|
component: <ServerConfigurator
|
||||||
stateServerUrl={stateServerUrl}
|
ref={serverSettingRef}
|
||||||
serverUrl={serverUrl}
|
onClose={handleClose}
|
||||||
onChange={setServerUrl}
|
|
||||||
onEnter={handlerApply}
|
|
||||||
/>
|
/>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
show: !isLogsApp,
|
show: !isLogsApp,
|
||||||
component: <LimitsConfigurator
|
component: <LimitsConfigurator
|
||||||
limits={limits}
|
ref={limitsSettingRef}
|
||||||
onChange={setLimits}
|
onClose={handleClose}
|
||||||
onEnter={handlerApply}
|
|
||||||
/>
|
/>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
show: true,
|
show: true,
|
||||||
component: <Timezones
|
component: <Timezones ref={timezoneSettingRef}/>
|
||||||
timezoneState={timezone}
|
|
||||||
defaultTimezone={defaultTimezone}
|
|
||||||
onChange={setTimezone}
|
|
||||||
/>
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
show: !appModeEnable,
|
show: !appModeEnable,
|
||||||
component: <ThemeControl
|
component: <ThemeControl/>
|
||||||
theme={theme}
|
|
||||||
onChange={handleChangeTheme}
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
].filter(control => control.show);
|
].filter(control => control.show);
|
||||||
|
|
||||||
@ -146,7 +99,7 @@ const GlobalSettings: FC = () => {
|
|||||||
{open && (
|
{open && (
|
||||||
<Modal
|
<Modal
|
||||||
title={title}
|
title={title}
|
||||||
onClose={handleCloseAndReset}
|
onClose={handleClose}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={classNames({
|
className={classNames({
|
||||||
@ -166,14 +119,14 @@ const GlobalSettings: FC = () => {
|
|||||||
<Button
|
<Button
|
||||||
color="error"
|
color="error"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onClick={handleCloseAndReset}
|
onClick={handleClose}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={handlerApply}
|
onClick={handleApply}
|
||||||
>
|
>
|
||||||
Apply
|
Apply
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { FC, useState } from "preact/compat";
|
import React, { forwardRef, useCallback, useImperativeHandle, useState } from "preact/compat";
|
||||||
import { DisplayType, ErrorTypes, SeriesLimits } from "../../../../types";
|
import { DisplayType, ErrorTypes } from "../../../../types";
|
||||||
import TextField from "../../../Main/TextField/TextField";
|
import TextField from "../../../Main/TextField/TextField";
|
||||||
import Tooltip from "../../../Main/Tooltip/Tooltip";
|
import Tooltip from "../../../Main/Tooltip/Tooltip";
|
||||||
import { InfoIcon, RestartIcon } from "../../../Main/Icons";
|
import { InfoIcon, RestartIcon } from "../../../Main/Icons";
|
||||||
@ -8,11 +8,11 @@ import { DEFAULT_MAX_SERIES } from "../../../../constants/graph";
|
|||||||
import "./style.scss";
|
import "./style.scss";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import useDeviceDetect from "../../../../hooks/useDeviceDetect";
|
import useDeviceDetect from "../../../../hooks/useDeviceDetect";
|
||||||
|
import { ChildComponentHandle } from "../GlobalSettings";
|
||||||
|
import { useCustomPanelDispatch, useCustomPanelState } from "../../../../state/customPanel/CustomPanelStateContext";
|
||||||
|
|
||||||
export interface ServerConfiguratorProps {
|
interface ServerConfiguratorProps {
|
||||||
limits: SeriesLimits
|
onClose: () => void
|
||||||
onChange: (limits: SeriesLimits) => void
|
|
||||||
onEnter: () => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const fields: {label: string, type: DisplayType}[] = [
|
const fields: {label: string, type: DisplayType}[] = [
|
||||||
@ -21,31 +21,38 @@ const fields: {label: string, type: DisplayType}[] = [
|
|||||||
{ label: "Table", type: DisplayType.table }
|
{ label: "Table", type: DisplayType.table }
|
||||||
];
|
];
|
||||||
|
|
||||||
const LimitsConfigurator: FC<ServerConfiguratorProps> = ({ limits, onChange , onEnter }) => {
|
const LimitsConfigurator = forwardRef<ChildComponentHandle, ServerConfiguratorProps>(({ onClose }, ref) => {
|
||||||
const { isMobile } = useDeviceDetect();
|
const { isMobile } = useDeviceDetect();
|
||||||
|
|
||||||
|
const { seriesLimits } = useCustomPanelState();
|
||||||
|
const customPanelDispatch = useCustomPanelDispatch();
|
||||||
|
|
||||||
|
const [limits, setLimits] = useState(seriesLimits);
|
||||||
const [error, setError] = useState({
|
const [error, setError] = useState({
|
||||||
table: "",
|
table: "",
|
||||||
chart: "",
|
chart: "",
|
||||||
code: ""
|
code: ""
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleChange = (val: string, type: DisplayType) => {
|
const handleReset = () => {
|
||||||
|
setLimits(DEFAULT_MAX_SERIES);
|
||||||
|
};
|
||||||
|
|
||||||
|
const createChangeHandler = (type: DisplayType) => (val: string) => {
|
||||||
const value = val || "";
|
const value = val || "";
|
||||||
setError(prev => ({ ...prev, [type]: +value < 0 ? ErrorTypes.positiveNumber : "" }));
|
setError(prev => ({ ...prev, [type]: +value < 0 ? ErrorTypes.positiveNumber : "" }));
|
||||||
onChange({
|
setLimits({
|
||||||
...limits,
|
...limits,
|
||||||
[type]: !value ? Infinity : value
|
[type]: !value ? Infinity : value
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReset = () => {
|
const handleApply = useCallback(() => {
|
||||||
onChange(DEFAULT_MAX_SERIES);
|
customPanelDispatch({ type: "SET_SERIES_LIMITS", payload: limits });
|
||||||
};
|
onClose();
|
||||||
|
}, [limits]);
|
||||||
|
|
||||||
const createChangeHandler = (type: DisplayType) => (val: string) => {
|
useImperativeHandle(ref, () => ({ handleApply }), [handleApply]);
|
||||||
handleChange(val, type);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="vm-limits-configurator">
|
<div className="vm-limits-configurator">
|
||||||
@ -84,7 +91,7 @@ const LimitsConfigurator: FC<ServerConfiguratorProps> = ({ limits, onChange , on
|
|||||||
value={limits[f.type]}
|
value={limits[f.type]}
|
||||||
error={error[f.type]}
|
error={error[f.type]}
|
||||||
onChange={createChangeHandler(f.type)}
|
onChange={createChangeHandler(f.type)}
|
||||||
onEnter={onEnter}
|
onEnter={handleApply}
|
||||||
type="number"
|
type="number"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -92,6 +99,6 @@ const LimitsConfigurator: FC<ServerConfiguratorProps> = ({ limits, onChange , on
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export default LimitsConfigurator;
|
export default LimitsConfigurator;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { FC, useEffect, useState } from "preact/compat";
|
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useState } from "preact/compat";
|
||||||
import { ErrorTypes } from "../../../../types";
|
import { ErrorTypes } from "../../../../types";
|
||||||
import TextField from "../../../Main/TextField/TextField";
|
import TextField from "../../../Main/TextField/TextField";
|
||||||
import { isValidHttpUrl } from "../../../../utils/url";
|
import { isValidHttpUrl } from "../../../../utils/url";
|
||||||
@ -7,12 +7,12 @@ import { StorageIcon } from "../../../Main/Icons";
|
|||||||
import Tooltip from "../../../Main/Tooltip/Tooltip";
|
import Tooltip from "../../../Main/Tooltip/Tooltip";
|
||||||
import { getFromStorage, removeFromStorage, saveToStorage } from "../../../../utils/storage";
|
import { getFromStorage, removeFromStorage, saveToStorage } from "../../../../utils/storage";
|
||||||
import useBoolean from "../../../../hooks/useBoolean";
|
import useBoolean from "../../../../hooks/useBoolean";
|
||||||
|
import { ChildComponentHandle } from "../GlobalSettings";
|
||||||
|
import { useAppDispatch, useAppState } from "../../../../state/common/StateContext";
|
||||||
|
import { getTenantIdFromUrl } from "../../../../utils/tenants";
|
||||||
|
|
||||||
export interface ServerConfiguratorProps {
|
interface ServerConfiguratorProps {
|
||||||
serverUrl: string
|
onClose: () => void;
|
||||||
stateServerUrl: string
|
|
||||||
onChange: (url: string) => void
|
|
||||||
onEnter: () => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const tooltipSave = {
|
const tooltipSave = {
|
||||||
@ -20,24 +20,33 @@ const tooltipSave = {
|
|||||||
disable: "Disable to stop saving the server URL to local storage, reverting to the default URL on page refresh."
|
disable: "Disable to stop saving the server URL to local storage, reverting to the default URL on page refresh."
|
||||||
};
|
};
|
||||||
|
|
||||||
const ServerConfigurator: FC<ServerConfiguratorProps> = ({
|
const ServerConfigurator = forwardRef<ChildComponentHandle, ServerConfiguratorProps>(({ onClose }, ref) => {
|
||||||
serverUrl,
|
const { serverUrl: stateServerUrl } = useAppState();
|
||||||
stateServerUrl,
|
const dispatch = useAppDispatch();
|
||||||
onChange ,
|
|
||||||
onEnter
|
|
||||||
}) => {
|
|
||||||
const {
|
const {
|
||||||
value: enabledStorage,
|
value: enabledStorage,
|
||||||
toggle: handleToggleStorage,
|
toggle: handleToggleStorage,
|
||||||
} = useBoolean(!!getFromStorage("SERVER_URL"));
|
} = useBoolean(!!getFromStorage("SERVER_URL"));
|
||||||
|
|
||||||
|
const [serverUrl, setServerUrl] = useState(stateServerUrl);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
|
|
||||||
const onChangeServer = (val: string) => {
|
const handleChange = (val: string) => {
|
||||||
const value = val || "";
|
const value = val || "";
|
||||||
onChange(value);
|
setServerUrl(value);
|
||||||
setError("");
|
setError("");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleApply = useCallback(() => {
|
||||||
|
const tenantIdFromUrl = getTenantIdFromUrl(serverUrl);
|
||||||
|
if (tenantIdFromUrl !== "") {
|
||||||
|
dispatch({ type: "SET_TENANT_ID", payload: tenantIdFromUrl });
|
||||||
|
}
|
||||||
|
dispatch({ type: "SET_SERVER", payload: serverUrl });
|
||||||
|
onClose();
|
||||||
|
}, [serverUrl]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!stateServerUrl) setError(ErrorTypes.emptyServer);
|
if (!stateServerUrl) setError(ErrorTypes.emptyServer);
|
||||||
if (!isValidHttpUrl(stateServerUrl)) setError(ErrorTypes.validServer);
|
if (!isValidHttpUrl(stateServerUrl)) setError(ErrorTypes.validServer);
|
||||||
@ -57,6 +66,14 @@ const ServerConfigurator: FC<ServerConfiguratorProps> = ({
|
|||||||
}
|
}
|
||||||
}, [serverUrl]);
|
}, [serverUrl]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// the tenant selector can change the serverUrl
|
||||||
|
if (stateServerUrl === serverUrl) return;
|
||||||
|
setServerUrl(stateServerUrl);
|
||||||
|
}, [stateServerUrl]);
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({ handleApply }), [handleApply]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="vm-server-configurator__title">
|
<div className="vm-server-configurator__title">
|
||||||
@ -67,8 +84,8 @@ const ServerConfigurator: FC<ServerConfiguratorProps> = ({
|
|||||||
autofocus
|
autofocus
|
||||||
value={serverUrl}
|
value={serverUrl}
|
||||||
error={error}
|
error={error}
|
||||||
onChange={onChangeServer}
|
onChange={handleChange}
|
||||||
onEnter={onEnter}
|
onEnter={handleApply}
|
||||||
inputmode="url"
|
inputmode="url"
|
||||||
/>
|
/>
|
||||||
<Tooltip title={enabledStorage ? tooltipSave.disable : tooltipSave.enable}>
|
<Tooltip title={enabledStorage ? tooltipSave.disable : tooltipSave.enable}>
|
||||||
@ -83,6 +100,6 @@ const ServerConfigurator: FC<ServerConfiguratorProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export default ServerConfigurator;
|
export default ServerConfigurator;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { FC, useMemo, useRef, useState } from "preact/compat";
|
import React, { FC, forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from "preact/compat";
|
||||||
import { getBrowserTimezone, getTimezoneList, getUTCByTimezone } from "../../../../utils/time";
|
import { getBrowserTimezone, getTimezoneList, getUTCByTimezone } from "../../../../utils/time";
|
||||||
import { ArrowDropDownIcon } from "../../../Main/Icons";
|
import { ArrowDropDownIcon } from "../../../Main/Icons";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
@ -10,12 +10,7 @@ import "./style.scss";
|
|||||||
import useDeviceDetect from "../../../../hooks/useDeviceDetect";
|
import useDeviceDetect from "../../../../hooks/useDeviceDetect";
|
||||||
import useBoolean from "../../../../hooks/useBoolean";
|
import useBoolean from "../../../../hooks/useBoolean";
|
||||||
import WarningTimezone from "./WarningTimezone";
|
import WarningTimezone from "./WarningTimezone";
|
||||||
|
import { useTimeDispatch, useTimeState } from "../../../../state/time/TimeStateContext";
|
||||||
interface TimezonesProps {
|
|
||||||
timezoneState: string;
|
|
||||||
defaultTimezone?: string;
|
|
||||||
onChange: (val: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PinnedTimezone extends Timezone {
|
interface PinnedTimezone extends Timezone {
|
||||||
title: string;
|
title: string;
|
||||||
@ -24,10 +19,14 @@ interface PinnedTimezone extends Timezone {
|
|||||||
|
|
||||||
const browserTimezone = getBrowserTimezone();
|
const browserTimezone = getBrowserTimezone();
|
||||||
|
|
||||||
const Timezones: FC<TimezonesProps> = ({ timezoneState, defaultTimezone, onChange }) => {
|
const Timezones: FC = forwardRef((props, ref) => {
|
||||||
const { isMobile } = useDeviceDetect();
|
const { isMobile } = useDeviceDetect();
|
||||||
const timezones = getTimezoneList();
|
const timezones = getTimezoneList();
|
||||||
|
|
||||||
|
const { timezone: stateTimezone, defaultTimezone } = useTimeState();
|
||||||
|
const timeDispatch = useTimeDispatch();
|
||||||
|
|
||||||
|
const [timezone, setTimezone] = useState(stateTimezone);
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
const targetRef = useRef<HTMLDivElement>(null);
|
const targetRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@ -68,16 +67,16 @@ const Timezones: FC<TimezonesProps> = ({ timezoneState, defaultTimezone, onChang
|
|||||||
const timezonesGroups = useMemo(() => Object.keys(searchTimezones), [searchTimezones]);
|
const timezonesGroups = useMemo(() => Object.keys(searchTimezones), [searchTimezones]);
|
||||||
|
|
||||||
const activeTimezone = useMemo(() => ({
|
const activeTimezone = useMemo(() => ({
|
||||||
region: timezoneState,
|
region: timezone,
|
||||||
utc: getUTCByTimezone(timezoneState)
|
utc: getUTCByTimezone(timezone)
|
||||||
}), [timezoneState]);
|
}), [timezone]);
|
||||||
|
|
||||||
const handleChangeSearch = (val: string) => {
|
const handleChangeSearch = (val: string) => {
|
||||||
setSearch(val);
|
setSearch(val);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSetTimezone = (val: Timezone) => {
|
const handleSetTimezone = (val: Timezone) => {
|
||||||
onChange(val.region);
|
setTimezone(val.region);
|
||||||
setSearch("");
|
setSearch("");
|
||||||
handleCloseList();
|
handleCloseList();
|
||||||
};
|
};
|
||||||
@ -86,6 +85,16 @@ const Timezones: FC<TimezonesProps> = ({ timezoneState, defaultTimezone, onChang
|
|||||||
handleSetTimezone(val);
|
handleSetTimezone(val);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTimezone(stateTimezone);
|
||||||
|
}, [stateTimezone]);
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
handleApply: () => {
|
||||||
|
timeDispatch({ type: "SET_TIMEZONE", payload: timezone });
|
||||||
|
}
|
||||||
|
}), [timezone]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="vm-timezones">
|
<div className="vm-timezones">
|
||||||
<div className="vm-server-configurator__title">
|
<div className="vm-server-configurator__title">
|
||||||
@ -169,6 +178,6 @@ const Timezones: FC<TimezonesProps> = ({ timezoneState, defaultTimezone, onChang
|
|||||||
</Popper>
|
</Popper>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export default Timezones;
|
export default Timezones;
|
||||||
|
@ -5,18 +5,17 @@ import Toggle from "../../Main/Toggle/Toggle";
|
|||||||
import useDeviceDetect from "../../../hooks/useDeviceDetect";
|
import useDeviceDetect from "../../../hooks/useDeviceDetect";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { FC } from "preact/compat";
|
import { FC } from "preact/compat";
|
||||||
|
import { useAppDispatch, useAppState } from "../../../state/common/StateContext";
|
||||||
interface ThemeControlProps {
|
|
||||||
theme: Theme;
|
|
||||||
onChange: (val: Theme) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = Object.values(Theme).map(value => ({ title: value, value }));
|
const options = Object.values(Theme).map(value => ({ title: value, value }));
|
||||||
const ThemeControl: FC<ThemeControlProps> = ({ theme, onChange }) => {
|
const ThemeControl: FC = () => {
|
||||||
const { isMobile } = useDeviceDetect();
|
const { isMobile } = useDeviceDetect();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const { theme } = useAppState();
|
||||||
|
|
||||||
const handleClickItem = (value: string) => {
|
const handleClickItem = (value: string) => {
|
||||||
onChange(value as Theme);
|
dispatch({ type: "SET_THEME", payload: value as Theme });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -32,7 +32,7 @@ See also [LTS releases](https://docs.victoriametrics.com/lts-releases/).
|
|||||||
* FEATURE: [dashboards](https://grafana.com/orgs/victoriametrics): add [Grafana dashboard](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/dashboards/vmauth.json) and [alerting rules](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/deployment/docker/alerts-vmauth.yml) for [vmauth](https://docs.victoriametrics.com/vmauth/) dashboard. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4313) for details.
|
* FEATURE: [dashboards](https://grafana.com/orgs/victoriametrics): add [Grafana dashboard](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/dashboards/vmauth.json) and [alerting rules](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/deployment/docker/alerts-vmauth.yml) for [vmauth](https://docs.victoriametrics.com/vmauth/) dashboard. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4313) for details.
|
||||||
|
|
||||||
* BUGFIX: [docker-compose](https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/deployment/docker#docker-compose-environment-for-victoriametrics): fix incorrect link to vmui from [VictoriaMetrics plugin in Grafana](https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/deployment/docker#grafana).
|
* BUGFIX: [docker-compose](https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/deployment/docker#docker-compose-environment-for-victoriametrics): fix incorrect link to vmui from [VictoriaMetrics plugin in Grafana](https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/deployment/docker#grafana).
|
||||||
|
* BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): fix input cursor position reset in modal settings. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/6530).
|
||||||
|
|
||||||
## [v1.102.0-rc2](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.102.0-rc2)
|
## [v1.102.0-rc2](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.102.0-rc2)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user