diff --git a/docs/public/video/accordion/header-navigation.webm b/docs/public/video/accordion/header-navigation.webm
new file mode 100644
index 00000000..2bf623d1
Binary files /dev/null and b/docs/public/video/accordion/header-navigation.webm differ
diff --git a/docs/src/assets/components/utility/navigation.svg b/docs/src/assets/components/utility/navigation.svg
new file mode 100644
index 00000000..dea27dca
--- /dev/null
+++ b/docs/src/assets/components/utility/navigation.svg
@@ -0,0 +1,88 @@
+
diff --git a/docs/src/content/docs/changelog/index.mdx b/docs/src/content/docs/changelog/index.mdx
index 365f29f9..848353e7 100644
--- a/docs/src/content/docs/changelog/index.mdx
+++ b/docs/src/content/docs/changelog/index.mdx
@@ -4,6 +4,48 @@ title: Changelog
import { Badge } from '@astrojs/starlight/components'
+## 2.0.0 :badge[Latest]{variant="note"}
+
+### ⚠️ Breaking Changes
+
+- The `ref` prop on layout components (Absolute, Flex, Block, etc.) that don't have any other methods or properties now exposes the DOM node directly. You no longer need to access the `.element` property.
+ Affected components:
+[Absolute](/components/layout/absolute/), [Block](/components/layout/block/), [Bottom](/components/layout/bottom/), [Column](/components/layout/column/), [Content](/components/layout/content/), [Flex](/components/layout/flex/),
+[Grid](/components/layout/grid/), [GridTile](/components/layout/grid-tile/), [Layout](/components/layout/layout/), [Layout3D](/components/layout/layout-3d/), [List](/components/layout/list/),
+[Relative](/components/layout/relative/), [Row](/components/layout/row/), [TabLink](/components/layout/tab-link/), [Tab](/components/layout/tab/), [Top](/components/layout/top/), [Transform](/components/layout/transform/),
+[BackgroundImage](/components/media/background-image/), [MaskImage](/components/media/mask-image/).
+
+### ✨ New Features
+
+- [**Navigation**](/components/utility/navigation/) - Introduced a new utility component for handling spatial navigation, gamepad/keyboard interactions and focus management.
+
+- **Added [Navigation.Area]((/components/utility/navigation/#navigationarea)) slot** for grouping navigable components within the [Navigation](/components/utility/navigation/) component.
+
+- **Custom Component Action Subscriptions:** Custom components can now subscribe to [Navigation actions](/components/utility/navigation/#extending-component-navigation-actions).
+
+### 🛠 Component Enhancements (Navigation Support)
+
+The following components have been extended to support onAction and anchor props. They now respond to specific spatial navigation actions when placed inside a Navigation area:
+
+| Component | Supported Actions / Props |
+| :--- | :--- |
+| [Button](/components/basic/button#props), [RoundedButton](/components/basic/rounded-button#props) | (`onAction`, `anchor`) |
+| [Checkbox](/components/basic/checkbox/#implemented-navigation-actions) | , |
+| [Accordion](/components/basic/accordion/#implemented-navigation-actions) | , |
+| [Dropdown](/components/basic/dropdown/#implemented-navigation-actions) | , |
+| [NumberInput](/components/basic/number-input/#implemented-navigation-actions) | , |
+| [PasswordInput](/components/basic/password-input/#implemented-navigation-actions), [TextInput](/components/basic/text-input/#implemented-navigation-actions) | , |
+| [Radio](/components/basic/radio/#implemented-navigation-actions), [Segment](/components/basic/segment/#implemented-navigation-actions), [Stepper](/components/basic/stepper/#implemented-navigation-actions), [Slider](/components/basic/slider/#implemented-navigation-actions), [TextSlider](/components/basic/text-slider/#implemented-navigation-actions) | , |
+| [XYSlider](/components/basic/xy-slider/#implemented-navigation-actions), [Scroll](/components/layout/scroll/#implemented-navigation-actions) | , |
+| [Tooltip](/components/feedback/tooltip/#implemented-navigation-actions) | , |
+| [TextBlock](/components/basic/text-block#props), [InlineTextBlock](/components/basic/inline-text-block#props) | (`onAction`, `anchor`) |
+
+### 📖 New Guides
+
+- [List](/components/layout/list) - Provide [a guide](/components/layout/list/#making-list-items-navigable) on how to initialize list items as navigable elements within a [Navigation](/components/utility/Navigation/) area.
+
+- [GridTile](/components/layout/grid-tile) - Provide [a guide](/components/layout/grid-tile/#making-grid-tiles-navigable) on how to initialize grid tiles as navigable elements within a [Navigation](/components/utility/Navigation/) area.
+
## 1.2.0 :badge[Latest]{variant="note"}
Added [Icon](/components/media/icon/) component.
@@ -12,7 +54,7 @@ import { Badge } from '@astrojs/starlight/components'
Added an automatic script that runs with the development server for updating the [Icon](/components/media/icon/) component's type whenever new images are added to the `src/assets/icons` directory.
-## 1.1.0
+## 1.1.0
Added [Tutorial](/components/complex/tutorial/) component.
diff --git a/docs/src/content/docs/components/Basic/accordion.mdx b/docs/src/content/docs/components/Basic/accordion.mdx
index d51f8a5b..7f4de690 100644
--- a/docs/src/content/docs/components/Basic/accordion.mdx
+++ b/docs/src/content/docs/components/Basic/accordion.mdx
@@ -124,6 +124,8 @@ It displays the panel's title or label and handles user interaction for expandin
| `style` | `JSX.CSSProperties` | `{}` | Inline styles to apply to the heading element. |
| `class` | `string` | `""` | Additional CSS classes for the heading element. |
| `children` | `JSX.Element` | `""` | Content of the heading, used to render text, HTML, or JSX elements within the heading. |
+| `onAction` | `Record void>` | `undefined` | Extends or overrides the heading's default navigation action handlers. See [Implemented Navigation Actions](#implemented-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, the heading's actions will execute. Can be a CSS selector or HTMLElement. |
### `Accordion.Icon`
@@ -185,6 +187,37 @@ It displays the main content associated with the panel.
| `class` | `string` | `""` | Additional CSS classes for the body element. |
| `children` | `JSX.Element` | `""` | Content of the body, used to render text, HTML, or JSX elements within the body. |
+## Implemented Navigation Actions
+
+The `Accordion.Heading` component slot implements the following navigation action by default:
+
+| Action Name | Behavior |
+|--------------|-----------------------------------------------------------------------------------------------|
+| `select` | Toggles the accordion panel (expands if collapsed, collapses if expanded) |
+
+You can extend the `Accordion.Heading` with additional navigation actions or override the default behavior using the `onAction` prop:
+
+```tsx
+import Accordion from '@components/Basic/Accordion/Accordion';
+
+
+
+ console.log('Custom select behavior!'),
+ 'info': () => console.log('Show panel info')
+ }}
+ >
+ Heading 1
+
+ Accordion content
+
+
+```
+
+For more information about navigation actions, see the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions).
+
## Guide
### Retrieve the modified panel's title on change
@@ -262,4 +295,140 @@ const App = () => {
};
export default App;
+```
+
+### Making accordion headings navigable
+
+The `Accordion.Heading` needs to be focused in order to respond to the `select` action.
+
+To enable gamepad/keyboard navigation for accordion headings, add them to a navigation area by assigning a CSS class and registering them with `Navigation.Area` or `navigationRef.registerArea()`.
+
+#### Steps
+
+1. Wrap your `Accordion` inside a `Navigation` component
+2. Assign a CSS class to each `Accordion.Heading` you want to be navigable
+3. Register the headings as a navigation area using either:
+ - `navigationRef.registerArea()` with the class selector
+ - Or wrap the accordion in `Navigation.Area` with the appropriate selector
+
+```tsx
+import Navigation, { NavigationRef } from '@components/Utility/Navigation/Navigation';
+import Accordion from '@components/Basic/Accordion/Accordion';
+
+const App = () => {
+ let navigationRef: NavigationRef | undefined;
+
+ // Register accordion headings as a navigation area
+ onMount(() => {
+ navigationRef?.registerArea('accordion-headers', ['.accordion-heading']);
+ });
+
+ return (
+
+
+
+
+ Settings
+
+ Settings content
+
+
+
+ Audio
+
+ Audio content
+
+
+
+ );
+};
+
+export default App;
+```
+
+### Navigating accordion body content
+
+
+
+For more complex accordion implementations where the body contains navigable elements (like form inputs, buttons, or lists), you can create a `Navigation.Area` inside the `Accordion.Body` and automatically focus it when the panel expands.
+
+#### Steps
+
+1. Wrap the elements you wish to be navigable inside `Accordion.Body` with `Navigation.Area`
+2. Use the `onChange` callback to detect when the panel expands
+3. Call `navigationRef.focusFirst()` or `navigationRef.switchArea()` to focus the body's navigation area
+4. Optionally set up a `back` action to return focus to the accordion headings
+
+```tsx
+import Navigation, { NavigationRef } from '@components/Utility/Navigation/Navigation';
+import Accordion from '@components/Basic/Accordion/Accordion';
+
+const App = () => {
+ let navigationRef: NavigationRef | undefined;
+
+ // Handle accordion panel changes
+ const handleAccordionChange = (title: string) => {
+ if (title === 'settings-panel') {
+ // Focus the navigation area inside the expanded panel
+ navigationRef?.focusFirst('settings-area');
+ }
+ };
+
+ onMount(() => {
+ // Register accordion headings as an area and focus it.
+ navigationRef?.registerArea('accordion-headers', ['.accordion-heading'], true);
+
+ // Configure back action to return to accordion headers
+ navigationRef?.updateAction('back', {
+ key: { binds: ['ESC'], type: ['press'] },
+ button: { binds: ['face-button-right'] },
+ callback: (scope) => {
+ if (scope === 'settings-area') {
+ navigationRef?.focusFirst('accordion-headers');
+ }
+ }
+ });
+ });
+
+ return (
+
+
+
+
+ Settings
+
+
+
+
Graphics Quality
+
Resolution
+
V-Sync
+
+
+
+
+
+ Audio
+
+ Audio settings content
+
+
+
+ );
+};
+
+export default App;
+```
+
+CSS for `.menu-item`:
+
+```css
+.menu-item {
+ padding: 1vmax;
+ border-bottom: 1px solid #232323;
+ background-color: #cecece;
+}
+
+.menu-item:focus {
+ background-color: #b1b1b1;
+}
```
\ No newline at end of file
diff --git a/docs/src/content/docs/components/Basic/button.mdx b/docs/src/content/docs/components/Basic/button.mdx
index a66cc89e..b314830b 100644
--- a/docs/src/content/docs/components/Basic/button.mdx
+++ b/docs/src/content/docs/components/Basic/button.mdx
@@ -37,6 +37,8 @@ export default App;
| `disabled` | `boolean` | `false` | Specify if the button is disabled |
| `size` | `'large' \| 'middle' \| 'small'` | `''` | Specify the size of the button. If an empty string is passed, the button won't have any size. In that case, please specify the size through the `class` or `style` properties. |
| `textFit` | `boolean` | `true` | Specify if the text inside the button should be fitted. By default, this option is **enabled**. |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the button. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, the button's actions will execute. Can be a CSS selector or HTMLElement. |
## Guide
diff --git a/docs/src/content/docs/components/Basic/checkbox.mdx b/docs/src/content/docs/components/Basic/checkbox.mdx
index 3d97d53b..d80c9acb 100644
--- a/docs/src/content/docs/components/Basic/checkbox.mdx
+++ b/docs/src/content/docs/components/Basic/checkbox.mdx
@@ -38,6 +38,8 @@ export default App;
| `value` | `any` | `''` | The value associated with the checkbox component. |
| `checked` | `boolean` | `false` | Specify if the checkbox is checked initially. |
| `onChange` | `(checked: boolean) => void` | `undefined` | A function that is called every time the checkbox is toggled. It can be used to retrieve whether the checkbox is checked. |
+| `onAction` | `Record void>` | `undefined` | Extends or overrides the component's default navigation action handlers. See [Implemented Navigation Actions](#implemented-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, the checkbox's actions will execute. Can be a CSS selector or HTMLElement. |
## Ref API
@@ -176,6 +178,32 @@ const App = () => {
export default App;
```
+## Implemented Navigation Actions
+
+The `Checkbox` component implements the following navigation actions by default:
+
+| Action Name | Behavior |
+|-------------|-----------------------------------|
+| `select` | Toggles the checkbox on/off |
+
+You can extend the Checkbox with additional navigation actions or override the default behavior using the `onAction` prop:
+
+```tsx
+import Checkbox from '@components/Basic/Checkbox/Checkbox';
+
+ console.log('Going back from checkbox'),
+ 'select': () => console.log('Custom toggle logic') // Overrides default
+ }}
+>
+ Enable V-Sync
+
+```
+
+For more information about navigation actions, see the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions).
+
## Guide
### Retrive the Checkbox Value
diff --git a/docs/src/content/docs/components/Basic/dropdown.mdx b/docs/src/content/docs/components/Basic/dropdown.mdx
index ed931484..2b8d80ea 100644
--- a/docs/src/content/docs/components/Basic/dropdown.mdx
+++ b/docs/src/content/docs/components/Basic/dropdown.mdx
@@ -39,6 +39,8 @@ export default App;
| `disabled` | `boolean` | `false` | Disables the dropdown when set to `true`. |
| `class-disabled` | `string` | `""` | Additional CSS classes to apply when the dropdown is disabled. |
| `onChange` | `(value: string) => void` | `undefined` | Callback function triggered whenever the selected option changes, providing the selected option's value. |
+| `onAction` | `Record void>` | `undefined` | Extends or overrides the component's default navigation action handlers. See [Implemented Navigation Actions](#implemented-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, the dropdown's actions will execute. Can be a CSS selector or HTMLElement. |
## Ref API
@@ -268,6 +270,40 @@ To hide the `Dropdown.Handle`, set its `display` style to `none`:
:::
+## Implemented Navigation Actions
+
+The `Dropdown` component implements the following navigation actions by default:
+
+| Action Name | Behavior |
+|--------------|-----------------------------------------------------------------------------------------------|
+| `select` | Opens the dropdown when closed. When open, collapses the dropdown (options handle selection) |
+| `back` | Closes the dropdown and returns focus to the trigger or anchor element |
+
+You can extend the `Dropdown` with additional navigation actions or override the default behavior using the `onAction` prop:
+
+```tsx
+import Dropdown from '@components/Basic/Dropdown/Dropdown';
+
+ console.log('Custom select behavior!'),
+ 'back': () => console.log('Custom back behavior!')
+ }}
+>
+
+ red
+ green
+ blue
+
+
+```
+
+:::caution
+Every `Dropdown.Option` component is listening for the `select` action to handle option selection. This action cannot be overriden.
+:::
+
+For more information about navigation actions, check the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions).
+
## Guide
### Retrieve the selected option's value on change
diff --git a/docs/src/content/docs/components/Basic/inline-text-block.mdx b/docs/src/content/docs/components/Basic/inline-text-block.mdx
index 092e94c1..106bab46 100644
--- a/docs/src/content/docs/components/Basic/inline-text-block.mdx
+++ b/docs/src/content/docs/components/Basic/inline-text-block.mdx
@@ -33,6 +33,8 @@ export default App;
| `style` | `JSX.CSSProperties` | `{}` | Styles applied directly to the root element of the component.. |
| `class` | `string` | `""` | Additional CSS classes to apply to the component. |
| `ref` | `HTMLButtonElement \| undefined` | `undefined` | Obtains the component's DOM element and assigns it to a variable, allowing direct access to the HTML element via the ref object. |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the component. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, the component's actions will execute. Can be a CSS selector or HTMLElement. |
## Guide
diff --git a/docs/src/content/docs/components/Basic/number-input.mdx b/docs/src/content/docs/components/Basic/number-input.mdx
index c52a5f61..1d685146 100644
--- a/docs/src/content/docs/components/Basic/number-input.mdx
+++ b/docs/src/content/docs/components/Basic/number-input.mdx
@@ -40,6 +40,8 @@ export default App;
| `class-disabled` | `string` | `undefined` | Optional class to apply when the input is disabled. |
| `readonly` | `boolean` | `false` | Specifies if the input is only able to be read. If set to true, the input will not accept values. |
| `onChange` | `(value: string \| number) => void` | `undefined` | A function that is called every time the input's value has changed. It can be used to retrieve the up to date value of the input. |
+| `onAction` | `Record void>` | `undefined` | Extends or overrides the component's default navigation action handlers. See [Implemented Navigation Actions](#implemented-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, the input's actions will execute. Can be a CSS selector or HTMLElement. |
## Ref API
@@ -177,6 +179,39 @@ const App = () => {
export default App;
```
+## Implemented Navigation Actions
+
+The `NumberInput` component implements the following navigation actions by default:
+
+| Action Name | Behavior |
+|-------------|---------------------------------------------------------|
+| `select` | Focuses the input element, making it ready for typing |
+| `back` | Blurs the input element, exiting typing mode |
+| `move-up` | Increases the value of the input by the `step` prop's value |
+| `move-down` | Increases the value of the input by the `step` prop's value |
+
+You can extend the `NumberInput` with additional navigation actions or override the default behavior using the `onAction` prop:
+
+```tsx
+import NumberInput, { NumberInputRef } from '@components/Basic/NumberInput/NumberInput';
+
+const App = () => {
+ let inputRef!: NumberInputRef;
+
+ return (
+ console.log('Custom select action'),
+ 'back': () => console.log('Custom back action'),
+ }}
+ />
+ );
+};
+```
+
+For more information about navigation actions, see the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions).
+
## Guide
### Increase or decrease value programmatically
diff --git a/docs/src/content/docs/components/Basic/pagination.mdx b/docs/src/content/docs/components/Basic/pagination.mdx
index 96d2b4cc..408a4860 100644
--- a/docs/src/content/docs/components/Basic/pagination.mdx
+++ b/docs/src/content/docs/components/Basic/pagination.mdx
@@ -36,6 +36,8 @@ export default App;
| `hasNumbers` | `boolean` | `false` | If true, displays page numbers inside each page indicator. |
| `ref` | `PaginationRef \| undefined` | `undefined` | Provides access to the pagination element and control methods. |
| `onChange` | `(index: number) => void` | `undefined` | Called whenever the selected page changes, providing the new index. |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the component. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, the component's actions will execute. Can be a CSS selector or HTMLElement. |
:::caution
The `pageIndex` prop is 1-based, meaning the first page is represented by `1`, the second by `2`, and so on.
diff --git a/docs/src/content/docs/components/Basic/password-input.mdx b/docs/src/content/docs/components/Basic/password-input.mdx
index 76501b5a..61901726 100644
--- a/docs/src/content/docs/components/Basic/password-input.mdx
+++ b/docs/src/content/docs/components/Basic/password-input.mdx
@@ -38,6 +38,8 @@ export default App;
| `max-symbols` | `number` | `undefined` | Maximum number of symbols the input can accept. |
| `class-disabled` | `string` | `undefined` | Optional class to apply when the input is disabled. |
| `onChange` | `(value: string) => void` | `undefined` | A function that is called every time the input's value has changed. It can be used to retrieve the up to date value of the input. |
+| `onAction` | `Record void>` | `undefined` | Extends or overrides the component's default navigation action handlers. See [Implemented Navigation Actions](#implemented-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, the input's actions will execute. Can be a CSS selector or HTMLElement. |
## Ref API
@@ -186,6 +188,39 @@ const App = () => {
export default App;
```
+## Implemented Navigation Actions
+
+The `PasswordInput` component implements the following navigation actions by default:
+
+| Action Name | Behavior |
+|-------------|---------------------------------------------------------|
+| `select` | Focuses the input element, making it ready for typing |
+| `back` | Blurs the input element, exiting typing mode |
+
+You can extend the `PasswordInput` with additional navigation actions or override the default behavior using the `onAction` prop.
+For example, you can bind the visibility toggle to a custom action like:
+
+```tsx
+import PasswordInput, { PasswordInputRef } from '@components/Basic/PasswordInput/PasswordInput';
+
+const App = () => {
+ let inputRef!: PasswordInputRef;
+
+ return (
+ {
+ inputRef.visible() ? inputRef.hide() : inputRef.show();
+ }
+ }}
+ />
+ );
+};
+```
+
+For more information about navigation actions, see the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions).
+
## Guide
### Toggle input visibility programmatically
diff --git a/docs/src/content/docs/components/Basic/radio.mdx b/docs/src/content/docs/components/Basic/radio.mdx
index 5fcdf3bb..926b5ec4 100644
--- a/docs/src/content/docs/components/Basic/radio.mdx
+++ b/docs/src/content/docs/components/Basic/radio.mdx
@@ -40,7 +40,9 @@ export default App;
| `ref` | `RadioRef \| undefined` | `undefined` | A reference to the component that gives you access to its methods and the underlying HTML element. Useful if you need to set the selected option programmatically. |
| `disabled` | `boolean` | `false` | Specify if the radio is disabled. |
| `class-disabled` | `string` | `""` | Additional CSS classes to apply to the component when disabled. |
-| `onChange` | `(selected: string) => void | `undefined` | A function that is called every time the selected option changes. It can be used to retrieve the selected option's value. |
+| `onChange` | `(selected: string) => void` | `undefined` | A function that is called every time the selected option changes. It can be used to retrieve the selected option's value. |
+| `onAction` | `Record void>` | `undefined` | Extends or overrides the component's default navigation action handlers. See [Implemented Navigation Actions](#implemented-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, the radio's actions will execute. Can be a CSS selector or HTMLElement. |
## Ref API
@@ -58,6 +60,45 @@ To interact with the `Radio` programmatically, you can use the `RadioRef` interf
| Method | Parameters | Return Value | Description |
|----------------|-------------------|--------------|--------------------------------------------------|
| `changeOption` | `newOption: string`| `void` | Programmatically change the selected option. |
+| `changeSelected` | direction: `prev` \| `next` | `void` | Programmatically change the selected option to the previous or next one. |
+
+## Implemented Navigation Actions
+
+The `Radio` component implements the following navigation actions by default:
+
+| Action Name | Behavior |
+|--------------|-----------------------------------------------|
+| `move-left` | Moves the selected option to the previous one |
+| `move-right` | Moves the selected option to the next one |
+
+You can extend the `Radio` with additional navigation actions or override the default behavior using the `onAction` prop:
+
+```tsx
+import Radio from '@components/Basic/RadioGroup/Radio';
+
+ console.log('Radio button confirmed!'),
+ 'move-left': () => console.log('Custom previous logic') // overrides default move-left behavior
+ }}
+>
+ Option 1
+ Option 2
+
+```
+
+Using an `anchor` element lets the `Radio` respond to navigation actions when another element is focused:
+
+```tsx
+
+
Select Difficulty:
+
+ Easy
+ Normal
+ Hard
+
+
+```
## Guide
diff --git a/docs/src/content/docs/components/Basic/rounded-button.mdx b/docs/src/content/docs/components/Basic/rounded-button.mdx
index 113c704d..4f27eeb6 100644
--- a/docs/src/content/docs/components/Basic/rounded-button.mdx
+++ b/docs/src/content/docs/components/Basic/rounded-button.mdx
@@ -37,6 +37,8 @@ export default App;
| `disabled` | `boolean` | `false` | Specify if the button is disabled |
| `size` | `'large' \| 'middle' \| 'small'` | `''` | Specify the size of the button. If an empty string is passed, the button won't have any size. In that case, please specify the size through the `class` or `style` properties. |
| `textFit` | `boolean` | `true` | Specify if the text inside the button should be fitted. By default, this option is **enabled**. |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the button. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, the button's actions will execute. Can be a CSS selector or HTMLElement. |
## Guide
diff --git a/docs/src/content/docs/components/Basic/segment.mdx b/docs/src/content/docs/components/Basic/segment.mdx
index e976a6fb..ba720cc0 100644
--- a/docs/src/content/docs/components/Basic/segment.mdx
+++ b/docs/src/content/docs/components/Basic/segment.mdx
@@ -42,7 +42,9 @@ export default App;
| `disabled` | `boolean` | `false` | Specify if the Segment is disabled. |
| `class-selected` | `string` | `""` | Additional CSS classes applied when the button option is selected. |
| `class-disabled` | `string` | `""` | Additional CSS classes to apply to the component when disabled. |
-| `onChange` | `(selected: string) => void | `undefined` | A function that is called every time the selected option changes. It can be used to retrieve the selected option's value. |
+| `onChange` | `(selected: string) => void` | `undefined` | A function that is called every time the selected option changes. It can be used to retrieve the selected option's value. |
+| `onAction` | `Record void>` | `undefined` | Extends or overrides the component's default navigation action handlers. See [Implemented Navigation Actions](#implemented-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, the segment's actions will execute. Can be a CSS selector or HTMLElement. |
## Ref API
@@ -60,6 +62,7 @@ To interact with the `Segment` programmatically, you can use the `SegmentRef` in
| Method | Parameters | Return Value | Description |
|----------------|-------------------|--------------|--------------------------------------------------|
| `selectOption` | `newOption: string`| `void` | Programmatically change the selected option. |
+| `changeSelected` | direction: `prev` \| `next` | `void` | Programmatically change the selected option to the previous or next one. |
## Slots
@@ -113,6 +116,44 @@ const App = () => {
export default App;
```
+## Implemented Navigation Actions
+
+The `Segment` component implements the following navigation actions by default:
+
+| Action Name | Behavior |
+|--------------|-----------------------------------------------|
+| `move-left` | Moves the selected option to the previous one |
+| `move-right` | Moves the selected option to the next one |
+
+You can extend the `Segment` with additional navigation actions or override the default behavior using the `onAction` prop:
+
+```tsx
+import Segment from '@components/Basic/Segment/Segment';
+
+ console.log('Segment item confirmed!'),
+ 'move-left': () => console.log('Custom previous logic') // overrides default move-left behavior
+ }}
+>
+ Option 1
+ Option 2
+
+```
+
+Using an `anchor` element lets the `Segment` respond to navigation actions when another element is focused:
+
+```tsx
+
+
Select Difficulty:
+
+ Easy
+ Normal
+ Hard
+
+
+```
+
## Guide
### Retrieve the value of the selected option when it changes
diff --git a/docs/src/content/docs/components/Basic/slider.mdx b/docs/src/content/docs/components/Basic/slider.mdx
index 40575b89..8f016c9d 100644
--- a/docs/src/content/docs/components/Basic/slider.mdx
+++ b/docs/src/content/docs/components/Basic/slider.mdx
@@ -36,6 +36,8 @@ export default App;
| `max` | `number` | `undefined` | The maximum value that the slider can select. |
| `step` | `number` | `undefined` | The amount by which the slider value changes when the handle is moved. Determines the granularity of the slider. |
| `onChange` | `(value: number) => void` | `undefined` | A function that is called every time the slider's value has changed. It can be used to retrieve the up to date value of the slider. |
+| `onAction` | `Record void>` | `undefined` | Extends or overrides the component's default navigation action handlers. See [Implemented Navigation Actions](#implemented-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, the slider's actions will execute. Can be a CSS selector or HTMLElement. |
## Ref API
@@ -216,9 +218,37 @@ export default App;
```
:::caution
-The `Slider.Grid` slot will only be rendered if you include it as a child of the `Slider` component.
+The `Slider.Grid` slot will only be rendered if you include it as a child of the `Slider` component.
:::
+## Implemented Navigation Actions
+
+The `Slider` component implements the following navigation actions by default:
+
+| Action Name | Behavior |
+|--------------|-------------------------------------------------------------|
+| `move-left` | Decreases the slider value by the `step` amount |
+| `move-right` | Increases the slider value by the `step` amount |
+
+You can extend the `Slider` with additional navigation actions or override the default behavior using the `onAction` prop:
+
+```tsx
+import Slider from '@components/Basic/Slider/Slider';
+
+ console.log('Slider confirmed!'),
+ 'back': () => console.log('Going back')
+ }}
+/>
+```
+
+For more information about navigation actions, see the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions).
+
## Guide
### Retrieve and use the slider value
diff --git a/docs/src/content/docs/components/Basic/stepper.mdx b/docs/src/content/docs/components/Basic/stepper.mdx
index e38d24f6..051cb43a 100644
--- a/docs/src/content/docs/components/Basic/stepper.mdx
+++ b/docs/src/content/docs/components/Basic/stepper.mdx
@@ -41,6 +41,8 @@ export default App;
| `controls-position` | `'before' \| 'after'` | `''` | Determines the position of the control arrows. If not set, the selected option appears between the arrows. If set to `'before'`, the arrows appear before the selected option; if `'after'`, they appear after it. |
| `loop` | `boolean` | `false` | Enables looping through options when navigating past the first or last option using the controls. |
| `onChange` | `(value: string) => void` | `undefined` | Callback function triggered whenever the selected option changes, providing the selected option's value. |
+| `onAction` | `Record void>` | `undefined` | Extends or overrides the component's default navigation action handlers. See [Implemented Navigation Actions](#implemented-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, the stepper's actions will execute. Can be a CSS selector or HTMLElement. |
## Ref API
@@ -150,6 +152,36 @@ const App = () => {
};
```
+## Implemented Navigation Actions
+
+The Stepper component implements the following navigation actions by default:
+
+| Action Name | Behavior |
+|--------------|-----------------------------------------------|
+| `move-left` | Moves to the previous option in the stepper |
+| `move-right` | Moves to the next option in the stepper |
+
+You can extend the Stepper with additional navigation actions or override the default behavior using the `onAction` prop:
+
+```tsx
+import Stepper from '@components/Basic/Stepper/Stepper';
+
+ console.log('Current option selected!'),
+ 'back': () => console.log('Going back from stepper')
+ }}
+>
+
+ red
+ green
+ blue
+
+
+```
+
+For more information about navigation actions, see the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions).
+
## Guide
### Retrieve the selected option's value on change
diff --git a/docs/src/content/docs/components/Basic/text-block.mdx b/docs/src/content/docs/components/Basic/text-block.mdx
index a2d3a97c..e8bbc82b 100644
--- a/docs/src/content/docs/components/Basic/text-block.mdx
+++ b/docs/src/content/docs/components/Basic/text-block.mdx
@@ -33,6 +33,8 @@ export default App;
| `style` | `JSX.CSSProperties` | `{}` | Styles applied directly to the root element of the component.. |
| `class` | `string` | `""` | Additional CSS classes to apply to the component. |
| `ref` | `HTMLButtonElement \| undefined` | `undefined` | Obtains the component's DOM element and assigns it to a variable, allowing direct access to the HTML element via the ref object. |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the component. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, the component's actions will execute. Can be a CSS selector or HTMLElement. |
## Guide
diff --git a/docs/src/content/docs/components/Basic/text-input.mdx b/docs/src/content/docs/components/Basic/text-input.mdx
index a3b3d77e..e97ea7dd 100644
--- a/docs/src/content/docs/components/Basic/text-input.mdx
+++ b/docs/src/content/docs/components/Basic/text-input.mdx
@@ -39,6 +39,8 @@ export default App;
| `max-symbols` | `number` | `undefined` | Maximum number of symbols the input can accept. |
| `class-disabled` | `string` | `undefined` | Optional class to apply when the input is disabled. |
| `onChange` | `(value: string) => void` | `undefined` | A function that is called every time the input's value has changed. It can be used to retrieve the up to date value of the input. |
+| `onAction` | `Record void>` | `undefined` | Extends or overrides the component's default navigation action handlers. See [Implemented Navigation Actions](#implemented-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, the input's actions will execute. Can be a CSS selector or HTMLElement. |
## Ref API
@@ -178,6 +180,30 @@ const App = () => {
export default App;
```
+## Implemented Navigation Actions
+
+The `TextInput` component implements the following navigation actions by default:
+
+| Action Name | Behavior |
+|-------------|---------------------------------------------------------|
+| `select` | Focuses the input element, making it ready for typing |
+| `back` | Blurs the input element, exiting typing mode |
+
+You can extend the `TextInput` with additional navigation actions or override the default behavior using the `onAction` prop:
+
+```tsx
+import TextInput from '@components/Basic/TextInput/TextInput';
+
+ console.log('Custom select action!'),
+ 'move-up': () => console.log('Move up pressed')
+ }}
+/>
+```
+
+For more information about navigation actions, see the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions).
+
## Guide
### Retrieve the input value
diff --git a/docs/src/content/docs/components/Basic/text-slider.mdx b/docs/src/content/docs/components/Basic/text-slider.mdx
index f0d08496..ce42eef6 100644
--- a/docs/src/content/docs/components/Basic/text-slider.mdx
+++ b/docs/src/content/docs/components/Basic/text-slider.mdx
@@ -31,6 +31,8 @@ export default App;
| `value` | `string` | `undefined` | The current value of the slider. Use this to control or set the slider's position. |
| `values` | `string[]` | `undefined` | An array of options that can be selected by dragging the slider's handle. |
| `onChange` | `(value: string) => void` | `undefined` | A callback function triggered whenever the slider's value changes. It provides the updated value of the slider. |
+| `onAction` | `Record void>` | `undefined` | Extends or overrides the component's default navigation action handlers. See [Implemented Navigation Actions](#implemented-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, the slider's actions will execute. Can be a CSS selector or HTMLElement. |
## Ref API
@@ -49,6 +51,7 @@ To interact with the `TextSlider` programmatically, you can use the `TextSliderR
| Method | Parameters | Return Value | Description |
|----------------|-------------------|--------------|--------------------------------------------------|
| `changeValue` | `newValue: string`| `void` | Programmatically sets a new value for the text slider. |
+| `stepValue` | `direction: 1 \| -1` | `string` | Steps to the next (`1`) or previous (`-1`) value in the values array. |
## Slots
@@ -192,6 +195,32 @@ const App = () => {
export default App;
```
+## Implemented Navigation Actions
+
+The `TextSlider` component implements the following navigation actions by default:
+
+| Action Name | Behavior |
+|--------------|-------------------------------------------------------------|
+| `move-left` | Moves to the previous value in the values array |
+| `move-right` | Moves to the next value in the values array |
+
+You can extend the `TextSlider` with additional navigation actions or override the default behavior using the `onAction` prop:
+
+```tsx
+import TextSlider from '@components/Basic/TextSlider/TextSlider';
+
+ console.log('Value selected!'),
+ 'back': () => console.log('Going back')
+ }}
+/>
+```
+
+For more information about navigation actions, see the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions).
+
## Guide
### Retrieve and use the text slider value
diff --git a/docs/src/content/docs/components/Basic/toggle-button.mdx b/docs/src/content/docs/components/Basic/toggle-button.mdx
index 4e7dadaa..5d03eb43 100644
--- a/docs/src/content/docs/components/Basic/toggle-button.mdx
+++ b/docs/src/content/docs/components/Basic/toggle-button.mdx
@@ -37,6 +37,8 @@ export default App;
| `class-disabled`| `string` | `""` | Additional CSS classes applied when the toggle button is disabled. |
| `checked` | `boolean` | `false` | Specifies the initial checked state of the toggle button. |
| `onChange` | `(checked: boolean) => void`| `undefined` | Callback function triggered whenever the toggle button is toggled, providing the current checked state. |
+| `onAction` | `Record void>` | `undefined` | Extends or overrides the component's default navigation action handlers. See [Implemented Navigation Actions](#implemented-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, the toggle button's actions will execute. Can be a CSS selector or HTMLElement. |
## Ref API
@@ -219,6 +221,32 @@ const App = () => {
export default App;
```
+## Implemented Navigation Actions
+
+The `ToggleButton` component implements the following navigation actions by default:
+
+| Action Name | Behavior |
+|-------------|-----------------------------------|
+| `select` | Toggles the button on/off |
+
+You can extend the `ToggleButton` with additional navigation actions or override the default behavior using the `onAction` prop:
+
+```tsx
+import ToggleButton from '@components/Basic/ToggleButton/ToggleButton';
+
+ console.log('Going back from toggle button'),
+ 'select': () => console.log('Custom toggle logic') // Overrides default
+ }}
+>
+ Off
+ On
+
+```
+
+For more information about navigation actions, see the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions).
+
## Guide
### Retrieve the ToggleButton value
diff --git a/docs/src/content/docs/components/Basic/xy-slider.mdx b/docs/src/content/docs/components/Basic/xy-slider.mdx
index cd881328..47ace847 100644
--- a/docs/src/content/docs/components/Basic/xy-slider.mdx
+++ b/docs/src/content/docs/components/Basic/xy-slider.mdx
@@ -36,7 +36,10 @@ export default App;
| `minY` | `number` | `0` | The minimum value that the slider can select on the Y-axis.|
| `maxX` | `number` | `100` | The maximum value that the slider can select on the X-axis. |
| `maxY` | `number` | `100` | The maximum value that the slider can select on the Y-axis. |
+| `step` | `number` | `1` | The base unit for value changes. Used primarily for gamepad inputs to calculate movement speed and precision. |
| `onChange` | `(value: {x:number, y: number}) => void` | `undefined` | A function that is called every time the slider's value has changed. It can be used to retrieve the up to date value of the slider. |
+| `onAction` | `Record void>` | `undefined` | Extends or overrides the component's default navigation action handlers. See [Implemented Navigation Actions](#implemented-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, the stepper's actions will execute. Can be a CSS selector or HTMLElement. |
## Ref API
@@ -117,6 +120,34 @@ const App = () => {
export default App;
```
+## Implemented Navigation Actions
+
+The `XYSlider` component implements the following navigation actions by default:
+
+| Action Name | Behavior |
+|--------------|-----------------------------------------------|
+| `pan` | Moves the slider handle based on 2D axis input. Includes acceleration and smoothing logic. |
+
+:::note
+The XYSlider uses the `step` prop to determine base movement speed. It employs a dynamic acceleration system where pushing the
+stick further increases the speed multiplier, allowing for both precise adjustments (slight tilt) and rapid movement (full tilt).
+[Read more here](#adjusting-gamepad-movement-speed-and-precision)
+:::
+
+You can extend the XYSlider with additional navigation actions or override the default behavior using the `onAction` prop:
+
+```tsx
+import XYSlider from '@components/Basic/XYSlider/XYSlider';
+
+ console.log('Selecting XYSlider'),
+ 'pan': (_, axis: [number, number]) => console.log(`x: ${axis[0]}, y: ${axis[1]}`),
+ }} />
+```
+
+For more information about navigation actions, see the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions).
+
## Guide
### Retrieve and use the slider value
@@ -175,4 +206,21 @@ const App = () => {
};
export default App;
-```
\ No newline at end of file
+```
+
+### Adjusting gamepad movement speed and precision
+
+The `XYSlider` is designed to work out-of-the-box with the `pan` action of the `Navigation` component which uses the right analogue stick of the gamepad.
+
+To tune the feel of the gamepad control, adjust the step prop.
+
+- A smaller step (e.g., 1) allows for pixel-perfect precision but might be slow on large ranges.
+- A larger step (e.g., 10) makes the slider faster but less precise.
+
+```ts
+ // Increases base speed for large range
+```
+
+:::note
+If you are working with very small values (e.g., 0.0 to 1.0), consider using a smaller step (e.g., 0.1) to maintain precision.
+:::
\ No newline at end of file
diff --git a/docs/src/content/docs/components/Feedback/tooltip.mdx b/docs/src/content/docs/components/Feedback/tooltip.mdx
index 44c93cb8..f57458f7 100644
--- a/docs/src/content/docs/components/Feedback/tooltip.mdx
+++ b/docs/src/content/docs/components/Feedback/tooltip.mdx
@@ -49,6 +49,8 @@ Creates a new tooltip component with customizable options.
| `content` | `JSX.Element` | `(props: { message: string }) => JSX.Element`| The content of the tooltip message. Can be a JSX element. If not specified, the `message` prop will be used to display a simple text message. |
| `position` | `'top' \| 'bottom' \| 'left' \| 'right' \| 'auto'`| `bottom` | Defines the position of the tooltip relative to the target element. If set to `auto`, the tooltip will automatically choose the best placement based on available space. |
| `action` | `'hover' \| 'click' \| 'focus'` | `hover` | Determines the user interaction that triggers the tooltip. `hover`: shows on mouse hover and hides on mouse leave. `click`: toggles visibility when the target is clicked. `focus`: shows when the target gains focus (e.g., via keyboard) and hides when it loses focus. |
+| `onAction` | `Record void>` | `undefined` | Extends or overrides the component's default navigation action handlers. See [Implemented Navigation Actions](#implemented-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, the tooltip's actions will execute. Can be a CSS selector or HTMLElement. |
## Ref API
@@ -67,6 +69,36 @@ To interact with the `Tooltip` programmatically, you can use the `TooltipRef` in
| `show` | none | `void` | Shows the tooltip if it's hidden. |
| `hide` | none | `void` | Hides the tooltip if it's visible. |
+## Implemented Navigation Actions
+
+The `Tooltip` component implements the following navigation action by default:
+
+| Action Name | Behavior |
+|-------------|---------------------------------------------|
+| `back` | Dismisses the visible tooltip |
+
+This allows users to dismiss tooltips using keyboard or gamepad navigation when the tooltip's target element is focused.
+
+```tsx
+import createTooltip from '@components/Feedback/Tooltip/tooltip';
+
+const App = () => {
+ // Tooltip with default back action - dismisses on back button press
+ const Tooltip = createTooltip({ action: 'focus' });
+
+ return (
+
+
Focus me and press ESC
+
+ );
+};
+```
+
+:::caution[Navigation Actions Requirements]
+For navigation actions to work, the tooltip must use `action: 'focus'` and the wrapped element must be focusable (set `tabindex={0}` or higher on the wrapped element).
+:::
+
+For more information about navigation actions, see the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions).
## Guide
diff --git a/docs/src/content/docs/components/Layout/absolute.mdx b/docs/src/content/docs/components/Layout/absolute.mdx
index 71380a68..50202959 100644
--- a/docs/src/content/docs/components/Layout/absolute.mdx
+++ b/docs/src/content/docs/components/Layout/absolute.mdx
@@ -35,8 +35,10 @@ export default App;
|---|---|---|---|
| `style` | `JSX.CSSProperties` | `{}` | Inline styles to apply directly to the component's root element. |
| `class` | `string` | `""` | Additional CSS classes to apply to the component |
-| `ref` | `BaseComponentRef` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. The HTML element can be accessed using the `element` property of the returned ref object. |
+| `ref` | `HTMLDivElement` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. |
| `top`, `left`, `right`, `bottom` | `string` | `""` | Specify the position offset of the `Absolute` component. It accepts any valid css unit value |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the component. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, this component's actions will execute. Can be a CSS selector or HTMLElement. |
## Guide
@@ -73,7 +75,7 @@ export default App;
To access the HTML DOM element of the `Absolute` component.
1. Declare a variable to hold the ref but don't initialize it with a value
-2. The declared value should have a type of `BaseComponentRef`, which you need to import
+2. The declared value should have a type of `HTMLDivElement`.
3. Set the declared variable as the value of the `ref` prop of the `Absolute` component
#### Example
@@ -82,10 +84,9 @@ To access the HTML DOM element of the `Absolute` component.
import Relative from '@components/Layout/Relative/Relative';
import Absolute from '@components/Layout/Absolute/Absolute';
import Block from '@components/Layout/Block/Block';
-import { BaseComponentRef } from '@components/types/ComponentProps';
const App = () => {
- let absoluteRef!: BaseComponentRef
+ let absoluteRef!: HTMLDivElement;
return (
@@ -99,4 +100,4 @@ const App = () => {
export default App;
```
-Now you can access the HTML element of `absoluteRef` with `absoluteRef.element` and make modifications to it if needed.
\ No newline at end of file
+Now you can access the HTML element of the component with `absoluteRef` and make modifications to it if needed.
\ No newline at end of file
diff --git a/docs/src/content/docs/components/Layout/block.mdx b/docs/src/content/docs/components/Layout/block.mdx
index b22b0042..816df3fc 100644
--- a/docs/src/content/docs/components/Layout/block.mdx
+++ b/docs/src/content/docs/components/Layout/block.mdx
@@ -30,7 +30,9 @@ export default App;
|---|---|---|---|
| `style` | `JSX.CSSProperties` | `{}` | Inline styles to apply directly to the component's root element. |
| `class` | `string` | `""` | Additional CSS classes to apply to the component |
-| `ref` | `BaseComponentRef` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. The HTML element can be accessed using the `element` property of the returned ref object. |
+| `ref` | `HTMLDivElement` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the component. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, this component's actions will execute. Can be a CSS selector or HTMLElement. |
## Guide
@@ -39,7 +41,7 @@ export default App;
To access the HTML DOM element of the `Block` component.
1. Declare a variable to hold the ref but don't initialize it with a value
-2. The declared value should have a type of `BaseComponentRef`, which you need to import
+2. The declared value should have a type of `HTMLDivElement`
3. Set the declared variable as the value of the `ref` prop of the `Block` component
#### Example
@@ -47,17 +49,16 @@ To access the HTML DOM element of the `Block` component.
```tsx
import Bottom from '@components/Layout/Bottom/Bottom';
import Block from '@components/Layout/Block/Block';
-import { BaseComponentRef } from '@components/types/ComponentProps';
const App = () => {
- let BlockRef!: BaseComponentRef
+ let blockRef!: HTMLDivElement
return (
-
+ Bottom Section
);
};
```
-Now you can access the HTML element of `Block` with `BlockRef.element` and make modifications to it if needed.
\ No newline at end of file
+Now you can access the HTML element of `Block` with `blockRef` and make modifications to it if needed.
\ No newline at end of file
diff --git a/docs/src/content/docs/components/Layout/bottom.mdx b/docs/src/content/docs/components/Layout/bottom.mdx
index 68f08415..315b0138 100644
--- a/docs/src/content/docs/components/Layout/bottom.mdx
+++ b/docs/src/content/docs/components/Layout/bottom.mdx
@@ -34,8 +34,10 @@ export default App;
|---|---|---|---|
| `style` | `JSX.CSSProperties` | `{}` | Inline styles to apply directly to the component's root element. |
| `class` | `string` | `""` | Additional CSS classes to apply to the component |
-| `ref` | `BaseComponentRef` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. The HTML element can be accessed using the `element` property of the returned ref object. |
+| `ref` | `HTMLDivElement` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. |
| `basis` | `number` | `15%` | Sets the flex-basis of the component as a percentage. For example, a value of `30` sets the flex-basis to `30%`. |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the component. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, this component's actions will execute. Can be a CSS selector or HTMLElement. |
## Guide
@@ -44,7 +46,7 @@ export default App;
To access the HTML DOM element of the `Bottom` component.
1. Declare a variable to hold the ref but don't initialize it with a value
-2. The declared value should have a type of `BaseComponentRef`, which you need to import
+2. The declared value should have a type of `HTMLDivElement`.
3. Set the declared variable as the value of the `ref` prop of the `Bottom` component
#### Example
@@ -54,13 +56,12 @@ import Layout from '@components/Layout/Layout/Layout';
import Top from '@components/Layout/Top/Top';
import Content from '@components/Layout/Content/Content';
import Bottom from '@components/Layout/Bottom/Bottom';
-import { BaseComponentRef } from '@components/types/ComponentProps';
const App = () => {
- let bottomRef!: BaseComponentRef
+ let bottomRef!: HTMLDivElement
return (
-
+ Top SectionMain ContentBottom Section
@@ -69,4 +70,4 @@ const App = () => {
};
```
-Now you can access the HTML element of `bottomRef` with `bottomRef.element` and make modifications to it if needed.
\ No newline at end of file
+Now you can access the HTML element of `Bottom` with `bottomRef` and make modifications to it if needed.
\ No newline at end of file
diff --git a/docs/src/content/docs/components/Layout/column.mdx b/docs/src/content/docs/components/Layout/column.mdx
index 9652f519..676e5802 100644
--- a/docs/src/content/docs/components/Layout/column.mdx
+++ b/docs/src/content/docs/components/Layout/column.mdx
@@ -60,7 +60,9 @@ export default App;
|---|---|---|---|
| `style` | `JSX.CSSProperties` | `{}` | Inline styles to apply directly to the component's root element. |
| `class` | `string` | `""` | Additional CSS classes to apply to the component |
-| `ref` | `BaseComponentRef` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. The HTML element can be accessed using the `element` property of the returned ref object. |
+| `ref` | `HTMLDivElement` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the component. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, this component's actions will execute. Can be a CSS selector or HTMLElement. |
## Guide
@@ -99,21 +101,20 @@ export default App;
### Accessing the HTML element
-To access the HTML DOM element of the `Row` component.
+To access the HTML DOM element of the `Column` component.
1. Declare a variable to hold the ref but don't initialize it with a value
-2. The declared value should have a type of `BaseComponentRef`, which you need to import
-3. Set the declared variable as the value of the `ref` prop of the `Row` component
+2. The declared value should have a type of `HTMLDivElement`.
+3. Set the declared variable as the value of the `ref` prop of the `Column` component
#### Example
```tsx
-import { BaseComponentRef } from '@components/types/ComponentProps';
import Row from '@components/Layout/Row/Row';
import { Column } from '@components/Layout/Column/Column'
const App = () => {
- let columnRef!: BaseComponentRef
+ let columnRef!: HTMLDivElement
return (
@@ -125,4 +126,4 @@ const App = () => {
export default App;
```
-Now you can access the HTML element of `columnRef` with `columnRef.element` and make modifications to it if needed.
\ No newline at end of file
+Now you can access the HTML element of `Column` with `columnRef` and make modifications to it if needed.
\ No newline at end of file
diff --git a/docs/src/content/docs/components/Layout/content.mdx b/docs/src/content/docs/components/Layout/content.mdx
index 6b404db8..d6cc9d6a 100644
--- a/docs/src/content/docs/components/Layout/content.mdx
+++ b/docs/src/content/docs/components/Layout/content.mdx
@@ -34,8 +34,10 @@ export default App;
|---|---|---|---|
| `style` | `JSX.CSSProperties` | `{}` | Inline styles to apply directly to the component's root element. |
| `class` | `string` | `""` | Additional CSS classes to apply to the component |
-| `ref` | `BaseComponentRef` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. The HTML element can be accessed using the `element` property of the returned ref object. |
+| `ref` | `HTMLDivElement` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. |
| `basis` | `number` | `auto` | Sets the flex-basis of the component as a percentage. For example, a value of `30` sets the flex-basis to `30%`. |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the component. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, this component's actions will execute. Can be a CSS selector or HTMLElement. |
## Guide
@@ -44,7 +46,7 @@ export default App;
To access the HTML DOM element of the `Content` component.
1. Declare a variable to hold the ref but don't initialize it with a value
-2. The declared value should have a type of `BaseComponentRef`, which you need to import
+2. The declared value should have a type of `HTMLDivElement`.
3. Set the declared variable as the value of the `ref` prop of the `Content` component
#### Example
@@ -54,13 +56,12 @@ import Layout from '@components/Layout/Layout/Layout';
import Top from '@components/Layout/Top/Top';
import Content from '@components/Layout/Content/Content';
import Bottom from '@components/Layout/Bottom/Bottom';
-import { BaseComponentRef } from '@components/types/ComponentProps';
const App = () => {
- let contentRef!: BaseComponentRef
+ let contentRef!: HTMLDivElement
return (
-
+ Top SectionMain ContentBottom Section
@@ -69,4 +70,4 @@ const App = () => {
};
```
-Now you can access the HTML element of `contentRef` with `contentRef.element` and make modifications to it if needed.
\ No newline at end of file
+Now you can access the HTML element of `Content` with `contentRef` and make modifications to it if needed.
\ No newline at end of file
diff --git a/docs/src/content/docs/components/Layout/flex.mdx b/docs/src/content/docs/components/Layout/flex.mdx
index 2b6b42e8..d304874b 100644
--- a/docs/src/content/docs/components/Layout/flex.mdx
+++ b/docs/src/content/docs/components/Layout/flex.mdx
@@ -38,12 +38,14 @@ export default App;
|---|---|---|---|
| `style` | `JSX.CSSProperties` | `{}` | Inline styles to apply directly to the component's root element. |
| `class` | `string` | `""` | Additional CSS classes to apply to the component |
-| `ref` | `BaseComponentRef` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. The HTML element can be accessed using the `element` property of the returned ref object. |
+| `ref` | `HTMLDivElement` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. |
| `direction` | `"row"`, `"row-reverse"`, `"column"`, `"column-reverse"` | `"row"` | Specifies the CSS `flex-direction` property, which determines the main axis direction (horizontal or vertical) and the order of items. For example, `"row"` arranges items left-to-right, while `"column-reverse"` arranges them bottom-to-top. |
| `wrap` | `"nowrap"`, `"wrap-reverse"`, `"wrap"` | `"nowrap"` | Specifies the CSS `wrap` property, which controls whether flex items wrap onto multiple lines. For example, `"wrap"` allows items to flow onto new rows or columns, while `"nowrap"` keeps them on a single line. |
| `justify-content` | `"start"`, `"center"`, `"end"`, `"space-between"`, `"space-around"` | `"start"` | Specifies the CSS `justify-content` property, which aligns items along the main axis (horizontal in row and vertical in column). Options like `"space-between"` distribute items with equal gaps, while `"center"` centers them. |
| `align-items` | `"start"`, `"center"`, `"end"`, `"stretch"` | `"stretch"` | Specifies the CSS `align-items` property, which controls the alignment of items along the cross axis (perpendicular to the main axis). For example, `"stretch"` makes all items stretch to fill the container, while `"center"` aligns them in the middle. |
| `align-content` | `"start"`, `"center"`, `"end"`, `"stretch"` | `"stretch"` | Specifies the CSS `align-content` property, which controls the spacing between rows or columns in a multi-line flex container. For example, `"space-between"` evenly distributes rows, while `"center"` aligns them in the middle of the container. |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the component. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, this component's actions will execute. Can be a CSS selector or HTMLElement. |
## Guide
@@ -133,7 +135,7 @@ export default App;
To access the HTML DOM element of the `Flex` component.
1. Declare a variable to hold the ref but don't initialize it with a value
-2. The declared value should have a type of `BaseComponentRef`, which you need to import
+2. The declared value should have a type of `HTMLDivElement`.
3. Set the declared variable as the value of the `ref` prop of the `Flex` component
#### Example
@@ -141,10 +143,9 @@ To access the HTML DOM element of the `Flex` component.
```tsx
import Flex from '@components/Layout/Flex/Flex';
import Block from '@components/Layout/Block/Block';
-import { BaseComponentRef } from '@components/types/ComponentProps';
const App = () => {
- let flexRef!: BaseComponentRef
+ let flexRef!: HTMLDivElement
return (
@@ -156,5 +157,5 @@ const App = () => {
};
```
-Now you can access the HTML element of `Flex` with `flexRef.element` and make modifications to it if needed.
+Now you can access the HTML element of `Flex` with `flexRef` and make modifications to it if needed.
diff --git a/docs/src/content/docs/components/Layout/grid-tile.mdx b/docs/src/content/docs/components/Layout/grid-tile.mdx
index 699d6701..50328492 100644
--- a/docs/src/content/docs/components/Layout/grid-tile.mdx
+++ b/docs/src/content/docs/components/Layout/grid-tile.mdx
@@ -33,9 +33,11 @@ export default App;
|---|---|---|---|
| `style` | `JSX.CSSProperties` | `{}` | Inline styles to apply directly to the component's root element. |
| `class` | `string` | `""` | Additional CSS classes to apply to the component |
-| `ref` | `BaseComponentRef` | `undefined` | Retrieves the `GridTile`'s properties, methods and HTML element and assigns them to a local variable |
+| `ref` | `GridTileRef` | `undefined` | Retrieves the `GridTile`'s properties, methods and HTML element and assigns them to a local variable |
| `row` | `number` | `Required` | The row index where the tile will be placed. |
| `col` | `number` | `Required` | The column index where the tile will be placed. |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the component. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, this component's actions will execute. Can be a CSS selector or HTMLElement. |
### Methods
|Method |Parameters |Return Value |Description |
@@ -85,4 +87,77 @@ const App = () => {
};
export default App;
+```
+
+### Making Grid Tiles navigable
+
+To make the `GridTile` component navigable via keyboard or gamepad, you can use the [Navigation](/components/utility/navigation/) component. There are two ways to achieve this.
+
+#### The Declarative Approach
+
+You can use the [Navigation.Area](/components/utility/navigation/#navigationarea) component to wrap your tiles, but you are required to use the `selector` prop.
+
+1. Assign a specific class name to your `GridTile` components.
+2. Pass that exact same class (without the dot) to the selector prop of the `Navigation.Area`.
+
+```tsx
+import Navigation from '@components/Utility/Navigation/Navigation';
+import Grid from '@components/Layout/Grid/Grid';
+import GridTile from '@components/Layout/Grid/GridTile';
+import Block from '@components/Layout/Block/Block';
+import styles from './MyPage.module.scss';
+
+const App = () => {
+ return (
+
+
+
+
+ Item 1
+
+
+
+ Item 2
+
+
+
+ Item 3
+
+
+
+
+ );
+};
+```
+
+:::danger[Selector is Mandatory]
+Unlike standard components, you cannot omit the selector prop for Grids.
+:::
+
+#### Alternative: Manual Registration With Callback Ref
+
+You can manually register the tiles as a navigable area using the [registerArea](/components/utility/navigation/#area-methods) method.
+
+1. Wrap the `Grid` (or a parent) with the `Navigation` component.
+2. Use the `ref` callback to access the `Navigation` API immediately.
+3. Register the specific class name of your list items using `registerArea`.
+
+```tsx
+import Navigation, { NavigationRef } from '@components/Utility/Navigation/Navigation';
+
+const App = () => {
+ const handleNavReady = (api: NavigationRef) => {
+ // Manually register the class used on the tiles
+ api.registerArea('grid-area', ['.my-grid-tile'], true);
+ };
+
+ return (
+
+
+ ...
+ ...
+
+
+ );
+}
```
\ No newline at end of file
diff --git a/docs/src/content/docs/components/Layout/grid.mdx b/docs/src/content/docs/components/Layout/grid.mdx
index 3a1f46cc..0fd61a71 100644
--- a/docs/src/content/docs/components/Layout/grid.mdx
+++ b/docs/src/content/docs/components/Layout/grid.mdx
@@ -35,9 +35,11 @@ export default App;
|---|---|---|---|
| `style` | `JSX.CSSProperties` | `{}` | Inline styles to apply directly to the component's root element. |
| `class` | `string` | `""` | Additional CSS classes to apply to the component |
-| `ref` | `BaseComponentRef` | `undefined` | Retrieves the grid's properties, methods and HTML element and assigns them to a local variable |
+| `ref` | `GridRef` | `undefined` | Retrieves the grid's properties, methods and HTML element and assigns them to a local variable |
| `rows` | `number` | `Required` | The number of rows in the grid. |
| `cols` | `number` | `Required` | The number of columns in the grid. |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the component. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, this component's actions will execute. Can be a CSS selector or HTMLElement. |
### Methods
|Method |Parameters |Return Value |Description |
diff --git a/docs/src/content/docs/components/Layout/layout-3d.mdx b/docs/src/content/docs/components/Layout/layout-3d.mdx
index 303a2073..54c84e0f 100644
--- a/docs/src/content/docs/components/Layout/layout-3d.mdx
+++ b/docs/src/content/docs/components/Layout/layout-3d.mdx
@@ -34,8 +34,10 @@ export default App;
|---|---|---|---|
| `style` | `JSX.CSSProperties` | `{}` | Inline styles to apply directly to the component's root element. |
| `class` | `string` | `""` | Additional CSS classes to apply to the component |
-| `ref` | `BaseComponentRef` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. The HTML element can be accessed using the `element` property of the returned ref object. |
+| `ref` | `HTMLDivElement` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. |
| `distance` | `string` | `0` | Defines the perspective distance (z-plane to user). Use valid CSS unit values (e.g., `px`, `em`, `%`). |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the component. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, this component's actions will execute. Can be a CSS selector or HTMLElement. |
## Guide
@@ -68,7 +70,7 @@ export default App;
To access the HTML DOM element of the `Layout3D` component.
1. Declare a variable to hold the ref but don't initialize it with a value
-2. The declared value should have a type of `BaseComponentRef`, which you need to import
+2. The declared value should have a type of `HTMLDivElement`.
3. Set the declared variable as the value of the `ref` prop of the `Layout3D` component
#### Example
@@ -76,10 +78,9 @@ To access the HTML DOM element of the `Layout3D` component.
```tsx
import Layout3D from '@components/Layout/Layout3D/Layout3D';
import Transform from '@components/Layout/Transform/Transform';
-import { BaseComponentRef } from '@components/types/ComponentProps';
const App = () => {
- let layout3DRef!: BaseComponentRef
+ let layout3DRef!: HTMLDivElement
return (
@@ -95,4 +96,4 @@ const App = () => {
export default App;
```
-Now you can access the HTML element of `Layout3D` with `layout3DRef.element` and make modifications to it if needed.
+Now you can access the HTML element of `Layout3D` with `layout3DRef` and make modifications to it if needed.
diff --git a/docs/src/content/docs/components/Layout/layout.mdx b/docs/src/content/docs/components/Layout/layout.mdx
index de81f3d5..36f9f9de 100644
--- a/docs/src/content/docs/components/Layout/layout.mdx
+++ b/docs/src/content/docs/components/Layout/layout.mdx
@@ -34,7 +34,9 @@ export default App;
|---|---|---|---|
| `style` | `JSX.CSSProperties` | `{}` | Inline styles to apply directly to the component's root element. |
| `class` | `string` | `""` | Additional CSS classes to apply to the component |
-| `ref` | `BaseComponentRef` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. The HTML element can be accessed using the `element` property of the returned ref object. |
+| `ref` | `HTMLDivElement` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the component. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, this component's actions will execute. Can be a CSS selector or HTMLElement. |
## Guide
@@ -43,7 +45,7 @@ export default App;
To access the HTML DOM element of the `Layout` component.
1. Declare a variable to hold the ref but don't initialize it with a value
-2. The declared value should have a type of `BaseComponentRef`, which you need to import
+2. The declared value should have a type of `HTMLDivElement`.
3. Set the declared variable as the value of the `ref` prop of the `Layout` component
#### Example
@@ -53,10 +55,9 @@ import Layout from '@components/Layout/Layout/Layout';
import Top from '@components/Layout/Top/Top';
import Content from '@components/Layout/Content/Content';
import Bottom from '@components/Layout/Bottom/Bottom';
-import { BaseComponentRef } from '@components/types/ComponentProps';
const App = () => {
- let layoutRef!: BaseComponentRef
+ let layoutRef!: HTMLDivElement
return (
@@ -68,4 +69,4 @@ const App = () => {
};
```
-Now you can access the HTML element of `Layout` with `layoutRef.element` and make modifications to it if needed.
+Now you can access the HTML element of `Layout` with `layoutRef` and make modifications to it if needed.
diff --git a/docs/src/content/docs/components/Layout/list.mdx b/docs/src/content/docs/components/Layout/list.mdx
index d6cbac02..36d75ff7 100644
--- a/docs/src/content/docs/components/Layout/list.mdx
+++ b/docs/src/content/docs/components/Layout/list.mdx
@@ -45,14 +45,15 @@ export default App;
| Prop Name | Type | Default | Description |
| --------------- | ---------------------------------------------------------------------------------------- | ------------- | ----------------------------------------------------------------------------------------------------------------- |
-| `ref` | `BaseComponentRef` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. The HTML element can be accessed using the `element` property of the returned ref object. |
+| `ref` | `HTMLDivElement` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. |
| `type` | `'ordered' \| 'unordered'` | `'unordered'` | Controls whether the list is ordered (numbered) or unordered (bulleted). Automatically set to `'ordered'` when `bullet-type` is `"number"`. |
| `bullet-type` | `'disc' \| 'circle' \| 'square' \| 'number' \| 'none' \| string \| ImageMetadata` | `'disc'` (unordered) / `'number'` (ordered) | Sets the bullet style for all items in the list. Can be a predefined style, or an image. |
| `bullet-class` | `string` | `""` | Additional CSS classes to apply to all bullet elements. |
| `style` | `JSX.CSSProperties` | `{}` | Inline styles to apply to the root element. |
| `class` | `string` | `""` | Additional CSS classes to apply to the root element. |
| `children` | `JSX.Element` | `""` | List items to render. It renders only `List.Item` components. |
-
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the component. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, this component's actions will execute. Can be a CSS selector or HTMLElement. |
## Slots
@@ -67,6 +68,8 @@ Use `List.Item` to define an item in the list. Items can contain any valid JSX c
| `style` | `JSX.CSSProperties` | `{}` | Inline styles for the individual list item. |
| `class` | `string` | `""` | Additional CSS classes for the item. |
| `children` | `JSX.Element` | `""` | Content of the list item. Supports nested `List` components and any other valid JSX content. |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the component. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, this component's actions will execute. Can be a CSS selector or HTMLElement. |
#### Usage
@@ -150,7 +153,7 @@ const App = () => {
| Prop Name | Type | Default | Description |
| ---------- | ------------------- | ------- | ------------------------------------------------------------------------------------------------------------------ |
-| `ref` | `BaseComponentRef` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. The HTML element can be accessed using the `element` property of the returned ref object. |
+| `ref` | `HTMLDivElement` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. |
| `class` | `string` | `""` | Additional CSS classes for **this icon only**. Overrides `bullet-class` from `List` for this item. |
| `style` | `JSX.CSSProperties` | `{}` | Inline styles for **this icon only**. Overrides styles from `List` for this item. |
| `children` | `JSX.Element` | `""` | Custom content to replace the bullet entirely for this item. If set, `bullet-type` and `bullet-class` are ignored. |
@@ -294,17 +297,16 @@ const App = () => {
To access the HTML DOM element of the `List` component.
1. Declare a variable to hold the ref but don't initialize it with a value
-2. The declared value should have a type of `BaseComponentRef`, which you need to import
+2. The declared value should have a type of `HTMLDivElement`.
3. Set the declared variable as the value of the `ref` prop of the `List` component
#### Example
```tsx
import List from '@components/Layout/List/List';
-import { BaseComponentRef } from '@components/types/ComponentProps';
const App = () => {
- let listRef!: BaseComponentRef
+ let listRef!: HTMLDivElement
return (
@@ -319,4 +321,82 @@ const App = () => {
You can access the element of every `List.Item` component in the same way.
:::
-Now you can access the HTML element of `List` with `listRef.element` and make modifications to it if needed.
\ No newline at end of file
+Now you can access the HTML element of `List` with `listRef` and make modifications to it if needed.
+
+### Making list items navigable
+
+To make the `List` component navigable via keyboard or gamepad, you can use the [Navigation](/components/utility/navigation/) component. There are two ways to achieve this.
+
+#### Approach 1: The Callback Ref
+
+You can manually register the list items using the [registerArea](/components/utility/navigation/#area-methods) method.
+
+1. Wrap the `List` (or a parent) with the `Navigation` component.
+2. Use the `ref` callback to access the `Navigation` API immediately.
+3. Register the specific class name of your list items using `registerArea`.
+
+```tsx
+import List from '@components/Layout/List/List';
+import Navigation, {NavigationRef} from '@components/Utility/Navigation/Navigation';
+import { For } from 'solid-js';
+
+const App = () => {
+ // 1. Use a callback to register the area immediately when Navigation initializes
+ const handleNavRef = (api: NavigationRef) => {
+ // Note the dot (.) prefix for the class selector
+ api.registerArea('list-items', ['.test'], true);
+ };
+
+ return (
+
+
+
+ {(item) => (
+ // 2. Add the matching class name to your items
+
+ {item.text}
+
+ )}
+
+
+
+ );
+}
+```
+
+#### Approach 2: The Wrapper Component (Declarative)
+
+Alternatively, you can wrap the `List` in a [Navigation.Area](/components/utility/navigation/#navigationarea) component.
+
+In this case, you must provide the `selector` prop to tell the area which inner elements are focusable.
+
+```tsx
+import List from '@components/Layout/List/List';
+import Navigation from '@components/Utility/Navigation/Navigation';
+import For from 'solid-js';
+
+const App = () => {
+ return (
+
+
+
+
+ {(item) => (
+ // Add the matching class name to your items
+
+ {item.text}
+
+ )}
+
+
+
+
+ );
+}
+```
+
+:::danger[Selector Required]
+When using `Navigation.Area` with a List, you must provide a valid class selector.
+
+If you omit the selector, the `Navigation` component will attempt to focus the direct children (the List wrapper itself), and your individual items will not be navigable.
+:::
\ No newline at end of file
diff --git a/docs/src/content/docs/components/Layout/relative.mdx b/docs/src/content/docs/components/Layout/relative.mdx
index 31a4acbf..05ccb52e 100644
--- a/docs/src/content/docs/components/Layout/relative.mdx
+++ b/docs/src/content/docs/components/Layout/relative.mdx
@@ -30,8 +30,10 @@ export default App;
|---|---|---|---|
| `style` | `JSX.CSSProperties` | `{}` | Inline styles to apply directly to the component's root element. |
| `class` | `string` | `""` | Additional CSS classes to apply to the component |
-| `ref` | `BaseComponentRef` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. The HTML element can be accessed using the `element` property of the returned ref object. |
+| `ref` | `HTMLDivElement` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. |
| `top`, `left`, `right`, `bottom` | `string` | `""` | Specify the offset of the `Relative` component. It accepts any valid css unit value |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the component. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, this component's actions will execute. Can be a CSS selector or HTMLElement. |
## Guide
@@ -92,17 +94,16 @@ export default App;
To access the HTML DOM element of the `Relative` component.
1. Declare a variable to hold the ref but don't initialize it with a value
-2. The declared value should have a type of `BaseComponentRef`, which you need to import
+2. The declared value should have a type of `HTMLDivElement`.
3. Set the declared variable as the value of the `ref` prop of the `Relative` component
#### Example
```tsx
-import { BaseComponentRef } from '@components/types/ComponentProps';
import Relative from '@components/Layout/Relative/Relative';
import Block from '@components/Layout/Block/Block';
-let relativeRef!: BaseComponentRef
+let relativeRef!: HTMLDivElement
const App = () => {
return (
@@ -115,4 +116,4 @@ const App = () => {
export default App;
```
-Now you can access the HTML element of `relativeRef` with `relativeRef.element` and make modifications to it if needed.
\ No newline at end of file
+Now you can access the HTML element of `Relative` with `relativeRef` and make modifications to it if needed.
\ No newline at end of file
diff --git a/docs/src/content/docs/components/Layout/row.mdx b/docs/src/content/docs/components/Layout/row.mdx
index e196f6b1..295674de 100644
--- a/docs/src/content/docs/components/Layout/row.mdx
+++ b/docs/src/content/docs/components/Layout/row.mdx
@@ -34,7 +34,9 @@ export default App;
|---|---|---|---|
| `style` | `JSX.CSSProperties` | `{}` | Inline styles to apply directly to the component's root element. |
| `class` | `string` | `""` | Additional CSS classes to apply to the component |
-| `ref` | `BaseComponentRef` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. The HTML element can be accessed using the `element` property of the returned ref object. |
+| `ref` | `HTMLDivElement` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the component. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, this component's actions will execute. Can be a CSS selector or HTMLElement. |
## Guide
@@ -67,18 +69,17 @@ export default App;
To access the HTML DOM element of the `Row` component.
1. Declare a variable to hold the ref but don't initialize it with a value
-2. The declared value should have a type of `BaseComponentRef`, which you need to import
+2. The declared value should have a type of `HTMLDivElement`.
3. Set the declared variable as the value of the `ref` prop of the `Row` component
#### Example
```tsx
-import { BaseComponentRef } from '@components/types/ComponentProps';
import Row from '@components/Layout/Row/Row';
import { Column } from '@components/Layout/Column/Column'
const App = () => {
- let rowRef!: BaseComponentRef;
+ let rowRef!: HTMLDivElement;
return (
@@ -90,4 +91,4 @@ const App = () => {
export default App;
```
-Now you can access the HTML element of `rowRef` with `rowRef.element` and make modifications to it if needed.
\ No newline at end of file
+Now you can access the HTML element of `Row` with `rowRef` and make modifications to it if needed.
\ No newline at end of file
diff --git a/docs/src/content/docs/components/Layout/scroll.mdx b/docs/src/content/docs/components/Layout/scroll.mdx
index fd706a75..4f5e52cc 100644
--- a/docs/src/content/docs/components/Layout/scroll.mdx
+++ b/docs/src/content/docs/components/Layout/scroll.mdx
@@ -39,6 +39,8 @@ In this example, the text is wrapped inside the `Scroll` component, and the maxi
| `class` | `string` | `""` | Additional CSS classes to apply to the scroll's wrapper element. |
| `ref` | `ScrollComponentRef \| undefined` | `undefined` | A reference to the component, providing access to its methods and the underlying HTML element. |
| `onScroll` | `(event: { scrollDirection: 'up' \| 'down' }) => void` | `undefined` | A callback function triggered whenever the scroll position changes. |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the component. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, this component's actions will execute. Can be a CSS selector or HTMLElement. |
## Ref API
@@ -174,6 +176,35 @@ const App = () => {
};
```
+
+## Implemented Navigation Actions
+
+The `Scroll` component implements the following navigation actions by default:
+
+| Action Name | Behavior |
+|-------------|-----------------------------------|
+| `pan` | Scrolls the scrollable container up or down depending on stick position. |
+
+You can extend the `Scroll` with additional navigation actions or override the default behavior using the `onAction` prop:
+
+```tsx
+import Scroll from '@components/Layout/Scroll/Scroll';
+
+ console.log('Custom select logic'),
+ 'pan': (_, axis: [number, number]) => console.log(`x: ${axis[0]}, y: ${axis[1]}`), // Overrides default
+ }}
+>
+
+ {/* Scrollable content goes here */}
+
+
+```
+
+For more information about navigation actions, see the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions).
+
+
## Guide
### Scrolling Programmatically
diff --git a/docs/src/content/docs/components/Layout/tab-link.mdx b/docs/src/content/docs/components/Layout/tab-link.mdx
index b9c01a9a..8c4de9ba 100644
--- a/docs/src/content/docs/components/Layout/tab-link.mdx
+++ b/docs/src/content/docs/components/Layout/tab-link.mdx
@@ -43,5 +43,7 @@ export default App;
|---|---|---|---|
| `style` | `JSX.CSSProperties` | `{}` | Inline styles to apply directly to the component's root element. |
| `class` | `string` | `""` | Additional CSS classes to apply to the component |
-| `ref` | `BaseComponentRef` | `undefined` | A reference to the component that gives you access to its methods and the underlying HTML element. Useful if you need to control scrolling programmatically. |
-| `location` | `string` | `undefined` | The target tab's identifier. This value must match the `location` prop of a corresponding `Tab` component. When clicked, the `TabLink` updates the active tab state in the `Tabs` component, making the associated `Tab` visible. |
\ No newline at end of file
+| `ref` | `HTMLDivElement` | `undefined` | A reference to the component that gives you access to the underlying HTML element. |
+| `location` | `string` | `undefined` | The target tab's identifier. This value must match the `location` prop of a corresponding `Tab` component. When clicked, the `TabLink` updates the active tab state in the `Tabs` component, making the associated `Tab` visible. |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the component. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, this component's actions will execute. Can be a CSS selector or HTMLElement. |
\ No newline at end of file
diff --git a/docs/src/content/docs/components/Layout/tab.mdx b/docs/src/content/docs/components/Layout/tab.mdx
index 5edc963d..608458c9 100644
--- a/docs/src/content/docs/components/Layout/tab.mdx
+++ b/docs/src/content/docs/components/Layout/tab.mdx
@@ -43,9 +43,11 @@ export default App;
|---|---|---|---|
| `style` | `JSX.CSSProperties` | `{}` | Inline styles to apply directly to the component's root element. |
| `class` | `string` | `""` | Additional CSS classes to apply to the component |
-| `ref` | `BaseComponentRef` | `undefined` | A reference to the component that gives you access to its methods and the underlying HTML element. Useful if you need to control scrolling programmatically. |
+| `ref` | `HTMLDivElement` | `undefined` | A reference to the component that gives you access to the underlying HTML element. |
| `location` | `string` | `undefined` | A unique identifier for the tab. This value must match the `location` prop of a corresponding `TabLink` component to correctly associate the tab with its trigger. Only the `Tab` with a `location` matching the active tab state in the `Tabs` component will be displayed. |
| `keepInMemory` | `boolean` | `false` | A `boolean` flag specifying whether the content of the Tab should be preserved in memory. Use this option if you want to improve scripting performance at the cost of increased memory usage and initial load time. Keep in mind that when this option is set to true, the content of the Tab will be initialized in memory upon component mount, even if the Tab is never rendered. |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the component. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, this component's actions will execute. Can be a CSS selector or HTMLElement. |
## Guide
diff --git a/docs/src/content/docs/components/Layout/top.mdx b/docs/src/content/docs/components/Layout/top.mdx
index 258bd1d5..d075e8cc 100644
--- a/docs/src/content/docs/components/Layout/top.mdx
+++ b/docs/src/content/docs/components/Layout/top.mdx
@@ -34,8 +34,10 @@ export default App;
|---|---|---|---|
| `style` | `JSX.CSSProperties` | `{}` | Inline styles to apply directly to the component's root element. |
| `class` | `string` | `""` | Additional CSS classes to apply to the component |
-| `ref` | `BaseComponentRef` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. The HTML element can be accessed using the `element` property of the returned ref object. |
+| `ref` | `HTMLDivElement` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. |
| `basis` | `number` | `15%` | Sets the flex-basis of the component as a percentage. For example, a value of `30` sets the flex-basis to `30%`. |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the component. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, this component's actions will execute. Can be a CSS selector or HTMLElement. |
## Guide
@@ -44,7 +46,7 @@ export default App;
To access the HTML DOM element of the `Top` component.
1. Declare a variable to hold the ref but don't initialize it with a value
-2. The declared value should have a type of `BaseComponentRef`, which you need to import
+2. The declared value should have a type of `HTMLDivElement`.
3. Set the declared variable as the value of the `ref` prop of the `Top` component
#### Example
@@ -54,13 +56,12 @@ import Layout from '@components/Layout/Layout/Layout';
import Top from '@components/Layout/Top/Top';
import Content from '@components/Layout/Content/Content';
import Bottom from '@components/Layout/Bottom/Bottom';
-import { BaseComponentRef } from '@components/types/ComponentProps';
const App = () => {
- let topRef!: BaseComponentRef
+ let topRef!: HTMLDivElement
return (
-
+ Top SectionMain ContentBottom Section
@@ -69,4 +70,4 @@ const App = () => {
};
```
-Now you can access the HTML element of `topRef` with `topRef.element` and make modifications to it if needed.
+Now you can access the HTML element of `Top` with `topRef` and make modifications to it if needed.
diff --git a/docs/src/content/docs/components/Layout/transform.mdx b/docs/src/content/docs/components/Layout/transform.mdx
index 2555b771..131e40d0 100644
--- a/docs/src/content/docs/components/Layout/transform.mdx
+++ b/docs/src/content/docs/components/Layout/transform.mdx
@@ -35,13 +35,15 @@ export default App;
|---|---|---|---|
| `style` | `JSX.CSSProperties` | `{}` | Inline styles to apply directly to the component's root element. |
| `class` | `string` | `""` | Additional CSS classes to apply to the component |
-| `ref` | `BaseComponentRef` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. The HTML element can be accessed using the `element` property of the returned ref object. |
+| `ref` | `HTMLDivElement` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. |
| `translate` | `{ x?: number; y?: number; z?: number; }` | `undefined` | Moves the element along the x, y, or z axis in `pixels`. For example, `{ x: 50 }` translates the element 50px to the right. |
| `rotate` | `{ x?: number; y?: number; z?: number; }` | `undefined` | Rotates the element around the x, y, or z axis in `degrees`. For example, `{ z: 45 }` rotates the element 45° around the z-axis. |
| `scale` | `{ x?: number; y?: number; z?: number; }` | `undefined` | Scales the element along the x, y, or z axis. For example, `{ x: 1.5 }` increases the width of the element by 50%. |
| `skew` | `{ x?: number; y?: number; }` | `undefined` | Skews the element along the x or y axis in degrees. For example, `{ x: 15 }` skews the element 15° along the x-axis. |
| `matrix` | `{ translate?: {...}; rotate?: {...}; scale?: {...}; skew?: {...}; }` | `undefined` | Defines a custom transformation matrix as a combination of multiple transformations. |
| `origin` | `"top"`, `"center"`, `"bottom"`, `"right"`, `"left"`, `{ x?: string; y?: string; z?: string }` | `undefined` | Defines the `transform-origin` property, specifying the point of origin for transformations. It can accept preset values, combination of 2 values like: `top left`, `top right`, etc. or custom coordinates as an object (e.g., `{ x: '50%', y: '50%', z: '0' }`). |
+| `onAction` | `Record void>` | `undefined` | Allows you to add custom navigation action handlers to the component. See the [Navigation component documentation](/components/utility/navigation#extending-component-navigation-actions) for details. |
+| `anchor` | `string \| HTMLElement` | `undefined` | Links navigation to another element. When the anchor element is focused, this component's actions will execute. Can be a CSS selector or HTMLElement. |
## Guide
@@ -119,7 +121,7 @@ The second origin is almost the same as the first one, with the difference being
To access the HTML DOM element of the `Transform` component.
1. Declare a variable to hold the ref but don't initialize it with a value
-2. The declared value should have a type of `BaseComponentRef`, which you need to import
+2. The declared value should have a type of `HTMLDivElement`.
3. Set the declared variable as the value of the `ref` prop of the `Transform` component
#### Example
@@ -127,10 +129,9 @@ To access the HTML DOM element of the `Transform` component.
```tsx
import Transform from '@components/Layout/Transform/Transform';
import Block from '@components/Layout/Block/Block';
-import { BaseComponentRef } from '@components/types/ComponentProps';
const App = () => {
- let transformRef!: BaseComponentRef;
+ let transformRef!: HTMLDivElement;
return (
@@ -142,4 +143,4 @@ const App = () => {
export default App;
```
-Now you can access the HTML element of `transformRef` with `transformRef.element` and make modifications to it if needed.
+Now you can access the HTML element of `Transform` with `transformRef` and make modifications to it if needed.
diff --git a/docs/src/content/docs/components/Media/background-image.mdx b/docs/src/content/docs/components/Media/background-image.mdx
index 34271103..9eff73a7 100644
--- a/docs/src/content/docs/components/Media/background-image.mdx
+++ b/docs/src/content/docs/components/Media/background-image.mdx
@@ -33,7 +33,7 @@ In this example, the `BackgroundImage` component wraps the `
Content
`
|---|---|---|---|
| `style` | `JSX.CSSProperties` | `{}` | Inline styles to apply directly to the component's root element. |
| `class` | `string` | `""` | Additional CSS classes to apply to the component. |
-| `ref` | `BaseComponentRef` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. The HTML element can be accessed using the `element` property of the returned ref object. |
+| `ref` | `HTMLDivElement` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. |
| `src` | `string` | `undefined` | The URL source of the background image. |
| `fill` | `boolean` | `undefined` | Specifies if the component should fill its container by setting the `width` and `height` of the component to be 100%. |
| `options` | `{ repeat?: ImageRepeat; size?: ImageSize; position?: ImagePosition; }` | `undefined` | Configures the background image. You can set whether the background repeats (default is no repeat), its size, and its position. For more information, check the [options object properties](#options-object-properties). |
@@ -236,16 +236,15 @@ export default App;
To access the HTML DOM element of the `BackgroundImage` component:
1. Declare a variable to hold the ref without initializing it.
-2. The variable should be of type `BaseComponentRef`, which you need to import.
+2. The variable should be of type `HTMLDivElement`.
3. Set the declared variable as the value of the `ref` prop of the `BackgroundImage` component.
```tsx
import BackgroundImage from '@components/Media/BackgroundImage/BackgroundImage';
import Background from '../../assets/bg.png';
-import { BaseComponentRef } from '@components/types/ComponentProps';
const App = () => {
- let ref!: BaseComponentRef;
+ let ref!: HTMLDivElement;
return (
@@ -257,4 +256,4 @@ const App = () => {
export default App;
```
-You can now access the HTML element of `BackgroundImage` using `ref.element` and make modifications as needed.
+You can now access the HTML element of `BackgroundImage` using `ref` and make modifications as needed.
diff --git a/docs/src/content/docs/components/Media/mask-image.mdx b/docs/src/content/docs/components/Media/mask-image.mdx
index 42bad996..7af1fe9a 100644
--- a/docs/src/content/docs/components/Media/mask-image.mdx
+++ b/docs/src/content/docs/components/Media/mask-image.mdx
@@ -35,7 +35,7 @@ In this example, the `MaskImage` component wraps the `
Masked content
`
|---|---|---|---|
| `style` | `JSX.CSSProperties` | `{}` | Inline styles to apply directly to the component's root element. |
| `class` | `string` | `""` | Additional CSS classes to apply to the component. |
-| `ref` | `BaseComponentRef` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. The HTML element can be accessed using the `element` property of the returned ref object. |
+| `ref` | `HTMLDivElement` | `undefined` | Retrieves the component's DOM element and assigns it to a variable. |
| `src` | `string` | `undefined` | The URL source of the mask image. |
| `fill` | `boolean` | `undefined` | Specifies if the component should fill its container by setting the `width` and `height` of the component to be 100%. |
| `options` | `{ repeat?: ImageRepeat; size?: ImageSize; position?: ImagePosition; }` | `undefined` | Configures the mask image. You can set whether the mask repeats (default is no repeat), its size, and its position. For more information, check the [options object properties](#options-object-properties). |
@@ -238,16 +238,15 @@ export default App;
To access the HTML DOM element of the `MaskImage` component:
1. Declare a variable to hold the ref without initializing it.
-2. The variable should be of type `BaseComponentRef`, which you need to import.
+2. The variable should be of type `HTMLDivElement`.
3. Set the declared variable as the value of the `ref` prop of the `MaskImage` component.
```tsx
import MaskImage from '@components/Media/MaskImage/MaskImage';
import Mask from '../../assets/mask.png';
-import { BaseComponentRef } from '@components/types/ComponentProps';
const App = () => {
- let ref!: BaseComponentRef;
+ let ref!: HTMLDivElement;
return (
@@ -259,4 +258,4 @@ const App = () => {
export default App;
```
-You can now access the HTML element of `MaskImage` using `ref.element` and make modifications as needed.
+You can now access the HTML element of `MaskImage` using `ref` and make modifications as needed.
diff --git a/docs/src/content/docs/components/Utility/Navigation.mdx b/docs/src/content/docs/components/Utility/Navigation.mdx
new file mode 100644
index 00000000..3ca123a2
--- /dev/null
+++ b/docs/src/content/docs/components/Utility/Navigation.mdx
@@ -0,0 +1,603 @@
+---
+title: Navigation
+tableOfContents:
+ maxHeadingLevel: 4
+---
+import { Steps } from '@astrojs/starlight/components';
+
+The `Navigation` component is a comprehensive navigation system designed for game UIs.
+It provides keyboard and gamepad input handling, spatial navigation between UI areas, and a flexible action system for mapping inputs to callbacks.
+Built on top of the [Coherent Gameface Interaction Manager](https://frontend-tools.coherent-labs.com/interaction-manager/getting-started/),
+it enables seamless navigation across complex menu systems and HUD elements.
+
+This component provides:
+
+- Keyboard and gamepad input management with customizable bindings
+- Spatial navigation with multiple navigation areas
+- Action system for mapping inputs to game-specific callbacks
+- Scope-based context awareness for different UI sections
+- Pause/resume functionality for navigation state
+- Integration with components via context and navigation actions directive
+
+:::tip
+For in-depth introduction to the interaction system provided by the `Navigation` component refer to the [Navigation System]() page.
+:::
+
+## Usage
+
+To use the `Navigation` component, wrap your UI with it and define navigation areas for different sections of your UI. You can configure default actions and provide custom input bindings for your game.
+
+:::tip
+For detailed usage instructions, examples, and advanced features, refer to the [Guide](/components/utility/navigation#guide) section.
+:::
+
+```tsx
+import Navigation from '@components/Navigation/Navigation/Navigation';
+import { ActionMap } from '@components/Navigation/Navigation/types';
+
+const App = () => {
+ const defaultActions: ActionMap = {
+ 'tab-left': {key: {binds: ['Q'], type: ['press']}, button: {binds: ['left-sholder'], type: 'press'}, callback: menuLeft, global: true},
+ 'tab-right': {key: {binds: ['E'], type: ['press']}, button: {binds: ['right-sholder'], type: 'press'}, callback: menuRight, global: true},
+ 'select': {key: {binds: ['SPACE'], type: ['press']}, button: {binds: ['face-button-left'], type: 'press'}},
+ 'back': {key: {binds: ['BACKSPACE'], type: ['press']}},
+ }
+ return (
+
+
+
+
+
+
+
+ );
+};
+
+export default App;
+```
+
+## Default Actions
+
+The Navigation component comes with a couple of pre-configured default actions that handle common navigation patterns in game UIs.
+These actions are automatically registered when the `Navigation` component mounts. However, components typically listen for these actions only when they are **focused**.
+
+For example, pressing Arrow Right emits a global `move-right` event, but only the currently focused `Stepper` will respond to it.
+This ensures that you don't accidentally change settings in a background menu while navigating the main screen.
+
+These actions are recognized across the entire Gameface UI component library, enabling preset components to work seamlessly with the navigation system out of the box.
+
+Each default action has:
+- **Keyboard bindings** - One or more keyboard keys
+- **Gamepad bindings** - One or more gamepad buttons
+- **Global emission** - They emit events via the [event bus](/concepts/ui-communication/), allowing any component to respond
+- **No default callbacks** - They don't execute callbacks by default, only emit events. However they can be extended to execute callback as well.
+- **Library-wide recognition** - Preset components like Dropdown, `Stepper`, `Checkbox`, and others automatically listen to these actions
+
+### Available Default Actions
+
+| Action Name | Keyboard Binding | Gamepad Binding | Purpose |
+|---------------|------------------|----------------------|----------------------------------------------------------------------------------|
+| `move-left` | `ARROW_LEFT` | `pad-left` (D-Pad) | Directional input left (used by components Stepper) |
+| `move-right` | `ARROW_RIGHT` | `pad-right` (D-Pad) | Directional input right (used by components Stepper) |
+| `move-up` | `ARROW_UP` | `pad-up` (D-Pad) | Directional input up (used by components like Dropdown) |
+| `move-down` | `ARROW_DOWN` | `pad-down` (D-Pad) | Directional input down (used by components like Dropdown) |
+| `select` | `ENTER` | `face-button-down` | Confirm selection or activate the currently focused element |
+| `back` | `ESC` | `face-button-right` | Cancel current operation or navigate back to the previous screen |
+| `pan` | `none` | `right.joystick` | Continuous 2D input for manipulating values on two axes simultaneously. Paused by default |
+
+:::caution[Reserved Action Names]
+Avoid creating custom actions with names starting with `move-focus-direction` (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.
+[Read more here](https://frontend-tools.coherent-labs.com/interaction-manager/features/spatial-navigation/#actions)
+:::
+
+:::caution[Paused default action]
+The `pan` action is paused by default to save performance when there are no components listening to it. If a component that is subscribed to it gets initialized, the action will automatically unpause.
+If you wish to subscribe to the `pan` action in your custom component, make sure to unpause it first using the `resumeAction()` method from the `useNavigation()` hook.
+:::
+
+### Customizing Default Actions
+
+Default actions can be customized by updating their configuration. This is useful when you want to:
+- Change keyboard/gamepad bindings to match your game's control scheme
+- Add custom callbacks to default actions
+- Modify input types (press, hold, lift)
+
+You can customize default actions either through the `actions` prop or programmatically using `updateAction()`:
+
+**Via props:**
+```tsx
+import Navigation from '@components/Navigation/Navigation/Navigation';
+import { ActionMap } from '@components/Navigation/Navigation/types';
+
+const customActions: ActionMap = {
+ // Customize the 'select' action to use SPACE instead of ENTER
+ 'select': {
+ key: {binds: ['SPACEBAR'], type: ['press']},
+ button: {binds: ['face-button-down'], type: 'press'},
+ callback: (scope) => console.log('Selected in', scope),
+ },
+ // Customize 'back' to add a callback
+ 'back': {
+ key: {binds: ['ESC'], type: ['press']},
+ button: {binds: ['face-button-right'], type: 'press'},
+ callback: () => handleBack(),
+ }
+};
+
+
+ {/* Your UI */}
+
+```
+
+**Programmatically:**
+```tsx
+const nav = useNavigation();
+
+// Update the select action at runtime
+nav.updateAction('select', {
+ key: {binds: ['SPACEBAR'], type: ['press']},
+ button: {binds: ['face-button-down'], type: 'press'},
+ callback: (scope) => handleSelection(scope),
+ global: true
+});
+```
+
+### Adding Custom Actions
+
+Beyond the default actions, you can add your own custom actions for game-specific inputs:
+
+```tsx
+import Navigation from '@components/Navigation/Navigation/Navigation';
+import { ActionMap } from '@components/Navigation/Navigation/types';
+
+const customActions: ActionMap = {
+ // Custom action for opening inventory
+ 'open-inventory': {
+ key: {binds: ['I'], type: ['press']},
+ button: {binds: ['face-button-top'], type: 'press'},
+ callback: () => openInventory(),
+ global: true
+ },
+ // Custom action for opening map
+ 'open-map': {
+ key: {binds: ['M'], type: ['press']},
+ button: {binds: ['face-button-left'], type: 'press'},
+ callback: (scope) => openMap(scope),
+ global: false // Only emit locally, not globally
+ }
+};
+
+
+ {/* Your UI */}
+
+```
+
+Or add them programmatically:
+
+```tsx
+const nav = useNavigation();
+
+nav.addAction('toggle-menu', {
+ key: {binds: ['TAB'], type: ['press']},
+ button: {binds: ['START'], type: 'press'},
+ callback: () => toggleMenu(),
+ global: true
+});
+```
+
+### Propagating Actions (anchor element)
+
+Some components may need to respond to actions when a different element is focused.
+You can achieve this by specifying an `anchor` element in the action configuration. When the anchor element is focused, the action will trigger for the specified element.
+
+:::tip
+The `anchor` prop can accept either a CSS selector string or a direct reference to an `HTMLElement`.
+:::
+
+In the following example, the `Stepper` component will respond to navigation actions when the parent `.menu-item` div is focused.
+
+:::caution[Unique CSS Selector]
+When using a CSS selector as an anchor, ensure that it uniquely identifies the desired element within the DOM to avoid unexpected behavior. The most robust method is to use an `id` selector.
+:::
+
+```tsx
+
+
Select Difficulty:
+
+
+ Easy
+ Normal
+ Hard
+ Nightmare
+
+
+
+
+```
+
+This functionality is especially useful for more complex component configurations where the component doesn't directly receive focus but still need to respond to navigation actions.
+
+:::caution[Using anchor with focusable elements]
+Ensure that the anchor element is focusable (e.g., has a `tabindex` attribute). If the anchor is not focusable, the anchor won't be registered.
+:::
+
+:::note
+All GamefaceUI components that support navigation actions can utilize the `anchor` prop.
+:::
+
+### Subscribing to Actions in Custom Components
+
+Custom components can respond to navigation actions using the `navigationActions` directive from `BaseComponent`.
+This directive automatically listens for action events and executes callbacks when the component (or its anchor) is focused.
+
+To subscribe your component to an action, follow these steps:
+
+
+
+1. Import the `useBaseComponent` hook in your component
+
+2. Extract the `navigationActions` method from the hook
+
+3. Add the `navigationActions` method as an attribute to the element you wish to subscribe, and prefix it with the `use:` keyword
+
+4. Provide the names of the actions you wish to subscribe to and their corresponding callbacks (what will happen when they're triggered)
+
+
+
+```tsx
+import { useBaseComponent } from '@components/BaseComponent/BaseComponent';
+const MyComponent = (props) => {
+ const { navigationActions } = useBaseComponent(props);
+ return (
+
+ );
+};
+```
+
+:::caution
+Only actions with `global: true` (including all default actions) can be subscribed to using the `navigationActions` directive. Actions with `global: false` only execute their callbacks and don't emit events for components to listen to.
+:::
+
+**With an anchor element:**
+
+Anchor can also be used when subscribing to actions in custom components.
+```tsx
+const Card = (props) => {
+ let buttonRef;
+ const { navigationActions } = useBaseComponent(props);
+
+ return (
+
+ );
+};
+```
+
+:::tip
+The `navigationActions` directive only triggers callbacks when the component or its anchor is focused. This ensures actions are contextual and don't fire globally for all components.
+:::
+
+### Extending Component Navigation Actions
+
+Many preset components in the Gameface UI library (such as [Stepper](/components/basic/stepper), [Dropdown](/components/basic/dropdown), etc.) come with predefined navigation action handlers. For example, the Stepper component responds to `move-left` and `move-right` actions by default.
+
+You can extend or override these default behaviors using the `onAction` prop available on all components that extend `ComponentProps`. This allows you to:
+
+- Add additional action handlers to components (e.g., adding a `select` action to a Stepper)
+- Override the component's default action behavior
+- Customize how components respond to navigation inputs
+
+**Example - Adding a select action to a Stepper:**
+
+```tsx
+import Stepper from '@components/Basic/Stepper/Stepper';
+
+ console.log('Stepper item confirmed!')
+ }}
+>
+
+ Option 1
+ Option 2
+
+
+```
+
+**Example - Overriding default behavior:**
+
+```tsx
+ customPreviousLogic(), // Overrides default
+ 'select': () => handleSelection() // Extends default
+ }}
+>
+ {/* ... */}
+
+```
+
+:::caution[Overriding Component Actions]
+When you provide an action that a component already implements (like `move-left` for Stepper), your handler will **override** the component's default behavior. This gives you full control but can break expected component functionality if not used carefully.
+
+Each component's documentation lists which navigation actions it implements by default. Always check the component's documentation before overriding actions.
+:::
+
+:::tip
+The `onAction` prop uses object spreading, so user-provided actions are merged with component defaults. Actions you define will take precedence over the component's predefined actions.
+:::
+
+### Action Configuration Reference (ActionCfg)
+
+When defining actions, you can configure the following properties:
+
+| Property | Type | Description |
+|------------|-------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `key` | `{binds: KeyName[], type?: ActionType[]}` | Keyboard configuration. `binds` specifies which keys trigger the action. `type` specifies when to trigger: `'press'` (default), `'hold'`, or `'lift'`. |
+| `button` | `{binds: GamepadInput[], type?: Exclude}` | Gamepad configuration. `binds` specifies which buttons trigger the action. `type` specifies when to trigger: `'press'` (default) or `'hold'`. |
+| `callback` | `(scope?: string, ...args: any[]) => void` | Function to execute when the action is triggered. Receives the current navigation scope as the first parameter. |
+| `global` | `boolean` | When `true`, the action emits globally via the [eventBus](/concepts/ui-communication/), allowing any component to listen. When `false`, only the callback is executed. Default actions have this set to `true`. |
+| `paused` | `boolean` | When set to true the action will be paused by default. Useful for configuring actions that are not immediately needed. |
+
+## API
+
+### Props
+
+| Prop Name | Type | Default | Description |
+|-------------------|-------------------------------|-------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `gamepad` | `boolean` | `true` | Enables gamepad input handling for navigation and actions. |
+| `keyboard` | `boolean` | `true` | Enables keyboard input handling for navigation and actions. |
+| `actions` | `ActionMap` | `{}` | Custom action configurations to register in addition to default actions. Each action can define keyboard/gamepad bindings and callbacks. |
+| `scope` | `string` | `""` | The initial navigation scope. When a `Navigation.Area` with a matching name is registered, it will be auto-focused. |
+| `pollingInterval` | `number` | `200` | Gamepad polling interval in milliseconds. Determines how frequently the system checks for gamepad input. |
+| `ref` | `NavigationRef` \| `((nav: NavigationRef) => void)` | `undefined` | A reference to the component, providing access to all navigation methods via the [NavigationRef](/components/utility/navigation#contextref-api) interface. |
+| `overlap` | `number` | `undefined` | Overlap threshold for spatial navigation. Determines how much elements can overlap before being considered in different navigation paths. |
+
+## UseNavigation hook & Ref API
+
+The `Navigation` component exposes a comprehensive API through the `useNavigation()` hook (via context) or through a `ref`. This API provides methods for managing navigation areas, handling actions, and controlling navigation state.
+
+### Action Methods
+
+| Method Name | Parameters | Return Value | Description |
+|-----------------|------------------------------|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `addAction` | `name: ActionName, config:` [ActionCfg](/components/utility/navigation#action-configuration-reference-actioncfg) | `void` | Adds a new `Navigation` action and registers it with the interaction manager. |
+| `removeAction` | `name: ActionName` | `void` | Removes a registered `Navigation` action and unregisters it from the interaction manager. |
+| `updateAction` | `name: ActionName, config:` [ActionCfg](/components/utility/navigation#action-configuration-reference-actioncfg) | `void` | Updates an existing action's configuration. Default actions can be updated as well. Accepts the same config properties as `addAction`. |
+| `executeAction` | `name: ActionName` | `void` | Executes a registered action by name, triggering its callback and event emission. |
+| `pauseAction` | `action: ActionName`, `force?: boolean = false` | `void` | Pauses an action, preventing its callback from executing. Actions can be paused only when they don't have subscribers, to bypass that provide `true` as the second argument. |
+| `resumeAction` | `action: ActionName`, `force?: boolean = false` | `void` | Resumes a paused action, allowing its callback to execute again. If an action was force paused, you must resume it by providing `true` as a second argument when calling the function. |
+| `isPaused` | `action: ActionName` | `boolean` | Checks if an action is currently paused. Returns true if paused, false otherwise. |
+| `getScope` | None | `string` | Gets the current navigation scope (typically the name of the active navigation area). |
+| `getAction` | `name: ActionName` | `ActionCfg \| undefined` | Gets a specific action configuration by name. Returns undefined if the action doesn't exist. |
+| `getActions` | None | `ActionMap` | Gets all currently registered actions. |
+
+### Area Methods
+
+| Method Name | Parameters | Return Value | Description |
+|------------------------|----------------------------------------------------------------------|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `registerArea` | `area: string, elements: string[] \| HTMLElement[], focused?: boolean` | `void` | Registers a navigation area with focusable elements. If `focused` is true, automatically focuses the first element in the area. |
+| `unregisterArea` | `area: string` | `void` | Unregisters a navigation area and removes it from spatial navigation. |
+| `focusFirst` | `area: string` | `void` | Focuses the first focusable element in the specified area and updates the navigation `scope`. |
+| `focusLast` | `area: string` | `void` | Focuses the last focusable element in the specified area and updates the navigation `scope`. |
+| `switchArea` | `area: string` | `void` | Switches the active navigation to the specified area by focusing the first element in it and updating the navigation `scope`. |
+| `clearFocus` | None | `void` | Clears the current focus from all navigation areas. |
+| `changeNavigationKeys` | `keys: { up?: string, down?: string, left?: string, right?: string }, clearCurrent?: boolean` | `void` | Changes the navigation keys for spatial navigation. If `clearCurrent` is true, clears current active keys before setting new ones. |
+| `resetNavigationKeys` | None | `void` | Resets navigation keys to their default values. |
+| `pauseNavigation` | None | `void` | Pauses navigation, preventing spatial navigation actions from executing. |
+| `resumeNavigation` | None | `void` | Resumes navigation, allowing spatial navigation actions to execute again. |
+
+## Slots
+
+The `Navigation` component exposes subcomponents that allow you to structure your navigation:
+
+- [**Navigation.Area**](/components/utility/navigation/#navigationarea) - Defines a navigation area with focusable elements.
+
+### Navigation.Area
+
+`Navigation.Area` registers a section of your UI as a navigable area. Elements within the area can be navigated using keyboard/gamepad inputs. Areas can be switched programmatically.
+
+When a `Navigation.Area` mounts:
+- It registers itself with the spatial navigation system
+- If its `name` matches the parent Navigation's `scope` prop or if `focused={true}`, it auto-focuses
+- It automatically handles cleanup on unmount
+
+#### Navigation.Area Props
+
+| Prop Name | Type | Default | Description |
+| ---------- | -------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `name` | `string` | *required* | The unique name identifier for this navigation area. Used to reference the area in methods like `focusFirst`, `switchArea`, and for `scope` tracking. |
+| `selector` | `string` | `undefined` | CSS class selector for navigable elements. If provided, only elements matching this selector will be navigable. If omitted, all child elements are considered navigable. |
+| `focused` | `boolean` | `false` | When true, this area will automatically receive focus when it mounts, focusing its first navigable element. |
+
+#### Usage
+
+```tsx
+
+
+
+
+
+
+
+
+
Resolution
+
Quality
+
V-Sync
+
+
+```
+
+## Guide
+
+### Getting access to the navigation API
+
+You can access the `Navigation` component's methods and properties from within your components in 2 ways:
+
+1. **Using the `useNavigation()` hook**
+
+This hook provides access to the navigation API via context. It is the recommended method for any child component nested inside `Navigation`.
+
+```tsx
+import { useNavigation } from "@components/Utility/Navigation/Navigation";
+import Button from '@components/Basic/Button/Button';
+
+const MyComponent = () => {
+ const navigation = useNavigation();
+
+ // Use navigation methods here
+ return (
+
+ );
+};
+```
+
+:::caution[Context Required]
+If `MyComponent` is not a child of `Navigation`, the `useNavigation()` hook will throw an error.
+:::
+
+2. **Using a `ref` to the `Navigation` component**
+
+For components that use the `Navigation` directly in the return block, you cannot use the hook. Instead, you must attach a `ref`.
+
+**Option A: The Callback Ref (Recommended for Initialization)**
+
+If you need to execute logic immediately when the navigation mounts (e.g., registering default areas), use the callback pattern. This guarantees the API is ready before you use it.
+
+```tsx
+import Navigation, { NavigationRef } from "@components/Utility/Navigation/Navigation";
+
+const MyComponent = () => {
+ // 1. Store it if you need it for later (clicks/events)
+ let navigationRef: NavigationRef | undefined;
+
+ // 2. Define the callback that runs immediately on mount
+ const handleNavReady = (api: NavigationRef) => {
+ navigationRef = api; // Save reference
+
+ // Safe to use API immediately here
+ api.registerArea('main-menu', ['#start-button', '#settings-button'], true);
+ }
+
+ return (
+
+ {/* Your UI */}
+
+ );
+};
+```
+
+**Option B: The Simple Ref (For Event Handlers Only)**
+
+If you only need the API for user interactions that happen after the page loads, you can use a simple variable assignment.
+
+```tsx
+import Navigation, { NavigationRef } from "@components/Utility/Navigation/Navigation";
+
+const MyComponent = () => {
+ let navigationRef: NavigationRef | undefined;
+
+ const someHandler = () => {
+ navigationRef?.focusFirst('main-menu');
+ }
+
+ return (
+
+ {/* Your UI */}
+
+ );
+};
+```
+
+:::note
+The `ref` prop accepts both a callback function with a `NavigationRef` argument and a direct variable assignment. If you need to immediately run some logic when the navigation mounts,
+use the callback pattern to ensure the API is ready (Option A).
+:::
+
+### Action states (Pause/Resume)
+
+Actions can be paused and resumed to control when they are active. This is useful for optimizing performance or managing context-specific inputs.
+
+For example, you might want to pause a certain action to avoid a case where a button press triggers multiple actions in a specific context.
+In such cases, you can pause the action when entering that context and resume it when exiting.
+
+```tsx
+import { useNavigation } from "@components/Utility/Navigation/Navigation";
+
+const MyComponent = () => {
+ const navigation = useNavigation();
+
+ const openMenu = () => {
+ // Pause the actions to prevent accidental component triggers
+ navigation.pauseAction('move-left');
+ navigation.pauseAction('move-right');
+ };
+
+ const closeMenu = () => {
+ // Resume the actions when leaving the menu
+ navigation.resumeAction('move-left');
+ navigation.resumeAction('move-right');
+ };
+
+ return (
+
+
+
+
+ );
+};
+```
+
+In this example, the `move-left` and `move-right` actions are paused when entering a specific context (e.g., a modal or special menu) and resumed when exiting that context.
+
+Internally `resumeAction` and `pauseAction` manage a counter that prevents desynchronization when multiple components try to pause/resume the same action.
+For example if **two** components pause the same action, it will require **two** calls to `resumeAction` to resume it.
+
+This is done to avoid cases where one component resumes an action that another component still needs paused.
+Another common case is if you have two component that use the `pan` action - both will resume it when mounted and pause it when unmounted.
+Without the counter, unmounting one component will pause the action even if the other component is still mounted, prevent it from reacting to the action.
+
+However, if you need to bypass this behavior, you can provide `true` as the second argument to both methods to **force** the action to pause or resume immediately.
+
+```tsx
+const MyComponent = () => {
+ const navigation = useNavigation();
+
+ const forcePause = () => {
+ // Force pause the 'select' action immediately
+ navigation.pauseAction('select', true);
+ };
+
+ const forceResume = () => {
+ // Force resume the 'select' action immediately
+ navigation.resumeAction('select', true);
+ };
+
+ return (
+
+
+
+
+ );
+};
+```
+
+:::note
+If an action has been force paused, you must also force resume it by providing `true` as the second argument when calling `resumeAction()`. Or vice versa.
+:::
+
+
diff --git a/docs/src/content/docs/components/index.mdx b/docs/src/content/docs/components/index.mdx
index 6a95543f..d02606b7 100644
--- a/docs/src/content/docs/components/index.mdx
+++ b/docs/src/content/docs/components/index.mdx
@@ -30,6 +30,7 @@ export const componentCategories = {
Complex: Object.keys(import.meta.glob('/src/assets/components/complex/*')),
Layout: Object.keys(import.meta.glob('/src/assets/components/layout/*')),
Media: Object.keys(import.meta.glob('/src/assets/components/media/*')),
+ Utility: Object.keys(import.meta.glob('/src/assets/components/utility/*'))
};
export function getCategorySlug(title) {
diff --git a/package.json b/package.json
index 40e7087c..3fc88854 100644
--- a/package.json
+++ b/package.json
@@ -32,6 +32,7 @@
"dependencies": {
"@solid-primitives/jsx-tokenizer": "^1.1.1",
"@types/node": "^22.9.0",
+ "coherent-gameface-interaction-manager": "^2.6.0",
"cors-env": "^1.0.2",
"dotenv": "^17.2.0",
"glob": "^11.0.0",
diff --git a/src/components/BaseComponent/BaseComponent.tsx b/src/components/BaseComponent/BaseComponent.tsx
index 871e36e9..c152f570 100644
--- a/src/components/BaseComponent/BaseComponent.tsx
+++ b/src/components/BaseComponent/BaseComponent.tsx
@@ -1,5 +1,10 @@
-import { ComponentProps } from "@components/types/ComponentProps";
-import { createEffect } from "solid-js";
+import { useNavigation } from "@components/Utility/Navigation/Navigation";
+import eventBus from "@components/Utility/EventBus";
+import { ComponentProps, NavigationActionsConfig } from "@components/types/ComponentProps";
+import { Accessor, createEffect } from "solid-js";
+import { waitForFrames } from "@components/utils/waitForFrames";
+import { DEFAULT_ACTION_NAMES } from "@components/Utility/Navigation/defaults";
+import { DefaultActions } from "@components/Utility/Navigation/types";
const baseEventsSet = new Set([
"abort",
@@ -83,6 +88,63 @@ function forwardEvents(el: HTMLElement, getData: () => Record) {
}
}
+function navigationActions(el: HTMLElement, accessor: Accessor) {
+ const config = accessor();
+ if (!config) return;
+
+ const nav = useNavigation();
+ if (!nav) return;
+
+ const { anchor, ...actionHandlers } = config;
+
+ el.setAttribute('tabindex', '0');
+
+ let anchorElement: HTMLElement | null = null;
+ if (anchor) {
+ if (typeof anchor === 'string') {
+ waitForFrames(() => anchorElement = document.querySelector(anchor));
+ } else if (anchor instanceof HTMLElement) {
+ anchorElement = anchor;
+ }
+ }
+
+ const isFocused = () => {
+ const active = document.activeElement;
+ return (
+ active === el ||
+ (anchorElement && active === anchorElement) ||
+ el.contains(active)
+ );
+ };
+
+ const listeners: Array<[string, (args: any) => void]> = [];
+ for (const [name, func] of Object.entries(actionHandlers)) {
+ const action = nav.getAction(name);
+ if (!action) {
+ console.warn(`Action "${name}" is not registered in Navigation.`);
+ continue;
+ }
+
+ if (!action.global && !DEFAULT_ACTION_NAMES.has(name as DefaultActions)) {
+ console.warn(`Action "${name}" is not global. To subscribe components to it, please make it global.`);
+ continue;
+ }
+
+ const handler = (...args: any) => {
+ if (isFocused()) (func as Function)(...args);
+ };
+
+ eventBus.on(name, handler);
+ listeners.push([name, handler]);
+ }
+
+ return () => {
+ for (const [name, handler] of listeners) {
+ eventBus.off(name, handler);
+ }
+ };
+}
+
export function useBaseComponent(props: ComponentProps) {
const className = () => {
const classes = (typeof props.componentClasses === "function" ? props.componentClasses() : props.componentClasses || '') + " " + (props.class || '');
@@ -94,7 +156,7 @@ export function useBaseComponent(props: ComponentProps) {
...props.style
});
- return { className, inlineStyles, forwardAttrs, forwardEvents };
+ return { className, inlineStyles, forwardAttrs, forwardEvents, navigationActions };
}
export default useBaseComponent;
\ No newline at end of file
diff --git a/src/components/Basic/Accordion/Accordion.module.scss b/src/components/Basic/Accordion/Accordion.module.scss
index 03a8ede8..1c384c64 100644
--- a/src/components/Basic/Accordion/Accordion.module.scss
+++ b/src/components/Basic/Accordion/Accordion.module.scss
@@ -33,6 +33,10 @@
&-content {
flex: 1;
}
+
+ &:focus {
+ background-color: #dcdcdc
+ }
}
diff --git a/src/components/Basic/Accordion/Accordion.tsx b/src/components/Basic/Accordion/Accordion.tsx
index cb5276a1..3e8c9436 100644
--- a/src/components/Basic/Accordion/Accordion.tsx
+++ b/src/components/Basic/Accordion/Accordion.tsx
@@ -1,11 +1,11 @@
import { ComponentProps } from "@components/types/ComponentProps";
-import { Accessor, createContext, createMemo, createSignal, createUniqueId, For, onMount, ParentComponent, Setter } from "solid-js";
-import styles from './Accordion.module.scss';
+import { Accessor, createContext, createMemo, createSignal, createUniqueId, For, onMount, ParentComponent } from "solid-js";
import useBaseComponent from "@components/BaseComponent/BaseComponent";
import { AccordionPanel, Panel, PanelTokenProps } from "./AccordionPanel";
import { Heading, Icon } from "./AccordionHeading";
import { Body } from "./AccordionBody";
import { useTokens } from "@components/utils/tokenComponents";
+import styles from './Accordion.module.scss';
export interface AccordionRef {
element: HTMLDivElement,
@@ -20,7 +20,7 @@ export interface PanelData {
id: string
}
-interface AccordionProps extends ComponentProps {
+interface AccordionProps extends Omit {
multiple?: boolean;
disabled?: boolean;
'class-disabled'?: string;
@@ -167,7 +167,7 @@ const Accordion: ParentComponent = (props) => {
class={className()}
style={inlineStyles()}
use:forwardEvents={props}
- use:forwardAttrs={props} >
+ use:forwardAttrs={props}>
{(data) => }
diff --git a/src/components/Basic/Accordion/AccordionHeading.tsx b/src/components/Basic/Accordion/AccordionHeading.tsx
index 5083e86d..febd3ece 100644
--- a/src/components/Basic/Accordion/AccordionHeading.tsx
+++ b/src/components/Basic/Accordion/AccordionHeading.tsx
@@ -4,22 +4,35 @@ import AccordionIcon from './AccordionIcon.svg?component-solid'
import styles from './Accordion.module.scss';
import { CommonAccordionSlotProps, PanelChildrenComponentProps } from "./AccordionPanel";
import { AccordionContext } from "./Accordion";
+import mergeNavigationActions from "@components/utils/mergeNavigationActions";
+import useBaseComponent from "@components/BaseComponent/BaseComponent";
+import { ComponentNavigationActions } from "@components/types/ComponentProps";
-export const Heading = createTokenComponent();
+interface AccordionHeadingProps extends CommonAccordionSlotProps {
+ onAction?: ComponentNavigationActions,
+ anchor?: string | HTMLElement,
+}
+
+export const Heading = createTokenComponent();
export const Icon = createTokenComponent();
-export const AccordionHeading: ParentComponent<{ id: string } & PanelChildrenComponentProps> = (props) => {
+export const AccordionHeading: ParentComponent<{ id: string, onAction?: Record void> } & PanelChildrenComponentProps> = (props) => {
const HeadingToken = useToken(Heading, props.parentChildren);
const IconToken = useToken(Icon, HeadingToken?.()?.children)
const accordion = useContext(AccordionContext)
+ const { navigationActions } = useBaseComponent({} as any);
+
return (