Skip to content

Commit faa64b7

Browse files
committed
navDynamic refactoring & fix: skip invisible elements
1 parent bf6f661 commit faa64b7

File tree

2 files changed

+73
-32
lines changed

2 files changed

+73
-32
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-navtree",
3-
"version": "0.0.5",
3+
"version": "0.0.6",
44
"description": "Library for making page navigation (using keyboard or TV Remote Control in STB/ Smart TV apps) React way",
55
"main": "dist/index.js",
66
"dependencies": {

src/NavFunctions.js

Lines changed: 72 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,59 +3,100 @@
33
// (elements' position evaluated at run-time)
44
//
55

6-
const getPoint = function (dir, pos) {
7-
if (dir === 'left') return { x: pos.left, y: pos.top + ((pos.bottom - pos.top) / 2) } // left middle
8-
else if (dir === 'right') return { x: pos.right, y: pos.top + ((pos.bottom - pos.top) / 2) } // right middle
9-
else if (dir === 'top') return { x: pos.left + ((pos.right - pos.left) / 2), y: pos.top } // center top
10-
else if (dir === 'bottom') return { x: pos.left + ((pos.right - pos.left) / 2), y: pos.bottom } // center bottom
6+
/**
7+
* Get element position
8+
* @param el
9+
* @returns {ClientRect|null}
10+
*/
11+
const getElRect = function (el) {
12+
let rect = el.getBoundingClientRect()
13+
if (rect.width === 0 && rect.height === 0) return null // element is not visible
14+
return rect
15+
}
16+
17+
/**
18+
* Get center point of a rectangle. The points are used to compare distance between elements.
19+
* up
20+
* ┌────*────┐
21+
* │ │
22+
* left * * right
23+
* │ │
24+
* └────*────┘
25+
* down
26+
*
27+
* @param side
28+
* @param rect {ClientRect}
29+
* @returns {{x, y}}
30+
*/
31+
const getCenterPoint = function (side, rect) {
32+
if (side === 'left') return { x: rect.left, y: rect.top + ((rect.bottom - rect.top) / 2) }
33+
else if (side === 'right') return { x: rect.right, y: rect.top + ((rect.bottom - rect.top) / 2) }
34+
else if (side === 'up') return { x: rect.left + ((rect.right - rect.left) / 2), y: rect.top }
35+
else if (side === 'down') return { x: rect.left + ((rect.right - rect.left) / 2), y: rect.bottom }
1136
}
1237

38+
/**
39+
* Calculate distance between two points
40+
* @param pos1 {{x, y}}
41+
* @param pos2 {{x, y}}
42+
* @returns {number}
43+
*/
1344
const getDistance = function (pos1, pos2) {
1445
let a = pos1.x - pos2.x
1546
let b = pos1.y - pos2.y
1647

1748
return Math.sqrt(a * a + b * b)
1849
}
1950

51+
const invertKey = {left: 'right', right: 'left', up: 'down', down: 'up'}
52+
2053
/**
2154
*
2255
*/
23-
export function navDynamic (key, navTree, focusedNode) {
56+
export function navDynamic (key, navTree, deepestFocusedNode) {
2457
if (key !== 'left' && key !== 'right' && key !== 'up' && key !== 'down') return false
2558

26-
let focusedEl
59+
let srcEl
2760
if (navTree.focusedNode !== null) {
28-
focusedEl = navTree.nodes[navTree.focusedNode].el
29-
} else if (focusedNode) {
30-
focusedEl = focusedNode.el
61+
srcEl = navTree.nodes[navTree.focusedNode].el
62+
} else if (deepestFocusedNode) {
63+
srcEl = deepestFocusedNode.el
3164
}
3265

33-
let srcPoint = {left: 'left', right: 'right', up: 'top', down: 'bottom'}
34-
let dstPoint = {left: 'right', right: 'left', up: 'bottom', down: 'top'}
35-
36-
let point = focusedEl ? getPoint(srcPoint[key], focusedEl.getBoundingClientRect()) : {x: 0, y: 0}
66+
// get src element position
67+
let srcRect
68+
let srcPoint
69+
if (srcEl && (srcRect = getElRect(srcEl))) {
70+
srcPoint = getCenterPoint(key, srcRect)
71+
} else { // no element, or not visible
72+
srcPoint = {x: 0, y: 0}
73+
}
3774

3875
let minDistance = null
3976
let minDistanceNode
4077

41-
// Loop over children nodes and find a node that is at minimum distance from previously focused element
78+
// Loop over child nodes to find a node that is at minimum distance from previously focused element
4279
navTree.nodesId.forEach(id => {
43-
let node = navTree.nodes[id]
44-
let el = node.el
45-
if (el !== focusedEl) {
46-
let elPoint = getPoint(dstPoint[key], el.getBoundingClientRect())
47-
48-
if ((key === 'left' && elPoint.x > point.x) ||
49-
(key === 'right' && elPoint.x < point.x) ||
50-
(key === 'up' && elPoint.y > point.y) ||
51-
(key === 'down' && elPoint.y < point.y)) return
52-
53-
let distance = getDistance(point, elPoint)
54-
55-
if (minDistance === null || minDistance > distance) {
56-
minDistance = distance
57-
minDistanceNode = node
58-
}
80+
let dstNode = navTree.nodes[id]
81+
let dstEl = dstNode.el
82+
if (dstEl === srcEl) return
83+
84+
// get dst element position
85+
let dstRect = getElRect(dstEl)
86+
if (!dstRect) return // element is not visible, skip
87+
let dstPoint = getCenterPoint(invertKey[key], dstRect)
88+
89+
// skip if element is beyond the scope
90+
if ((key === 'left' && dstPoint.x > srcPoint.x) ||
91+
(key === 'right' && dstPoint.x < srcPoint.x) ||
92+
(key === 'up' && dstPoint.y > srcPoint.y) ||
93+
(key === 'down' && dstPoint.y < srcPoint.y)) return
94+
95+
let distance = getDistance(srcPoint, dstPoint)
96+
97+
if (minDistance === null || minDistance > distance) {
98+
minDistance = distance
99+
minDistanceNode = dstNode
59100
}
60101
})
61102

0 commit comments

Comments
 (0)