diff --git a/CHANGELOG.md b/CHANGELOG.md index f88efe1..84b79e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +# [2.1.0] +## Added +- new `init` config option `shouldUseNativeEvents` that enables the use of native events for triggering actions, such as clicks or key presses. +- new `init` config option `rtl` that changes focus behavior for layouts in right-to-left (RTL) languages such as Arabic and Hebrew. + # [2.0.2] ## Added - Console warning when passing an empty `ref` to `useFocusable` diff --git a/README.md b/README.md index 319c916..ac39de9 100644 --- a/README.md +++ b/README.md @@ -260,6 +260,9 @@ Also consider `role` and `aria-label` attributes. But that depends on the applic The flag is ignored if `nativeMode` is set. +### `shouldUseNativeEvents`: boolean (default: false) +This flag, when set to true, enables the use of native events for triggering actions, such as clicks or key presses. For instance, the onClick method will be triggered while pressing the enterKey, as well as cliicking the element itself. It is particularly beneficial for enhancing the accessibility of web applications. When shouldUseNativeEvents is active, the underlying accessible DOM node becomes the focus of the event. + ##### `rtl`: boolean (default: false) This flag changes focus behavior for layouts in right-to-left (RTL) languages such as Arabic and Hebrew. diff --git a/package.json b/package.json index 0552b7d..c421c6a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@noriginmedia/norigin-spatial-navigation", - "version": "2.0.2", + "version": "2.1.0", "description": "React hooks based Spatial Navigation solution", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/SpatialNavigation.ts b/src/SpatialNavigation.ts index e5bcf86..817dd1a 100644 --- a/src/SpatialNavigation.ts +++ b/src/SpatialNavigation.ts @@ -139,15 +139,18 @@ export type BackwardsCompatibleKeyMap = { export type KeyMap = { [index: string]: (string | number)[] }; -const getChildClosestToOrigin = (children: FocusableComponent[], writingDirection: WritingDirection) => { - const comparator = writingDirection === WritingDirection.LTR - ? (({ layout }: FocusableComponent) => Math.abs(layout.left) + Math.abs(layout.top)) - : (({ layout }: FocusableComponent) => Math.abs(window.innerWidth - layout.right) + Math.abs(layout.top)); - - const childrenClosestToOrigin = sortBy( - children, - comparator, - ); +const getChildClosestToOrigin = ( + children: FocusableComponent[], + writingDirection: WritingDirection +) => { + const comparator = + writingDirection === WritingDirection.LTR + ? ({ layout }: FocusableComponent) => + Math.abs(layout.left) + Math.abs(layout.top) + : ({ layout }: FocusableComponent) => + Math.abs(window.innerWidth - layout.right) + Math.abs(layout.top); + + const childrenClosestToOrigin = sortBy(children, comparator); return first(childrenClosestToOrigin); }; @@ -178,6 +181,8 @@ class SpatialNavigationService { private shouldFocusDOMNode: boolean; + private shouldUseNativeEvents: boolean; + /** * This collection contains focus keys of the elements that are having a child focused * Might be handy for styling of certain parent components if their child is focused. @@ -244,27 +249,27 @@ class SpatialNavigationService { isIncremental: boolean, isSibling: boolean, layout: FocusableComponentLayout, - writingDirection: WritingDirection, + writingDirection: WritingDirection ) { const itemStart = isVertical ? layout.top : writingDirection === WritingDirection.LTR - ? layout.left - : layout.right; + ? layout.left + : layout.right; const itemEnd = isVertical ? layout.bottom : writingDirection === WritingDirection.LTR - ? layout.right - : layout.left; + ? layout.right + : layout.left; return isIncremental ? isSibling ? itemStart : itemEnd : isSibling - ? itemEnd - : itemStart; + ? itemEnd + : itemStart; } /** @@ -545,6 +550,7 @@ class SpatialNavigationService { this.throttleKeypresses = false; this.useGetBoundingClientRect = false; this.shouldFocusDOMNode = false; + this.shouldUseNativeEvents = false; this.writingDirection = WritingDirection.LTR; this.pressedKeys = {}; @@ -591,6 +597,7 @@ class SpatialNavigationService { throttleKeypresses = false, useGetBoundingClientRect = false, shouldFocusDOMNode = false, + shouldUseNativeEvents = false, rtl = false } = {}) { if (!this.enabled) { @@ -599,6 +606,7 @@ class SpatialNavigationService { this.throttleKeypresses = throttleKeypresses; this.useGetBoundingClientRect = useGetBoundingClientRect; this.shouldFocusDOMNode = shouldFocusDOMNode && !nativeMode; + this.shouldUseNativeEvents = shouldUseNativeEvents; this.writingDirection = rtl ? WritingDirection.RTL : WritingDirection.LTR; this.debug = debug; @@ -696,8 +704,10 @@ class SpatialNavigationService { ? this.pressedKeys[eventType] + 1 : 1; - event.preventDefault(); - event.stopPropagation(); + if (!this.shouldUseNativeEvents) { + event.preventDefault(); + event.stopPropagation(); + } const keysDetails = { pressedKeys: this.pressedKeys @@ -896,7 +906,10 @@ class SpatialNavigationService { const isVerticalDirection = direction === DIRECTION_DOWN || direction === DIRECTION_UP; const isIncrementalDirection = - direction === DIRECTION_DOWN || (this.writingDirection === WritingDirection.LTR ? direction === DIRECTION_RIGHT : direction === DIRECTION_LEFT); + direction === DIRECTION_DOWN || + (this.writingDirection === WritingDirection.LTR + ? direction === DIRECTION_RIGHT + : direction === DIRECTION_LEFT); this.log('smartNavigate', 'direction', direction); this.log('smartNavigate', 'fromParentFocusKey', fromParentFocusKey); @@ -939,7 +952,7 @@ class SpatialNavigationService { isIncrementalDirection, false, layout, - this.writingDirection, + this.writingDirection ); /** @@ -957,7 +970,7 @@ class SpatialNavigationService { isIncrementalDirection, true, component.layout, - this.writingDirection, + this.writingDirection ); return isVerticalDirection @@ -965,13 +978,12 @@ class SpatialNavigationService { ? siblingCutoffCoordinate >= currentCutoffCoordinate // vertical next : siblingCutoffCoordinate <= currentCutoffCoordinate // vertical previous : this.writingDirection === WritingDirection.LTR - ? isIncrementalDirection - ? siblingCutoffCoordinate >= currentCutoffCoordinate // horizontal LTR next - : siblingCutoffCoordinate <= currentCutoffCoordinate // horizontal LTR previous - : isIncrementalDirection - ? siblingCutoffCoordinate <= currentCutoffCoordinate // horizontal RTL next - : siblingCutoffCoordinate >= currentCutoffCoordinate // horizontal RTL previous - ; + ? isIncrementalDirection + ? siblingCutoffCoordinate >= currentCutoffCoordinate // horizontal LTR next + : siblingCutoffCoordinate <= currentCutoffCoordinate // horizontal LTR previous + : isIncrementalDirection + ? siblingCutoffCoordinate <= currentCutoffCoordinate // horizontal RTL next + : siblingCutoffCoordinate >= currentCutoffCoordinate; // horizontal RTL previous } return false; @@ -1087,7 +1099,17 @@ class SpatialNavigationService { */ const sortedForceFocusableComponents = this.sortSiblingsByPriority( forceFocusableComponents, - { x: 0, y: 0, width: 0, height: 0, left: 0, top: 0, right: 0, bottom: 0, node: null }, + { + x: 0, + y: 0, + width: 0, + height: 0, + left: 0, + top: 0, + right: 0, + bottom: 0, + node: null + }, 'down', ROOT_FOCUS_KEY ); @@ -1167,7 +1189,10 @@ class SpatialNavigationService { * Otherwise, trying to focus something by coordinates */ children.forEach((component) => this.updateLayout(component.focusKey)); - const { focusKey: childKey } = getChildClosestToOrigin(children, this.writingDirection); + const { focusKey: childKey } = getChildClosestToOrigin( + children, + this.writingDirection + ); this.log('getNextFocusKey', 'childKey will be focused', childKey);