From 6aee9916332f1f10a88549600955b475b5158cdf Mon Sep 17 00:00:00 2001 From: Yunus M Date: Wed, 27 Nov 2024 18:34:23 +0530 Subject: [PATCH] =?UTF-8?q?feat:=20handle=20keyboard=20navigations=20for?= =?UTF-8?q?=20column=20selection=20in=20logs=20explor=E2=80=A6=20(#6548)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: handle keyboard navigations for column selection in logs explorer options view * chore: remove console log --- .../LogsFormatOptionsMenu.styles.scss | 121 ++++++++- .../LogsFormatOptionsMenu.tsx | 236 +++++++++++++----- 2 files changed, 286 insertions(+), 71 deletions(-) diff --git a/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.styles.scss b/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.styles.scss index 070d440781..72ceaef26a 100644 --- a/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.styles.scss +++ b/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.styles.scss @@ -3,7 +3,7 @@ position: absolute; right: -2px; margin: 6px 0; - width: 160px; + width: 240px; border-radius: 4px; @@ -24,7 +24,7 @@ .back-btn { display: flex; align-items: center; - gap: 6px; + gap: 4px; padding: 12px; border: none !important; box-shadow: none !important; @@ -32,14 +32,16 @@ .icon { flex-shrink: 0; } + .text { - color: var(--bg-vanilla-400); + color: var(--bg-slate-50); font-family: Inter; - font-size: 13px; + font-size: 11px; font-style: normal; - font-weight: 400; + font-weight: 500; line-height: 20px; /* 142.857% */ letter-spacing: 0.14px; + text-transform: uppercase; } } @@ -252,6 +254,75 @@ } } + .add-new-column-container { + display: flex; + flex-direction: column; + + .add-new-column-header { + display: flex; + flex-direction: column; + padding: 8px; + gap: 8px; + + .back-icon { + cursor: pointer; + } + + .title { + display: flex; + gap: 4px; + align-items: center; + color: var(--bg-slate-50); + text-transform: uppercase; + font-size: 11px; + font-weight: 500; + line-height: 18px; + letter-spacing: 0.88px; + } + } + + .add-new-column-content { + display: flex; + flex-direction: column; + + padding-bottom: 16px; + + min-height: 240px; + max-height: 400px; + + .loading-container { + padding: 8px; + } + + .column-format-new-options { + overflow-y: auto; + overflow-x: hidden; + + .column-name { + padding: 4px 8px; + border-radius: 1px; + color: var(--bg-vanilla-400, #c0c1c3); + font-family: Inter; + font-size: 13px; + font-style: normal; + font-weight: 400; + line-height: 20px; /* 142.857% */ + letter-spacing: -0.07px; + + &.selected { + background-color: var(--bg-ink-200); + cursor: pointer; + } + } + + &::-webkit-scrollbar { + height: 1rem; + width: 0.2rem; + } + } + } + } + .selected-item-content-container { .add-new-column-header { padding: 8px; @@ -314,6 +385,22 @@ cursor: pointer; + &.default-column { + color: var(--bg-vanilla-400, #c0c1c3); + cursor: not-allowed; + } + + &.no-columns-selected { + color: var(--bg-slate-100); + font-size: 12px; + cursor: not-allowed; + } + + &.add-new-column-btn { + color: var(--bg-vanilla-400, #c0c1c3); + cursor: pointer; + } + .name { flex: 1; overflow: hidden; @@ -428,6 +515,30 @@ } } + .add-new-column-container { + .add-new-column-header { + .title { + color: var(--bg-ink-100); + } + } + + .add-new-column-content { + .column-format-new-options { + .column-name { + color: var(--bg-ink-400); + + &.selected { + background-color: var(--bg-vanilla-400); + } + } + } + + .loading-container { + color: var(--bg-ink-400); + } + } + } + .font-size-container { .title { color: var(--bg-ink-100); diff --git a/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.tsx b/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.tsx index 527c77c6af..beec3b55a9 100644 --- a/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.tsx +++ b/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.tsx @@ -3,13 +3,13 @@ /* eslint-disable jsx-a11y/click-events-have-key-events */ import './LogsFormatOptionsMenu.styles.scss'; -import { Button, Divider, Input, InputNumber, Tooltip, Typography } from 'antd'; +import { Button, Input, InputNumber, Tooltip, Typography } from 'antd'; import cx from 'classnames'; import { LogViewMode } from 'container/LogsTable'; import { FontSize, OptionsMenuConfig } from 'container/OptionsMenu/types'; import useDebouncedFn from 'hooks/useDebouncedFunction'; import { Check, ChevronLeft, ChevronRight, Minus, Plus, X } from 'lucide-react'; -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; interface LogsFormatOptionsMenuProps { title: string; @@ -35,7 +35,13 @@ export default function LogsFormatOptionsMenu({ false, ); - const [addNewColumn, setAddNewColumn] = useState(false); + const [showAddNewColumnContainer, setShowAddNewColumnContainer] = useState( + false, + ); + + const [selectedValue, setSelectedValue] = useState(null); + const listRef = useRef(null); + const initialMouseEnterRef = useRef(false); const onChange = useCallback( (key: LogViewMode) => { @@ -49,7 +55,7 @@ export default function LogsFormatOptionsMenu({ const handleMenuItemClick = (key: LogViewMode): void => { setSelectedItem(key); onChange(key); - setAddNewColumn(false); + setShowAddNewColumnContainer(false); }; const incrementMaxLinesPerRow = (): void => { @@ -75,7 +81,7 @@ export default function LogsFormatOptionsMenu({ }, 300); const handleToggleAddNewColumn = (): void => { - setAddNewColumn(!addNewColumn); + setShowAddNewColumnContainer(!showAddNewColumnContainer); }; const handleLinesPerRowChange = (maxLinesPerRow: number | null): void => { @@ -100,9 +106,92 @@ export default function LogsFormatOptionsMenu({ } }, [fontSizeValue]); + const handleKeyDown = (e: KeyboardEvent): void => { + if (!selectedValue) return; + + const optionsData = addColumn?.options || []; + + const currentIndex = optionsData.findIndex( + (item) => item?.value === selectedValue, + ); + + const currentItem = optionsData[currentIndex]; + + const itemLength = optionsData.length; + + switch (e.key) { + case 'ArrowUp': { + const newValue = optionsData[Math.max(0, currentIndex - 1)]?.value; + + setSelectedValue(newValue as string | null); + e.preventDefault(); + break; + } + case 'ArrowDown': { + const newValue = + optionsData[Math.min(itemLength - 1, currentIndex + 1)]?.value; + + setSelectedValue(newValue as string | null); + e.preventDefault(); + break; + } + case 'Enter': + e.preventDefault(); + if (addColumn && addColumn?.onSelect) { + addColumn?.onSelect(selectedValue, { + label: currentItem.label, + disabled: false, + }); + + if (currentIndex === itemLength - 1) { + setSelectedValue(null); + break; + } else { + const nextIndex = currentIndex + 1; + + const nextValue = optionsData[nextIndex]?.value || null; + + setSelectedValue(nextValue as string | null); + } + } + + break; + default: + break; + } + }; + + useEffect(() => { + // Scroll the selected item into view + const listNode = listRef.current; + if (listNode && selectedValue) { + const optionsData = addColumn?.options || []; + const currentIndex = optionsData.findIndex( + (item) => item?.value === selectedValue, + ); + const itemNode = listNode.children[currentIndex] as HTMLElement; + if (itemNode) { + itemNode.scrollIntoView({ + behavior: 'smooth', + block: 'nearest', + }); + } + } + }, [selectedValue]); + + useEffect(() => { + window.addEventListener('keydown', handleKeyDown); + return (): void => { + window.removeEventListener('keydown', handleKeyDown); + }; + }, [selectedValue]); + return (
{ // this is to restrict click events to propogate to parent event.stopPropagation(); @@ -158,8 +247,73 @@ export default function LogsFormatOptionsMenu({
- ) : ( - <> + ) : null} + + {showAddNewColumnContainer && ( +
+
+
+
+ +
+ Add New Column +
+ + +
+ +
+ {addColumn?.isFetching && ( +
Loading ...
+ )} + +
+ {addColumn?.options?.map(({ label, value }) => ( +
{ + if (!initialMouseEnterRef.current) { + setSelectedValue(value as string | null); + } + + initialMouseEnterRef.current = true; + }} + onClick={(eve): void => { + eve.stopPropagation(); + + if (addColumn && addColumn?.onSelect) { + addColumn?.onSelect(value, { label, disabled: false }); + } + + setSelectedValue(null); + }} + > +
+ + {label} + +
+
+ ))} +
+
+
+ )} + + {!isFontSizeOptionsOpen && !showAddNewColumnContainer && ( +
Font Size
)}
);