feat: updated highlight status when using the new filter bar (#42623)

## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

The experience is now based on the keyboard
- Selecting a filter can be done by pressing left or backspace on the
keyboard
- Pressing enter allows you to select and edit that filter
- Pressing backspace again on a filters value deletes it 
- Left or right arrows does selection, enter to edit

## Demo
 

![filters_new_keyboard](https://github.com/user-attachments/assets/3bc6f348-5a03-448f-93b8-a48ac3068c52)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

* **New Features**
* Filter conditions now display visual highlighting with a distinct ring
outline when navigated via keyboard
* Enhanced keyboard navigation behavior for more intuitive filter
management
  * Filter condition highlights can be cleared using the Escape key
* Improved navigation flow when moving through filter groups and
conditions

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Ali Waseem authored and GitHub committed 2026-02-10 08:48:07 -07:00
1 parent 4decdd3680
commit cc7eab9dcf
10 files changed
+246 -166

No files matched your search

@@ -2,9 +2,15 @@
import React, { createContext, useCallback, useContext, useEffect, useRef } from 'react'
import { ActiveInput, useFilterBarState, useOptionsCache } from './hooks'
import { useFilterBarState, useOptionsCache } from './hooks'
import { MenuItem } from './menuItems'
import { FilterBarAction, FilterGroup, FilterOptionObject, FilterProperty } from './types'
import {
ActiveInputState,
FilterBarAction,
FilterGroup,
FilterOptionObject,
FilterProperty,
} from './types'
import { useCommandHandling } from './useCommandHandling'
import { useKeyboardNavigation } from './useKeyboardNavigation'
import {
@@ -20,15 +26,16 @@ export type FilterBarContextValue = {
// Core state
filters: FilterGroup
filterProperties: FilterProperty[]
activeInput: ActiveInput
activeInput: ActiveInputState
freeformText: string
isLoading: boolean
error: string | null
highlightedConditionPath: number[] | null
// Handlers
onFilterChange: (filters: FilterGroup) => void
onFreeformTextChange: (text: string) => void
setActiveInput: (input: ActiveInput) => void
setActiveInput: (input: ActiveInputState) => void
handleInputChange: (path: number[], value: string) => void
handleOperatorChange: (path: number[], value: string) => void
handleRemoveCondition: (path: number[]) => void
@@ -110,6 +117,8 @@ export function FilterBarRoot({
setActiveInput,
newPathRef,
setIsCommandMenuVisible,
highlightedConditionPath,
setHighlightedConditionPath,
} = useFilterBarState()
const { loadingOptions, propertyOptionsCache, loadPropertyOptions, optionsError } =
@@ -163,6 +172,8 @@ export function FilterBarRoot({
setActiveInput,
activeFilters: filters,
onFilterChange,
highlightedConditionPath,
setHighlightedConditionPath,
})
const handleInputFocus = useCallback(
@@ -229,14 +240,20 @@ export function FilterBarRoot({
}
setIsCommandMenuVisible(false)
setActiveInput(null)
// Clear highlight when clicking outside
setHighlightedConditionPath(null)
}, 0)
}, [setIsCommandMenuVisible, setActiveInput, hideTimeoutRef])
}, [setIsCommandMenuVisible, setActiveInput, hideTimeoutRef, setHighlightedConditionPath])
const handleGroupFreeformChange = useCallback(
(_path: number[], value: string) => {
// Clear highlight when user types
if (highlightedConditionPath) {
setHighlightedConditionPath(null)
}
onFreeformTextChange(value)
},
[onFreeformTextChange]
[onFreeformTextChange, highlightedConditionPath, setHighlightedConditionPath]
)
const handleLabelClick = useCallback(
@@ -282,6 +299,7 @@ export function FilterBarRoot({
freeformText,
isLoading: loading,
error,
highlightedConditionPath,
// Handlers
onFilterChange,
@@ -22,6 +22,7 @@ export type FilterConditionProps = {
path: number[]
isActive: boolean
isOperatorActive: boolean
isHighlighted: boolean
}
export function FilterCondition({
@@ -29,6 +30,7 @@ export function FilterCondition({
path,
isActive,
isOperatorActive,
isHighlighted,
}: FilterConditionProps) {
const {
filters: rootFilters,
@@ -210,7 +212,8 @@ export function FilterCondition({
ref={wrapperRef}
className={cn(
'flex items-stretch px-0 bg-muted group shrink-0',
variant === 'pill' ? 'rounded border' : 'border-r'
variant === 'pill' ? 'rounded border' : 'border-r',
isHighlighted && 'ring-2 ring-primary'
)}
>
<span
@@ -31,6 +31,7 @@ export function FilterGroup({ group, path }: FilterGroupProps) {
supportsOperators,
actions,
variant,
highlightedConditionPath,
handleInputBlur,
handleGroupFreeformFocus,
handleGroupFreeformChange,
@@ -83,6 +84,11 @@ export function FilterGroup({ group, path }: FilterGroupProps) {
return activeInput.type === 'operator' && pathsEqual(conditionPath, activeInput.path)
}
const isConditionHighlighted = (conditionPath: number[]) => {
if (!highlightedConditionPath) return false
return pathsEqual(conditionPath, highlightedConditionPath)
}
const items = useMemo(
() =>
buildPropertyItems({
@@ -106,7 +112,8 @@ export function FilterGroup({ group, path }: FilterGroupProps) {
(index) => {
if (items[index]) handleSelectMenuItem(items[index])
},
handleKeyDown
handleKeyDown,
{ skipEnterWhenFilterHighlighted: highlightedConditionPath !== null }
)
useEffect(() => {
@@ -156,12 +163,15 @@ export function FilterGroup({ group, path }: FilterGroupProps) {
path={currentPath}
isActive={isConditionActive(currentPath)}
isOperatorActive={isOperatorActive(currentPath)}
isHighlighted={isConditionHighlighted(currentPath)}
/>
)}
</React.Fragment>
)
})}
<Popover_Shadcn_ open={isActive && !isLoading && items.length > 0}>
<Popover_Shadcn_
open={isActive && !isLoading && items.length > 0 && !highlightedConditionPath}
>
<PopoverAnchor_Shadcn_ asChild>
{isRootGroup ? (
<Input_Shadcn_
+31 -11
View File
@@ -2,22 +2,29 @@
import { useCallback, useEffect, useRef, useState } from 'react'
import { AsyncOptionsFunction, FilterOptionObject, FilterProperty } from './types'
import {
ActiveInputState,
AsyncOptionsFunction,
ConditionPath,
FilterOptionObject,
FilterProperty,
} from './types'
import { isAsyncOptionsFunction } from './utils'
export type ActiveInput =
| { type: 'value'; path: number[] }
| { type: 'operator'; path: number[] }
| { type: 'group'; path: number[] }
| null
export type HighlightNavigationOptions = {
skipEnterWhenFilterHighlighted?: boolean
}
export function useFilterBarState() {
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const [isCommandMenuVisible, setIsCommandMenuVisible] = useState(false)
const hideTimeoutRef = useRef<NodeJS.Timeout | null>(null)
const [activeInput, setActiveInput] = useState<ActiveInput>(null)
const newPathRef = useRef<number[]>([])
const [activeInput, setActiveInput] = useState<ActiveInputState>(null)
const newPathRef = useRef<ConditionPath>([])
const [highlightedConditionPath, setHighlightedConditionPath] = useState<ConditionPath | null>(
null
)
return {
isLoading,
@@ -30,6 +37,8 @@ export function useFilterBarState() {
activeInput,
setActiveInput,
newPathRef,
highlightedConditionPath,
setHighlightedConditionPath,
}
}
@@ -122,7 +131,8 @@ export function useDeferredBlur(wrapperRef: React.RefObject<HTMLElement>, onBlur
export function useHighlightNavigation(
itemsLength: number,
onEnter: (index: number) => void,
fallbackKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void
fallbackKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void,
options?: HighlightNavigationOptions
) {
const [highlightedIndex, setHighlightedIndex] = useState(0)
@@ -145,7 +155,11 @@ export function useHighlightNavigation(
return
}
if (e.key === 'Enter' || e.key === 'Tab') {
// Only select dropdown item if there are items available
// Edge case: when a filter is highlighted, skip dropdown selection and let fallback handle it
if (options?.skipEnterWhenFilterHighlighted) {
if (fallbackKeyDown) fallbackKeyDown(e)
return
}
if (itemsLength > 0) {
e.preventDefault()
onEnter(highlightedIndex)
@@ -160,7 +174,13 @@ export function useHighlightNavigation(
}
if (fallbackKeyDown) fallbackKeyDown(e)
},
[itemsLength, highlightedIndex, onEnter, fallbackKeyDown]
[
itemsLength,
highlightedIndex,
onEnter,
fallbackKeyDown,
options?.skipEnterWhenFilterHighlighted,
]
)
const reset = useCallback(() => setHighlightedIndex(0), [])
@@ -1,5 +1,4 @@
import { ActiveInput } from './hooks'
import { FilterBarAction, FilterGroup, FilterProperty } from './types'
import { ActiveInputState, FilterBarAction, FilterGroup, FilterProperty } from './types'
import {
findConditionByPath,
isCustomOptionObject,
@@ -19,7 +18,7 @@ export type MenuItem = {
}
export function buildOperatorItems(
activeInput: Extract<ActiveInput, { type: 'operator' }> | null,
activeInput: Extract<ActiveInputState, { type: 'operator' }> | null,
activeFilters: FilterGroup,
filterProperties: FilterProperty[],
hasTypedSinceFocus: boolean = true
@@ -89,7 +88,7 @@ export function buildPropertyItems(params: {
}
export function buildValueItems(
activeInput: Extract<ActiveInput, { type: 'value' }> | null,
activeInput: Extract<ActiveInputState, { type: 'value' }> | null,
activeFilters: FilterGroup,
filterProperties: FilterProperty[],
propertyOptionsCache: Record<string, { options: any[]; searchValue: string }>,
+23 -2
View File
@@ -51,13 +51,15 @@ export function isGroup(condition: FilterCondition | FilterGroup): condition is
return 'logicalOperator' in condition
}
export type ConditionPath = number[]
export type FilterBarAction = {
value: string
label: string
icon?: React.ReactNode
onSelect: (
inputValue: string,
context: { path: number[]; activeFilters: FilterGroup }
context: { path: ConditionPath; activeFilters: FilterGroup }
) => void | Promise<void>
}
@@ -71,5 +73,24 @@ export type SerializableFilterProperty = Pick<
export type AIFilterRequestPayload = {
prompt: string
filterProperties: SerializableFilterProperty[]
currentPath: number[]
currentPath: ConditionPath
}
export type NavigationDirection = 'prev' | 'next'
export type HighlightNavigationResult = ConditionPath | 'clear' | null
export type ActiveInputState =
| { type: 'value'; path: ConditionPath }
| { type: 'operator'; path: ConditionPath }
| { type: 'group'; path: ConditionPath }
| null
export type KeyboardNavigationConfig = {
activeInput: ActiveInputState
setActiveInput: (input: ActiveInputState) => void
activeFilters: FilterGroup
onFilterChange: (filters: FilterGroup) => void
highlightedConditionPath: ConditionPath | null
setHighlightedConditionPath: (path: ConditionPath | null) => void
}
@@ -1,7 +1,6 @@
import { useCallback } from 'react'
import { ActiveInput } from './hooks'
import { FilterGroup, FilterProperty, isGroup } from './types'
import { ActiveInputState, FilterGroup, FilterProperty, isGroup } from './types'
import { updateGroupAtPath } from './utils'
export function useAIFilter({
@@ -16,7 +15,7 @@ export function useAIFilter({
setError,
setIsCommandMenuVisible,
}: {
activeInput: ActiveInput
activeInput: ActiveInputState
aiApiUrl?: string
freeformText: string
filterProperties: FilterProperty[]
@@ -1,8 +1,7 @@
import { useCallback } from 'react'
import { ActiveInput } from './hooks'
import { MenuItem } from './menuItems'
import { FilterGroup, FilterProperty } from './types'
import { ActiveInputState, FilterGroup, FilterProperty } from './types'
import { addFilterToGroup, addGroupToGroup, findGroupByPath, isCustomOptionObject } from './utils'
export function useCommandHandling({
@@ -18,8 +17,8 @@ export function useCommandHandling({
newPathRef,
setIsCommandMenuVisible,
}: {
activeInput: ActiveInput
setActiveInput: (input: ActiveInput) => void
activeInput: ActiveInputState
setActiveInput: (input: ActiveInputState) => void
activeFilters: FilterGroup
onFilterChange: (filters: FilterGroup) => void
filterProperties: FilterProperty[]
@@ -2,8 +2,7 @@ import { Sparkles } from 'lucide-react'
import { useMemo } from 'react'
import * as React from 'react'
import { ActiveInput } from './hooks'
import { FilterGroup, FilterProperty } from './types'
import { ActiveInputState, FilterGroup, FilterProperty } from './types'
import {
findConditionByPath,
isCustomOptionObject,
@@ -31,7 +30,7 @@ export function useCommandMenu({
aiApiUrl,
supportsOperators,
}: {
activeInput: ActiveInput
activeInput: ActiveInputState
freeformText: string
activeFilters: FilterGroup
filterProperties: FilterProperty[]
@@ -94,7 +93,7 @@ export function useCommandMenu({
}
function getOperatorItems(
activeInput: Extract<ActiveInput, { type: 'operator' }>,
activeInput: Extract<ActiveInputState, { type: 'operator' }>,
activeFilters: FilterGroup,
filterProperties: FilterProperty[]
): CommandItem[] {
@@ -117,7 +116,7 @@ function getOperatorItems(
}
function getInputValue(
activeInput: ActiveInput,
activeInput: ActiveInputState,
freeformText: string,
activeFilters: FilterGroup
): string {
@@ -138,7 +137,7 @@ function getPropertyItems(filterProperties: FilterProperty[], inputValue: string
}
function getValueItems(
activeInput: Extract<ActiveInput, { type: 'value' }>,
activeInput: Extract<ActiveInputState, { type: 'value' }>,
activeFilters: FilterGroup,
filterProperties: FilterProperty[],
propertyOptionsCache: Record<string, { options: any[]; searchValue: string }>,
@@ -1,7 +1,11 @@
import { KeyboardEvent, useCallback } from 'react'
import { ActiveInput } from './hooks'
import { FilterGroup } from './types'
import {
ConditionPath,
HighlightNavigationResult,
KeyboardNavigationConfig,
NavigationDirection,
} from './types'
import { findConditionByPath, findGroupByPath, removeFromGroup } from './utils'
export function useKeyboardNavigation({
@@ -9,14 +13,11 @@ export function useKeyboardNavigation({
setActiveInput,
activeFilters,
onFilterChange,
}: {
activeInput: ActiveInput
setActiveInput: (input: ActiveInput) => void
activeFilters: FilterGroup
onFilterChange: (filters: FilterGroup) => void
}) {
highlightedConditionPath,
setHighlightedConditionPath,
}: KeyboardNavigationConfig) {
const removeByPath = useCallback(
(path: number[]) => {
(path: ConditionPath) => {
const updatedFilters = removeFromGroup(activeFilters, path)
onFilterChange(updatedFilters)
},
@@ -24,7 +25,7 @@ export function useKeyboardNavigation({
)
const findFirstConditionInGroup = useCallback(
(groupPath: number[]): number[] | null => {
(groupPath: ConditionPath): ConditionPath | null => {
const group = findGroupByPath(activeFilters, groupPath)
if (!group || group.conditions.length === 0) return null
@@ -38,7 +39,7 @@ export function useKeyboardNavigation({
)
const findLastConditionInGroup = useCallback(
(groupPath: number[]): number[] | null => {
(groupPath: ConditionPath): ConditionPath | null => {
const group = findGroupByPath(activeFilters, groupPath)
if (!group || group.conditions.length === 0) return null
@@ -53,7 +54,7 @@ export function useKeyboardNavigation({
)
const findPreviousCondition = useCallback(
(currentPath: number[]): number[] | null => {
(currentPath: ConditionPath): ConditionPath | null => {
const groupPath = currentPath.slice(0, -1)
const conditionIndex = currentPath[currentPath.length - 1]
@@ -78,82 +79,47 @@ export function useKeyboardNavigation({
[activeFilters, findLastConditionInGroup]
)
const findNextCondition = useCallback(
(currentPath: number[]): number[] | null => {
const groupPath = currentPath.slice(0, -1)
const conditionIndex = currentPath[currentPath.length - 1]
const group = findGroupByPath(activeFilters, groupPath)
const getHighlightNavigationPath = useCallback(
(direction: NavigationDirection): HighlightNavigationResult => {
if (activeInput?.type !== 'group') return null
if (group && conditionIndex < group.conditions.length - 1) {
const nextPath = [...groupPath, conditionIndex + 1]
const nextCondition = group.conditions[conditionIndex + 1]
if (!('logicalOperator' in nextCondition)) {
return nextPath
const group = findGroupByPath(activeFilters, activeInput.path)
if (!group || group.conditions.length === 0) return null
if (highlightedConditionPath) {
const currentIndex = highlightedConditionPath[highlightedConditionPath.length - 1]
if (direction === 'prev') {
if (currentIndex > 0) {
return [...activeInput.path, currentIndex - 1]
}
return 'clear'
} else {
if (currentIndex < group.conditions.length - 1) {
return [...activeInput.path, currentIndex + 1]
}
return 'clear'
}
return findFirstConditionInGroup(nextPath)
} else {
if (direction === 'prev') {
return [...activeInput.path, group.conditions.length - 1]
}
// 'next' with no highlight does nothing - user is already at the rightmost position
return null
}
if (groupPath.length > 0) {
return findNextCondition(groupPath)
}
return null
},
[activeFilters, findFirstConditionInGroup]
[activeInput, activeFilters, highlightedConditionPath]
)
const findPreviousConditionFromGroup = useCallback(
(groupPath: number[]): number[] | null => {
const group = findGroupByPath(activeFilters, groupPath)
if (group && group.conditions.length > 0) {
return findLastConditionInGroup(groupPath)
const applyHighlightNavigation = useCallback(
(result: HighlightNavigationResult) => {
if (result === 'clear') {
setHighlightedConditionPath(null)
} else if (result) {
setHighlightedConditionPath(result)
}
if (groupPath.length > 0) {
const parentPath = groupPath.slice(0, -1)
const groupIndex = groupPath[groupPath.length - 1]
if (groupIndex > 0) {
const prevSiblingPath = [...parentPath, groupIndex - 1]
const parentGroup = findGroupByPath(activeFilters, parentPath)
const prevSibling = parentGroup?.conditions[groupIndex - 1]
if (prevSibling) {
if ('logicalOperator' in prevSibling) {
return findLastConditionInGroup(prevSiblingPath)
} else {
return prevSiblingPath
}
}
}
return findPreviousConditionFromGroup(parentPath)
}
return null
},
[activeFilters, findLastConditionInGroup]
)
const findNextConditionFromGroup = useCallback(
(groupPath: number[]): number[] | null => {
if (groupPath.length > 0) {
const parentPath = groupPath.slice(0, -1)
const groupIndex = groupPath[groupPath.length - 1]
const parentGroup = findGroupByPath(activeFilters, parentPath)
if (parentGroup && groupIndex < parentGroup.conditions.length - 1) {
const nextSiblingPath = [...parentPath, groupIndex + 1]
const nextSibling = parentGroup.conditions[groupIndex + 1]
if ('logicalOperator' in nextSibling) {
return findFirstConditionInGroup(nextSiblingPath)
} else {
return nextSiblingPath
}
}
return findNextConditionFromGroup(parentPath)
}
return null
},
[activeFilters, findFirstConditionInGroup]
[setHighlightedConditionPath]
)
const handleBackspace = useCallback(
@@ -165,14 +131,19 @@ export function useKeyboardNavigation({
if (activeInput?.type === 'group' && isEmpty) {
e.preventDefault()
const group = findGroupByPath(activeFilters, activeInput.path)
if (group && group.conditions.length > 0) {
const lastConditionPath = [...activeInput.path, group.conditions.length - 1]
removeByPath(lastConditionPath)
setActiveInput({ type: 'group', path: activeInput.path })
} else if (group && group.conditions.length === 0 && activeInput.path.length > 0) {
// Only remove nested empty groups, not the root group
if (highlightedConditionPath) {
removeByPath(highlightedConditionPath)
setHighlightedConditionPath(null)
} else {
const result = getHighlightNavigationPath('prev')
applyHighlightNavigation(result)
}
} else if (activeInput?.type === 'group' && activeInput.path.length > 0) {
// Edge case: remove nested empty groups, but not the root group
const group = findGroupByPath(activeFilters, activeInput.path)
if (group && group.conditions.length === 0) {
e.preventDefault()
removeByPath(activeInput.path)
setActiveInput({
type: 'group',
@@ -187,66 +158,90 @@ export function useKeyboardNavigation({
}
}
},
[activeInput, activeFilters, removeByPath, setActiveInput]
[
activeInput,
activeFilters,
removeByPath,
setActiveInput,
highlightedConditionPath,
setHighlightedConditionPath,
getHighlightNavigationPath,
applyHighlightNavigation,
]
)
const handleArrowLeft = useCallback(
(e: KeyboardEvent<HTMLInputElement>) => {
const inputElement = e.target as HTMLInputElement
if (inputElement.selectionStart === 0) {
const isEmpty = inputElement.value === ''
if (activeInput?.type === 'group' && isEmpty) {
e.preventDefault()
if (activeInput?.type === 'value') {
const prevPath = findPreviousCondition(activeInput.path)
if (prevPath) {
setActiveInput({ type: 'value', path: prevPath })
}
} else if (activeInput?.type === 'group') {
const prevPath = findPreviousConditionFromGroup(activeInput.path)
if (prevPath) {
setActiveInput({ type: 'value', path: prevPath })
}
const result = getHighlightNavigationPath('prev')
applyHighlightNavigation(result)
return
}
if (activeInput?.type === 'value' && inputElement.selectionStart === 0) {
e.preventDefault()
const prevPath = findPreviousCondition(activeInput.path)
if (prevPath) {
setActiveInput({ type: 'value', path: prevPath })
}
}
},
[activeInput, findPreviousCondition, findPreviousConditionFromGroup, setActiveInput]
[
activeInput,
getHighlightNavigationPath,
applyHighlightNavigation,
findPreviousCondition,
setActiveInput,
]
)
const handleArrowRight = useCallback(
(e: KeyboardEvent<HTMLInputElement>) => {
const inputElement = e.target as HTMLInputElement
if (inputElement.selectionStart === inputElement.value.length) {
e.preventDefault()
if (activeInput?.type === 'value') {
const groupPath = activeInput.path.slice(0, -1)
const conditionIndex = activeInput.path[activeInput.path.length - 1]
const group = findGroupByPath(activeFilters, groupPath)
const isEmpty = inputElement.value === ''
if (group && conditionIndex < group.conditions.length - 1) {
const nextCondition = group.conditions[conditionIndex + 1]
if ('logicalOperator' in nextCondition) {
const nextPath = findFirstConditionInGroup([...groupPath, conditionIndex + 1])
if (nextPath) {
setActiveInput({ type: 'value', path: nextPath })
}
} else {
setActiveInput({ type: 'value', path: [...groupPath, conditionIndex + 1] })
if (activeInput?.type === 'group' && isEmpty && highlightedConditionPath) {
e.preventDefault()
const result = getHighlightNavigationPath('next')
applyHighlightNavigation(result)
return
}
if (
activeInput?.type === 'value' &&
inputElement.selectionStart === inputElement.value.length
) {
e.preventDefault()
const groupPath = activeInput.path.slice(0, -1)
const conditionIndex = activeInput.path[activeInput.path.length - 1]
const group = findGroupByPath(activeFilters, groupPath)
if (group && conditionIndex < group.conditions.length - 1) {
const nextCondition = group.conditions[conditionIndex + 1]
if ('logicalOperator' in nextCondition) {
const nextPath = findFirstConditionInGroup([...groupPath, conditionIndex + 1])
if (nextPath) {
setActiveInput({ type: 'value', path: nextPath })
}
} else {
setActiveInput({ type: 'group', path: groupPath })
}
} else if (activeInput?.type === 'group') {
const nextPath = findNextConditionFromGroup(activeInput.path)
if (nextPath) {
setActiveInput({ type: 'value', path: nextPath })
setActiveInput({ type: 'value', path: [...groupPath, conditionIndex + 1] })
}
} else {
setActiveInput({ type: 'group', path: groupPath })
}
}
},
[
activeInput,
activeFilters,
highlightedConditionPath,
getHighlightNavigationPath,
applyHighlightNavigation,
findFirstConditionInGroup,
findNextConditionFromGroup,
setActiveInput,
]
)
@@ -260,12 +255,21 @@ export function useKeyboardNavigation({
} else if (e.key === 'ArrowRight') {
handleArrowRight(e)
} else if (e.key === 'Escape') {
const activeElement = document.activeElement as HTMLElement | null
if (activeElement && activeElement.blur) {
activeElement.blur()
if (highlightedConditionPath) {
e.preventDefault()
setHighlightedConditionPath(null)
} else {
const activeElement = document.activeElement as HTMLElement | null
if (activeElement && activeElement.blur) {
activeElement.blur()
}
}
} else if (e.key === 'Enter') {
if (activeInput?.type === 'value') {
if (highlightedConditionPath) {
e.preventDefault()
setActiveInput({ type: 'value', path: highlightedConditionPath })
setHighlightedConditionPath(null)
} else if (activeInput?.type === 'value') {
e.preventDefault()
setActiveInput({ type: 'group', path: activeInput.path.slice(0, -1) })
} else if (activeInput?.type === 'operator') {
@@ -275,7 +279,15 @@ export function useKeyboardNavigation({
}
}
},
[activeInput, handleBackspace, handleArrowLeft, handleArrowRight, setActiveInput]
[
activeInput,
handleBackspace,
handleArrowLeft,
handleArrowRight,
setActiveInput,
highlightedConditionPath,
setHighlightedConditionPath,
]
)
return {