From c896bf340d89572905ef9dc3445a1067922de181 Mon Sep 17 00:00:00 2001 From: Yury Molodov Date: Fri, 27 Sep 2024 11:52:01 +0200 Subject: [PATCH] vmui: add functionality to preserve selected columns (#7037) ### Describe Your Changes 1) Changed table settings from a popup to a modal window to simplify future functionality additions. 2) Added functionality to save selected columns when data is modified or the page is reloaded. See #7016.
Example screenshots demo-1
### Checklist The following checks are **mandatory**: - [x] My change adheres [VictoriaMetrics contributing guidelines](https://docs.victoriametrics.com/contributing/). --------- Co-authored-by: Roman Khavronenko --- .../src/components/Main/TextField/style.scss | 2 +- .../Table/TableSettings/TableSettings.tsx | 154 ++++++++++-------- .../components/Table/TableSettings/style.scss | 126 ++++++++------ .../CustomPanel/CustomPanelTabs/TableTab.tsx | 2 +- .../ExploreLogsBody/ExploreLogsBody.tsx | 2 +- .../QueryAnalyzerView/QueryAnalyzerView.tsx | 2 +- app/vmui/packages/vmui/src/utils/storage.ts | 1 + docs/VictoriaLogs/CHANGELOG.md | 2 + docs/changelog/CHANGELOG.md | 1 + 9 files changed, 173 insertions(+), 119 deletions(-) diff --git a/app/vmui/packages/vmui/src/components/Main/TextField/style.scss b/app/vmui/packages/vmui/src/components/Main/TextField/style.scss index 24dfa80e8..823f490a9 100644 --- a/app/vmui/packages/vmui/src/components/Main/TextField/style.scss +++ b/app/vmui/packages/vmui/src/components/Main/TextField/style.scss @@ -149,7 +149,7 @@ max-width: 15px; top: 0; left: $padding-small; - height: 40px; + height: 36px; position: absolute; color: $color-text-secondary; } diff --git a/app/vmui/packages/vmui/src/components/Table/TableSettings/TableSettings.tsx b/app/vmui/packages/vmui/src/components/Table/TableSettings/TableSettings.tsx index a5eb0219f..a59892a55 100644 --- a/app/vmui/packages/vmui/src/components/Table/TableSettings/TableSettings.tsx +++ b/app/vmui/packages/vmui/src/components/Table/TableSettings/TableSettings.tsx @@ -1,23 +1,23 @@ import React, { FC, useEffect, useRef, useMemo } from "preact/compat"; import Button from "../../Main/Button/Button"; import { SearchIcon, SettingsIcon } from "../../Main/Icons"; -import Popper from "../../Main/Popper/Popper"; import "./style.scss"; import Checkbox from "../../Main/Checkbox/Checkbox"; import Tooltip from "../../Main/Tooltip/Tooltip"; import Switch from "../../Main/Switch/Switch"; import { arrayEquals } from "../../../utils/array"; import classNames from "classnames"; -import useDeviceDetect from "../../../hooks/useDeviceDetect"; import useBoolean from "../../../hooks/useBoolean"; import TextField from "../../Main/TextField/TextField"; import { KeyboardEvent, useState } from "react"; +import Modal from "../../Main/Modal/Modal"; +import { getFromStorage, removeFromStorage, saveToStorage } from "../../../utils/storage"; const title = "Table settings"; interface TableSettingsProps { columns: string[]; - defaultColumns?: string[]; + selectedColumns?: string[]; tableCompact: boolean; toggleTableCompact: () => void; onChangeColumns: (arr: string[]) => void @@ -25,13 +25,11 @@ interface TableSettingsProps { const TableSettings: FC = ({ columns, - defaultColumns = [], + selectedColumns = [], tableCompact, onChangeColumns, toggleTableCompact }) => { - const { isMobile } = useDeviceDetect(); - const buttonRef = useRef(null); const { @@ -41,31 +39,34 @@ const TableSettings: FC = ({ } = useBoolean(false); const { - value: showSearch, - toggle: toggleShowSearch, - } = useBoolean(false); + value: saveColumns, + toggle: toggleSaveColumns, + } = useBoolean(Boolean(getFromStorage("TABLE_COLUMNS"))); const [searchColumn, setSearchColumn] = useState(""); const [indexFocusItem, setIndexFocusItem] = useState(-1); + const customColumns = useMemo(() => { + return selectedColumns.filter(col => !columns.includes(col)); + }, [columns, selectedColumns]); + const filteredColumns = useMemo(() => { - if (!searchColumn) return columns; - return columns.filter(col => col.includes(searchColumn)); - }, [columns, searchColumn]); + const allColumns = customColumns.concat(columns); + if (!searchColumn) return allColumns; + return allColumns.filter(col => col.includes(searchColumn)); + }, [columns, customColumns, searchColumn]); const isAllChecked = useMemo(() => { - return filteredColumns.every(col => defaultColumns.includes(col)); - }, [defaultColumns, filteredColumns]); - - const disabledButton = useMemo(() => !columns.length, [columns]); + return filteredColumns.every(col => selectedColumns.includes(col)); + }, [selectedColumns, filteredColumns]); const handleChange = (key: string) => { - onChangeColumns(defaultColumns.includes(key) ? defaultColumns.filter(col => col !== key) : [...defaultColumns, key]); + onChangeColumns(selectedColumns.includes(key) ? selectedColumns.filter(col => col !== key) : [...selectedColumns, key]); }; const toggleAllColumns = () => { if (isAllChecked) { - onChangeColumns(defaultColumns.filter(col => !filteredColumns.includes(col))); + onChangeColumns(selectedColumns.filter(col => !filteredColumns.includes(col))); } else { onChangeColumns(filteredColumns); } @@ -94,10 +95,24 @@ const TableSettings: FC = ({ }; useEffect(() => { - if (arrayEquals(columns, defaultColumns)) return; + if (arrayEquals(columns, selectedColumns) || saveColumns) return; onChangeColumns(columns); }, [columns]); + useEffect(() => { + if (!saveColumns) { + removeFromStorage(["TABLE_COLUMNS"]); + } else if (selectedColumns.length) { + saveToStorage("TABLE_COLUMNS", selectedColumns.join(",")); + } + }, [saveColumns, selectedColumns]); + + useEffect(() => { + const saveColumns = getFromStorage("TABLE_COLUMNS") as string; + if (!saveColumns) return; + onChangeColumns(saveColumns.split(",")); + }, []); + return (
@@ -106,48 +121,24 @@ const TableSettings: FC = ({ variant="text" startIcon={} onClick={toggleOpenSettings} - disabled={disabledButton} ariaLabel={title} />
- -
-
- -
-
-
-
-

Display columns

- -
- {showSearch && ( +
+
+ Customize columns +
+
+
} value={searchColumn} onChange={setSearchColumn} @@ -155,13 +146,10 @@ const TableSettings: FC = ({ onKeyDown={handleKeyDown} type="search" /> - )} - {!filteredColumns.length && ( -

No columns found

- )} -
+
+
{!!filteredColumns.length && ( -
+
= ({ />
)} -
-
+ {!filteredColumns.length && ( +
+

+ No columns found. +

+
+ )} {filteredColumns.map((col, i) => (
= ({
))}
+
+ +

+ This label indicates that when the checkbox is activated, + the current column configurations will not be reset. +

+
-
- +
+
+ Table view +
+
+ +
+
+ )}
); }; diff --git a/app/vmui/packages/vmui/src/components/Table/TableSettings/style.scss b/app/vmui/packages/vmui/src/components/Table/TableSettings/style.scss index 2c0049cda..7866edf99 100644 --- a/app/vmui/packages/vmui/src/components/Table/TableSettings/style.scss +++ b/app/vmui/packages/vmui/src/components/Table/TableSettings/style.scss @@ -1,66 +1,98 @@ @use "src/styles/variables" as *; -.vm-table-settings-popper { - display: grid; - min-width: 250px; - - &_mobile &-list { - gap: $padding-global; - - &:first-child { - padding-top: 0; - } - } - - &-list { - display: grid; - gap: 12px; - padding: $padding-global; - border-bottom: $border-divider; - max-width: 250px; - - &_first { - padding-top: 0; +.vm-table-settings { + &-modal { + .vm-modal-content-body { + padding: 0; } - &-header { - display: grid; - align-items: center; - justify-content: space-between; - grid-template-columns: 1fr auto; - gap: $padding-small; - min-height: 25px; + &-section { + padding-block: $padding-global; + border-top: $border-divider; + + &:first-child { + padding-top: 0; + border-top: none; + } &__title { + padding-inline: $padding-global; + font-size: $font-size; font-weight: bold; + margin-bottom: $padding-global; } } &-columns { - max-height: 350px; - overflow: auto; - } - - &__item { - padding: calc($padding-global/2) $padding-global; - font-size: $font-size; - - &:hover, - &_focus { - background-color: $color-hover-black; + &__search { + padding-inline: $padding-global; } - &_check_all { - padding: calc($padding-global/2) $padding-global; - margin: 0 (-$padding-global); + &-list { + display: flex; + flex-direction: column; + max-height: 250px; + min-height: 250px; + overflow: auto; + margin-bottom: $padding-global; + + &__item { + width: 100%; + font-size: $font-size; + border-radius: $border-radius-small; + + &>div { + padding: $padding-small $padding-global; + } + + &_all { + font-weight: bold; + } + + &:hover, + &_focus { + background-color: $color-hover-black; + } + + &_custom { + .vm-checkbox__label:after { + width: 100%; + content: "(custom column, will be removed if unchecked)"; + padding: 0 $padding-small; + text-align: right; + font-style: italic; + color: $color-text-secondary; + } + } + } + } + + &-no-found { + display: flex; + flex-direction: column; + min-width: 100%; + min-height: 250px; + align-items: center; + justify-content: center; + gap: $padding-global; + + &__info { + text-align: center; + font-style: italic; + color: $color-text-secondary; + } } } - &__no-found { - text-align: center; - font-style: italic; - color: $color-text-secondary; - margin-bottom: $padding-small; + &-preserve { + padding: $padding-global; + + &__info { + padding-top: $padding-small; + font-size: $font-size-small; + color: $color-text-secondary; + line-height: 130%; + } } } } diff --git a/app/vmui/packages/vmui/src/pages/CustomPanel/CustomPanelTabs/TableTab.tsx b/app/vmui/packages/vmui/src/pages/CustomPanel/CustomPanelTabs/TableTab.tsx index afa42c1b1..d8374c52b 100644 --- a/app/vmui/packages/vmui/src/pages/CustomPanel/CustomPanelTabs/TableTab.tsx +++ b/app/vmui/packages/vmui/src/pages/CustomPanel/CustomPanelTabs/TableTab.tsx @@ -26,7 +26,7 @@ const TableTab: FC = ({ liveData, controlsRef }) => { const controls = ( = ({ data }) => {
= ({ data, period }) => { {displayType === "table" && (