Conversation
| :::caution[Reserved Action Names] | ||
| Avoid creating custom actions with names starting with `move-focus-*` (e.g., `move-focus-left`, `move-focus-right`, etc.). These names are reserved and used internally by the spatial navigation system for managing focus between UI elements. | ||
| ::: |
There was a problem hiding this comment.
Can we outline all the actions that are reserved? For example we can describe them in the IM documentation and here we can leave a link to them.
|
|
||
| **With an anchor element:** | ||
|
|
||
| Sometimes you want a component to respond to actions when a different element is focused (like a child button): |
There was a problem hiding this comment.
Can we add more descriptive example - for example how to propagate action when a row with a dropdown is focused. The dropdown should receive the actions from the row via the anchor.
There was a problem hiding this comment.
Added a new section with more in-depth explanation
| if (!areas.has(area)) { | ||
| console.warn(`Area "${area}" not registered. Available areas:`, Array.from(areas)); | ||
| return; | ||
| } |
There was a problem hiding this comment.
We can move this into method (e.g isAreaValid) and reuse it.
const isAreaValid = (area: string) => {
if (!areas.has(area)) {
console.warn(`Area "${area}" not registered. Available areas:`, Array.from(areas));
return false;
}
return true;
}
const focusFirst = (area: string) => {
if (!isAreaValid(area)) return;
| import { keybindPresetContent } from "@custom-components/Menu/SidePanel/keybindsPanelContent"; | ||
| import KeyBind from "@custom-components/Menu/KeyBind/KeyBind"; | ||
| import eventBus from "@components/tools/EventBus"; | ||
| import eventBus from "@components/Utility/EventBus"; |
There was a problem hiding this comment.
Some import statements here are not used. we can remove them
| let anchorElement: HTMLElement | null = null; | ||
| if (anchor) { | ||
| if (typeof anchor === 'string') { | ||
| waitForFrames(() => anchorElement = document.querySelector(anchor)); |
There was a problem hiding this comment.
Why do we wait for frames here?
There was a problem hiding this comment.
Sometimes some elements can't be found.
There was a problem hiding this comment.
Pull request overview
This PR implements a comprehensive Navigation component that provides keyboard and gamepad input handling for the Menu UI. The integration enables tab switching between Gameplay and Graphics sections using Q/E keys, menu item navigation with arrow keys and D-pad, and stepper control via left/right inputs. The implementation is built on top of the Coherent Gameface Interaction Manager and includes spatial navigation, a flexible action mapping system, and context-aware scope management.
- Adds new Navigation component with action and area management systems
- Integrates Navigation into the Menu UI with keyboard (Q/E, arrows) and gamepad (D-pad, shoulder buttons) controls
- Updates EventBus import paths from
@components/tools/EventBusto@components/Utility/EventBus
Reviewed changes
Copilot reviewed 21 out of 24 changed files in this pull request and generated 23 comments.
Show a summary per file
| File | Description |
|---|---|
| src/views/menu/Menu.tsx | Wraps menu in Navigation component, adds default actions for tab switching and test buttons for development |
| src/views/menu/util/index.ts | Updates EventBus import path to new Utility directory |
| src/custom-components/Menu/Options/Graphics/Graphics.tsx | Wraps content in Navigation.Area for spatial navigation |
| src/custom-components/Menu/Options/Gameplay/Gameplay.tsx | Wraps content in Navigation.Area, adds navigation actions to Stepper, changes subtitle default to visible |
| src/custom-components/Menu/Options/Audio/Audio.tsx | Updates EventBus import path |
| src/custom-components/Menu/MenuItem/MenuItem.tsx | Adds focus handling and menu-item class for navigation targeting |
| src/components/types/ComponentProps.d.ts | Adds navigation-actions prop and anchor support for components |
| src/components/Utility/Navigation/types.ts | Defines action configuration types and navigation config interface |
| src/components/Utility/Navigation/keybindings/keybindings.types.ts | Defines keyboard and gamepad button binding types |
| src/components/Utility/Navigation/defaults.ts | Configures default navigation actions (move, select, back) |
| src/components/Utility/Navigation/areaMethods/useAreaMethods.ts | Implements area registration, focus management, and pause/resume functionality |
| src/components/Utility/Navigation/areaMethods/areaMethods.types.ts | Defines interface for area-related navigation methods |
| src/components/Utility/Navigation/actionMethods/useActionMethods.ts | Implements action registration, execution, and state management |
| src/components/Utility/Navigation/actionMethods/actionMethods.types.ts | Defines interface for action-related navigation methods |
| src/components/Utility/Navigation/NavigationArea.tsx | Subcomponent for defining navigable UI areas with automatic cleanup |
| src/components/Utility/Navigation/Navigation.tsx | Main Navigation component providing context and API for navigation system |
| src/components/Utility/EventBus/index.ts | Adds line numbers to EventBus (no functional changes) |
| src/components/Basic/Stepper/Stepper.tsx | Integrates navigationActions directive for move-left/move-right support |
| src/components/BaseComponent/BaseComponent.tsx | Adds navigationActions directive for subscribing components to navigation actions |
| package.json | Adds coherent-gameface-interaction-manager dependency (v2.4.4) |
| docs/src/content/docs/components/index.mdx | Adds Utility category to component documentation |
| docs/src/content/docs/components/Utility/Navigation.mdx | Comprehensive documentation for Navigation component and action system |
| docs/src/content/docs/components/Basic/stepper.mdx | Documents Stepper's navigation action support |
| docs/src/assets/components/utility/navigation.svg | Adds Navigation component icon |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| </Stepper> | ||
| <Navigation.Area name="menu" selector="menu-item" focused> | ||
| <MenuItem id="difficulty" name='Difficulty'> | ||
| <Stepper anchor=".menu-item" onChange={emitChange} style={{width: '15vmax'}} navigation-actions={{select: () => console.log('custom select implementation')}}> |
There was a problem hiding this comment.
The console.log statement 'custom select implementation' appears to be debug/placeholder code. Consider removing this or implementing the actual custom select behavior for the difficulty stepper.
| <Stepper anchor=".menu-item" onChange={emitChange} style={{width: '15vmax'}} navigation-actions={{select: () => console.log('custom select implementation')}}> | |
| <Stepper anchor=".menu-item" onChange={emitChange} style={{width: '15vmax'}}> |
| | 'left-sholder' | ||
| | 'right-sholder' | ||
| | 'left-sholder-bottom' | ||
| | 'right-sholder-bottom' |
There was a problem hiding this comment.
The gamepad button binding 'right-sholder-bottom' is misspelled. It should be 'right-shoulder-bottom' to match the correct spelling.
| | 'right-sholder-bottom' | |
| | 'right-shoulder-bottom' |
|
|
||
| const App = () => { | ||
| const defaultActions: ActionMap = { | ||
| 'tab-left': {key: {binds: ['Q'], type: ['press']}, button: {binds: ['left-shoulder'], type: 'press'}, callback: menuLeft, global: true}, |
There was a problem hiding this comment.
The word 'sholder' in the comment is misspelled. It should be 'shoulder' to be consistent with the corrected gamepad button names.
|
|
||
| props.componentClasses = () => stepperClasses(); | ||
| const { className, inlineStyles, forwardEvents, forwardAttrs } = useBaseComponent(props); | ||
| const { className, inlineStyles, forwardEvents, forwardAttrs, navigationActions } = useBaseComponent(props); |
There was a problem hiding this comment.
Unused variable navigationActions.
| const { className, inlineStyles, forwardEvents, forwardAttrs, navigationActions } = useBaseComponent(props); | |
| const { className, inlineStyles, forwardEvents, forwardAttrs } = useBaseComponent(props); |
| // @ts-ignore | ||
| import { gamepad } from 'coherent-gameface-interaction-manager'; | ||
| import NavigationArea from "./NavigationArea"; | ||
| import eventBus from "@components/Utility/EventBus"; |
There was a problem hiding this comment.
Unused import eventBus.
| import eventBus from "@components/Utility/EventBus"; |
| @@ -0,0 +1,48 @@ | |||
| import { children, createEffect, on, onCleanup, onMount, ParentComponent, useContext } from "solid-js" | |||
There was a problem hiding this comment.
Unused import useContext.
| import { children, createEffect, on, onCleanup, onMount, ParentComponent, useContext } from "solid-js" | |
| import { children, createEffect, on, onCleanup, onMount, ParentComponent } from "solid-js" |
| @@ -0,0 +1,48 @@ | |||
| import { children, createEffect, on, onCleanup, onMount, ParentComponent, useContext } from "solid-js" | |||
| import { NavigationContext, useNavigation } from "./Navigation"; | |||
There was a problem hiding this comment.
Unused import NavigationContext.
| import { NavigationContext, useNavigation } from "./Navigation"; | |
| import { useNavigation } from "./Navigation"; |
| import { keybindPresetContent } from "@custom-components/Menu/SidePanel/keybindsPanelContent"; | ||
| import KeyBind from "@custom-components/Menu/KeyBind/KeyBind"; | ||
| import eventBus from "@components/tools/EventBus"; | ||
| import eventBus from "@components/Utility/EventBus"; |
There was a problem hiding this comment.
Unused import eventBus.
| import eventBus from "@components/Utility/EventBus"; |
* Extend button, toggleButton and checkbox and add docs * Extend sliders to use navigation * Extend tooltip to implement actions * Extend dropdown with navigation actions * handle dropdown nav edge cases * Add nav actions to inputs and add reusable input wrapper component * Refactor Navigation pause/resume to avoid deinit cycles * Extend Accordion to use navigation actions * Fix anchor and action type inconsistencies across components * Fix IM types after ts migration * resolve pr comments
b25550c to
e75acae
Compare
I made a minimal integration of the Navigation component inside the Menu UI for easier testing. You can: