Skip to content

Commit b7ad1ea

Browse files
authored
feat(framework): ItemNavigation cyclic behavior implemented for multiple rows (#2780)
1 parent d769a0d commit b7ad1ea

File tree

3 files changed

+71
-87
lines changed

3 files changed

+71
-87
lines changed

packages/base/src/delegate/ItemNavigation.js

+67-87
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { renderFinished } from "../Render.js";
21
import {
32
isDown,
43
isUp,
@@ -92,97 +91,101 @@ class ItemNavigation extends EventProvider {
9291
});
9392
}
9493

95-
_horizontalNavigationOn() {
96-
return this.horizontalNavigationOn;
97-
}
98-
99-
_verticalNavigationOn() {
100-
return this.verticalNavigationOn;
101-
}
94+
onkeydown(event) {
95+
if (!this._canNavigate()) {
96+
return;
97+
}
10298

103-
async _onKeyPress(event) {
104-
if (this.currentIndex >= this._getItems().length) {
105-
this.onOverflowBottomEdge();
106-
} else if (this.currentIndex < 0) {
107-
this.onOverflowTopEdge();
99+
if (isUp(event) && this.verticalNavigationOn) {
100+
this._handleUp();
101+
} else if (isDown(event) && this.verticalNavigationOn) {
102+
this._handleDown();
103+
} else if (isLeft(event) && this.horizontalNavigationOn) {
104+
this._handleLeft();
105+
} else if (isRight(event) && this.horizontalNavigationOn) {
106+
this._handleRight();
107+
} else if (isHome(event)) {
108+
this._handleHome();
109+
} else if (isEnd(event)) {
110+
this._handleEnd();
111+
} else {
112+
return; // if none of the supported keys is pressed, we don't want to prevent the event or update the item navigation
108113
}
109114

110115
event.preventDefault();
111-
112-
await renderFinished();
113-
114116
this.update();
115117
this.focusCurrent();
116118
}
117119

118-
onkeydown(event) {
119-
if (isUp(event) && this._verticalNavigationOn()) {
120-
return this._handleUp(event);
121-
}
122-
123-
if (isDown(event) && this._verticalNavigationOn()) {
124-
return this._handleDown(event);
125-
}
126-
127-
if (isLeft(event) && this._horizontalNavigationOn()) {
128-
return this._handleLeft(event);
120+
_handleUp() {
121+
const itemsLength = this._getItems().length;
122+
if (this.currentIndex - this.rowSize >= 0) { // no border reached, just decrease the index by a row
123+
this.currentIndex -= this.rowSize;
124+
return;
129125
}
130126

131-
if (isRight(event) && this._horizontalNavigationOn()) {
132-
return this._handleRight(event);
127+
if (this.behavior === ItemNavigationBehavior.Cyclic) { // if cyclic, go to the **last** item in the **previous** column
128+
const firstItemInThisColumnIndex = this.currentIndex % this.rowSize;
129+
const firstItemInPreviousColumnIndex = firstItemInThisColumnIndex === 0 ? this.rowSize - 1 : firstItemInThisColumnIndex - 1; // find the first item in the previous column (if the current column is the first column -> move to the last column)
130+
const rows = Math.ceil(itemsLength / this.rowSize); // how many rows there are (even if incomplete, f.e. for 14 items and rowSize=4 -> 4 rows total, although only 2 items on the last row)
131+
let lastItemInPreviousColumnIndex = firstItemInPreviousColumnIndex + (rows - 1) * this.rowSize; // multiply rows by columns, and add the column's first item's index
132+
if (lastItemInPreviousColumnIndex > itemsLength - 1) { // for incomplete rows, use the previous row's last item, as for them the last item is missing
133+
lastItemInPreviousColumnIndex -= this.rowSize;
134+
}
135+
this.currentIndex = lastItemInPreviousColumnIndex;
136+
} else { // not cyclic, so just go to the first item
137+
this.currentIndex = 0;
133138
}
139+
}
134140

135-
if (isHome(event)) {
136-
return this._handleHome(event);
141+
_handleDown() {
142+
const itemsLength = this._getItems().length;
143+
if (this.currentIndex + this.rowSize < itemsLength) { // no border reached, just increase the index by a row
144+
this.currentIndex += this.rowSize;
145+
return;
137146
}
138147

139-
if (isEnd(event)) {
140-
return this._handleEnd(event);
148+
if (this.behavior === ItemNavigationBehavior.Cyclic) { // if cyclic, go to the **first** item in the **next** column
149+
const firstItemInThisColumnIndex = this.currentIndex % this.rowSize; // find the first item in the current column first
150+
const firstItemInNextColumnIndex = (firstItemInThisColumnIndex + 1) % this.rowSize; // to get the first item in the next column, just increase the index by 1. The modulo by rows is for the case when we are at the last column
151+
this.currentIndex = firstItemInNextColumnIndex;
152+
} else { // not cyclic, so just go to the last item
153+
this.currentIndex = itemsLength - 1;
141154
}
142155
}
143156

144-
_handleUp(event) {
145-
if (this._canNavigate()) {
146-
this.currentIndex -= this.rowSize;
147-
this._onKeyPress(event);
157+
_handleLeft() {
158+
const itemsLength = this._getItems().length;
159+
if (this.currentIndex > 0) {
160+
this.currentIndex -= 1;
161+
return;
148162
}
149-
}
150163

151-
_handleDown(event) {
152-
if (this._canNavigate()) {
153-
this.currentIndex += this.rowSize;
154-
this._onKeyPress(event);
164+
if (this.behavior === ItemNavigationBehavior.Cyclic) { // go to the first item in the next column
165+
this.currentIndex = itemsLength - 1;
155166
}
156167
}
157168

158-
_handleLeft(event) {
159-
if (this._canNavigate()) {
160-
this.currentIndex -= 1;
161-
this._onKeyPress(event);
169+
_handleRight() {
170+
const itemsLength = this._getItems().length;
171+
if (this.currentIndex < itemsLength - 1) {
172+
this.currentIndex += 1;
173+
return;
162174
}
163-
}
164175

165-
_handleRight(event) {
166-
if (this._canNavigate()) {
167-
this.currentIndex += 1;
168-
this._onKeyPress(event);
176+
if (this.behavior === ItemNavigationBehavior.Cyclic) { // go to the first item in the next column
177+
this.currentIndex = 0;
169178
}
170179
}
171180

172-
_handleHome(event) {
173-
if (this._canNavigate()) {
174-
const homeEndRange = this.rowSize > 1 ? this.rowSize : this._getItems().length;
175-
this.currentIndex -= this.currentIndex % homeEndRange;
176-
this._onKeyPress(event);
177-
}
181+
_handleHome() {
182+
const homeEndRange = this.rowSize > 1 ? this.rowSize : this._getItems().length;
183+
this.currentIndex -= this.currentIndex % homeEndRange;
178184
}
179185

180-
_handleEnd(event) {
181-
if (this._canNavigate()) {
182-
const homeEndRange = this.rowSize > 1 ? this.rowSize : this._getItems().length;
183-
this.currentIndex += (homeEndRange - 1 - this.currentIndex % homeEndRange); // eslint-disable-line
184-
this._onKeyPress(event);
185-
}
186+
_handleEnd() {
187+
const homeEndRange = this.rowSize > 1 ? this.rowSize : this._getItems().length;
188+
this.currentIndex += (homeEndRange - 1 - this.currentIndex % homeEndRange); // eslint-disable-line
186189
}
187190

188191
/**
@@ -219,8 +222,7 @@ class ItemNavigation extends EventProvider {
219222
}
220223

221224
/**
222-
* @public
223-
* @deprecated
225+
* @private
224226
*/
225227
focusCurrent() {
226228
const currentItem = this._getCurrentItem();
@@ -286,28 +288,6 @@ class ItemNavigation extends EventProvider {
286288
set current(val) {
287289
this.currentIndex = val;
288290
}
289-
290-
onOverflowBottomEdge(event) {
291-
const items = this._getItems();
292-
293-
if (this.behavior === ItemNavigationBehavior.Cyclic) {
294-
this.currentIndex = 0;
295-
return;
296-
}
297-
298-
this.currentIndex = items.length - 1;
299-
}
300-
301-
onOverflowTopEdge(event) {
302-
const items = this._getItems();
303-
304-
if (this.behavior === ItemNavigationBehavior.Cyclic) {
305-
this.currentIndex = items.length - 1;
306-
return;
307-
}
308-
309-
this.currentIndex = 0;
310-
}
311291
}
312292

313293
export default ItemNavigation;

packages/fiori/src/ProductSwitch.js

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
22
import ItemNavigation from "@ui5/webcomponents-base/dist/delegate/ItemNavigation.js";
3+
import ItemNavigationBehavior from "@ui5/webcomponents-base/dist/types/ItemNavigationBehavior.js";
34
import ResizeHandler from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js";
45
import Integer from "@ui5/webcomponents-base/dist/types/Integer.js";
56
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
@@ -64,6 +65,7 @@ class ProductSwitch extends UI5Element {
6465

6566
this._itemNavigation = new ItemNavigation(this, {
6667
rowSize: 4,
68+
behavior: ItemNavigationBehavior.Cyclic,
6769
getItemsCallback: () => this.items,
6870
});
6971
}

packages/main/bundle.esm.js

+2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ import { getAssetsPath, setAssetsPath } from "@ui5/webcomponents-base/dist/confi
22
// setAssetsPath("/my-resources/");
33

44
import { addCustomCSS, attachThemeLoaded, detachThemeLoaded } from "@ui5/webcomponents-base/dist/Theming";
5+
/*
56
attachThemeLoaded(theme => {
67
console.log("Theme load complete: ", theme);
78
});
9+
*/
810

911
// OpenUI5 integration
1012
import "@ui5/webcomponents-base/dist/features/OpenUI5Support.js";

0 commit comments

Comments
 (0)