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 + + + + + + + + + + + + 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 + +``` ## 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 + +``` + ## 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 Section Main Content Bottom 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 Section Main Content Bottom 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 Section Main Content Bottom 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 +