Skip to content

Commit 3665193

Browse files
feat(ui5-wheelslider): swipe feature implementation (#1470)
1 parent 348bde9 commit 3665193

File tree

4 files changed

+143
-73
lines changed

4 files changed

+143
-73
lines changed

packages/main/src/TimePicker.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ class TimePicker extends UI5Element {
581581
}
582582
}
583583

584-
_oncontainerkeydown(e) {
584+
async _oncontainerkeydown(e) {
585585
if (isLeft(e)) {
586586
let expandedSliderIndex = 0;
587587
for (let i = 0; i < this._slidersDomRefs.length; i++) {
@@ -610,11 +610,13 @@ class TimePicker extends UI5Element {
610610
}
611611

612612
if (isTabNext(e) && e.target === this._slidersDomRefs[this._slidersDomRefs.length - 1]) {
613+
const responsivePopover = await this._getPopover();
613614
e.preventDefault();
614-
this.getStaticAreaItemDomRef().querySelector(".ui5-timepicker-footer").firstElementChild.focus();
615+
responsivePopover.querySelector(".ui5-timepicker-footer").firstElementChild.focus();
615616
} else if (isTabPrevious(e) && e.target === this._slidersDomRefs[0]) {
617+
const responsivePopover = await this._getPopover();
616618
e.preventDefault();
617-
this.getStaticAreaItemDomRef().querySelector(`.ui5-timepicker-footer`).lastElementChild.focus();
619+
responsivePopover.querySelector(`.ui5-timepicker-footer`).lastElementChild.focus();
618620
}
619621
}
620622

packages/main/src/WheelSlider.js

+128-68
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
} from "@ui5/webcomponents-base/src/Keys.js";
88
import "@ui5/webcomponents-icons/dist/icons/navigation-up-arrow.js";
99
import "@ui5/webcomponents-icons/dist/icons/navigation-down-arrow.js";
10+
import ScrollEnablement from "@ui5/webcomponents-base/dist/delegate/ScrollEnablement.js";
1011
import WheelSliderTemplate from "./generated/templates/WheelSliderTemplate.lit.js";
1112
import Button from "./Button.js";
1213

@@ -153,6 +154,10 @@ class WheelSlider extends UI5Element {
153154
this._currentElementIndex = 0;
154155
this._itemCellHeight = 0;
155156
this._itemsToShow = [];
157+
this._scroller = new ScrollEnablement(this);
158+
this._scroller.attachEvent("scroll", this._updateScrolling.bind(this));
159+
this._scroller.attachEvent("mouseup", this._handleScrollTouchEnd.bind(this));
160+
this._scroller.attachEvent("touchend", this._handleScrollTouchEnd.bind(this));
156161
}
157162

158163
onBeforeRendering() {
@@ -169,15 +174,19 @@ class WheelSlider extends UI5Element {
169174
this._updateItemCellHeight();
170175
}
171176

172-
_updateItemCellHeight() {
173-
this._itemCellHeight = this.shadowRoot.querySelectorAll(".ui5-wheelslider-item").length && Number(getComputedStyle(this.shadowRoot.querySelector(".ui5-wheelslider-item")).getPropertyValue("--_ui5_wheelslider_item_height").replace("rem", ""));
174-
}
175-
176177
static async onDefine() {
177178
await Button.define();
178179
}
179180

180181
onAfterRendering() {
182+
if (!this._scroller.scrollContainer) {
183+
this._scroller.scrollContainer = this.shadowRoot.querySelector(`#${this._id}--wrapper`);
184+
}
185+
186+
if (!this._expanded) {
187+
this._scroller.scrollTo(0, 0);
188+
}
189+
181190
if (this._expanded) {
182191
const elements = this.shadowRoot.querySelectorAll(".ui5-wheelslider-item");
183192
for (let i = 0; i < elements.length; i++) {
@@ -191,25 +200,6 @@ class WheelSlider extends UI5Element {
191200
}
192201
}
193202

194-
_timesMultipliedOnCyclic() {
195-
const minElementsInCyclicWheelSlider = 70;
196-
const repetitionCount = Math.round(minElementsInCyclicWheelSlider / this._items.length);
197-
const minRepetitionCount = 3;
198-
199-
return Math.max(minRepetitionCount, repetitionCount);
200-
}
201-
202-
_buildItemsToShow() {
203-
this._itemsToShow = this._items;
204-
if (this.cyclic) {
205-
if (this._itemsToShow.length < this._items.length * this._timesMultipliedOnCyclic()) {
206-
for (let i = 0; i < this._timesMultipliedOnCyclic(); i++) {
207-
this._itemsToShow = this._itemsToShow.concat(this._items);
208-
}
209-
}
210-
}
211-
}
212-
213203
get classes() {
214204
return {
215205
root: {
@@ -219,49 +209,56 @@ class WheelSlider extends UI5Element {
219209
};
220210
}
221211

222-
_handleWheel(e) {
223-
if (!e) {
224-
return;
225-
}
212+
expandSlider() {
213+
this._expanded = true;
214+
this.fireEvent("expand", {});
215+
}
226216

227-
e.stopPropagation();
228-
e.preventDefault();
217+
collapseSlider() {
218+
this._expanded = false;
219+
this.fireEvent("collapse", {});
220+
}
229221

230-
if (e.timeStamp === this._prevWheelTimestamp || !this._expanded) {
231-
return;
222+
_updateItemCellHeight() {
223+
if (this.shadowRoot.querySelectorAll(".ui5-wheelslider-item").length) {
224+
const itemComputedStyle = getComputedStyle(this.shadowRoot.querySelector(".ui5-wheelslider-item"));
225+
const itemHeightValue = itemComputedStyle.getPropertyValue("--_ui5_wheelslider_item_height");
226+
const onlyDigitsValue = itemHeightValue.replace("rem", "");
227+
228+
this._itemCellHeight = Number(onlyDigitsValue);
232229
}
230+
}
233231

234-
if (e.deltaY > 0) {
235-
this._onArrowUp(e);
236-
} else if (e.deltaY < 0) {
237-
this._onArrowDown(e);
232+
_updateScrolling() {
233+
const sizeOfOneElementInPixels = this._itemCellHeight * 16,
234+
scrollWhere = this._scroller.scrollContainer.scrollTop;
235+
let offsetIndex;
236+
237+
if (!scrollWhere) {
238+
return;
238239
}
239240

240-
this._prevWheelTimestamp = e.timeStamp;
241-
}
241+
offsetIndex = Math.round(scrollWhere / sizeOfOneElementInPixels);
242242

243-
_onclick(e) {
244-
if (!e.target.classList.contains("ui5-wheelslider-item")) {
243+
if (this.value === this._itemsToShow[offsetIndex]) {
245244
return;
246245
}
247246

248-
if (this._expanded) {
249-
this.value = e.target.textContent;
250-
this._selectElement(e.target);
251-
this.fireEvent("valueSelect", { value: this.value });
252-
} else {
253-
this._expanded = true;
247+
if (this.cyclic) {
248+
const newIndex = this._handleArrayBorderReached(offsetIndex);
249+
if (offsetIndex !== newIndex) {
250+
offsetIndex = newIndex;
251+
}
254252
}
255-
}
256253

257-
expandSlider() {
258-
this._expanded = true;
259-
this.fireEvent("expand", {});
254+
this.value = this._itemsToShow[offsetIndex];
255+
this._currentElementIndex = offsetIndex;
260256
}
261257

262-
collapseSlider() {
263-
this._expanded = false;
264-
this.fireEvent("collapse", {});
258+
_handleScrollTouchEnd() {
259+
if (this._expanded) {
260+
this._selectElementByIndex(this._currentElementIndex);
261+
}
265262
}
266263

267264
_selectElement(element) {
@@ -280,28 +277,48 @@ class WheelSlider extends UI5Element {
280277
}
281278

282279
_selectElementByIndex(currentIndex) {
283-
const sliderElement = this.shadowRoot.getElementById(`${this._id}--items-list`);
284-
const itemsCount = this._itemsToShow.length;
285-
const itemCellHeight = this._itemCellHeight ? this._itemCellHeight : 2.875;
286-
const offsetStep = isPhone() ? 4 : 2;
287280
let index = currentIndex;
281+
const itemsCount = this._itemsToShow.length;
282+
const sizeOfCellInCompactInRem = 2;
283+
const sizeOfCellInCozyInRem = 2.875;
284+
const sizeOfCellInCompactInPixels = sizeOfCellInCompactInRem * 16;
285+
const sizeOfCellInCozyInPixels = sizeOfCellInCozyInRem * 16;
286+
const scrollBy = this.isCompact ? sizeOfCellInCompactInPixels * index : sizeOfCellInCozyInPixels * index;
288287

289288
if (this.cyclic) {
290-
index = this.handleArrayBorderReached(index);
289+
index = this._handleArrayBorderReached(index);
291290
}
292291

293292
if (index < itemsCount && index > -1) {
294-
const offsetSelectedElement = offsetStep * itemCellHeight - (index * itemCellHeight);
295-
sliderElement.setAttribute("style", `top:${offsetSelectedElement}rem`);
293+
this._scroller.scrollTo(0, scrollBy);
296294
this._currentElementIndex = index;
297295
this.value = this._items[index - (this._getCurrentRepetition() * this._items.length)];
298296
this.fireEvent("valueSelect", { value: this.value });
299297
}
300298
}
301299

302-
handleArrayBorderReached(currentIndex) {
300+
_timesMultipliedOnCyclic() {
301+
const minElementsInCyclicWheelSlider = 70;
302+
const repetitionCount = Math.round(minElementsInCyclicWheelSlider / this._items.length);
303+
const minRepetitionCount = 3;
304+
305+
return Math.max(minRepetitionCount, repetitionCount);
306+
}
307+
308+
_buildItemsToShow() {
309+
this._itemsToShow = this._items;
310+
if (this.cyclic) {
311+
if (this._itemsToShow.length < this._items.length * this._timesMultipliedOnCyclic()) {
312+
for (let i = 0; i < this._timesMultipliedOnCyclic(); i++) {
313+
this._itemsToShow = this._itemsToShow.concat(this._items);
314+
}
315+
}
316+
}
317+
}
318+
319+
_handleArrayBorderReached(currentIndex) {
303320
const arrayLength = this._itemsToShow.length;
304-
const maxVisibleElementsOnOneSide = 5;
321+
const maxVisibleElementsOnOneSide = 7;
305322
let index = currentIndex;
306323

307324
if (maxVisibleElementsOnOneSide > index) {
@@ -313,29 +330,72 @@ class WheelSlider extends UI5Element {
313330
return index;
314331
}
315332

333+
_handleWheel(e) {
334+
if (!e) {
335+
return;
336+
}
337+
338+
e.stopPropagation();
339+
e.preventDefault();
340+
341+
if (e.timeStamp === this._prevWheelTimestamp || !this._expanded) {
342+
return;
343+
}
344+
345+
if (e.deltaY > 0) {
346+
this._itemUp();
347+
} else if (e.deltaY < 0) {
348+
this._itemDown();
349+
}
350+
351+
this._prevWheelTimestamp = e.timeStamp;
352+
}
353+
354+
_onclick(e) {
355+
if (!e.target.classList.contains("ui5-wheelslider-item")) {
356+
return;
357+
}
358+
359+
if (this._expanded) {
360+
this.value = e.target.textContent;
361+
this._selectElement(e.target);
362+
this.fireEvent("valueSelect", { value: this.value });
363+
} else {
364+
this._expanded = true;
365+
}
366+
}
367+
316368
_onArrowDown(e) {
317369
e.preventDefault();
318-
const nextElementIndex = this._currentElementIndex + 1;
319-
this._selectElementByIndex(nextElementIndex);
370+
this._itemDown();
320371
}
321372

322373
_onArrowUp(e) {
323374
e.preventDefault();
375+
this._itemUp();
376+
}
377+
378+
_itemDown() {
379+
const nextElementIndex = this._currentElementIndex + 1;
380+
this._selectElementByIndex(nextElementIndex);
381+
}
382+
383+
_itemUp() {
324384
const nextElementIndex = this._currentElementIndex - 1;
325385
this._selectElementByIndex(nextElementIndex);
326386
}
327387

328-
_onkeydown(event) {
388+
_onkeydown(е) {
329389
if (!this._expanded) {
330390
return;
331391
}
332392

333-
if (isUp(event)) {
334-
this._onArrowUp(event);
393+
if (isUp(е)) {
394+
this._onArrowUp(е);
335395
}
336396

337-
if (isDown(event)) {
338-
this._onArrowDown(event);
397+
if (isDown(е)) {
398+
this._onArrowDown(е);
339399
}
340400
}
341401

packages/main/src/themes/WheelSlider.css

+8
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
padding: 0;
9898
position: absolute;
9999
top: var(--_ui5_wheelslider_selection_frame_margin_top);
100+
height: 3000px;
100101
cursor: pointer;
101102
list-style-type: none;
102103
}
@@ -108,6 +109,13 @@
108109
.ui5-wheelslider-root[expanded] .ui5-wheelslider-inner .ui5-wheelslider-wrapper > ul {
109110
list-style-type: none;
110111
top: 0;
112+
padding-top: calc(var(--_ui5_wheelslider_item_height) * 2);
113+
}
114+
115+
.ui5-wheelslider-root.ui5-phone[expanded] .ui5-wheelslider-inner .ui5-wheelslider-wrapper > ul {
116+
list-style-type: none;
117+
top: 0;
118+
padding-top: calc(var(--_ui5_wheelslider_item_height) * 4);
111119
}
112120

113121
.ui5-wheelslider-root .ui5-wheelslider-inner .ui5-wheelslider-wrapper {

packages/main/test/specs/WheelSlider.spec.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ describe("Wheel Slider general interaction", () => {
5454
const slider = browser.$("#wheelslider");
5555
const sliderElements = slider.shadow$$(".ui5-wheelslider-item");
5656

57-
sliderElements[5].click();
57+
sliderElements[3].click();
5858

59-
assert.strictEqual(slider.getValue(), "6", "Wheel Slider pick elements with click works");
59+
assert.strictEqual(slider.getValue(), "4", "Wheel Slider pick elements with click works");
6060
});
6161
});

0 commit comments

Comments
 (0)