Skip to content

Commit ef6a729

Browse files
committed
slider commit
1 parent 92b562f commit ef6a729

File tree

8 files changed

+203
-14
lines changed

8 files changed

+203
-14
lines changed

packages/base/src/Keys.js

+1
Original file line numberDiff line numberDiff line change
@@ -212,4 +212,5 @@ export {
212212
isPageDownShift,
213213
isPageUpShiftCtrl,
214214
isPageDownShiftCtrl,
215+
getCtrlKey,
215216
};

packages/main/src/RangeSlider.hbs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
{{>include "./SliderBase.hbs"}}
22

33
{{#*inline "handles"}}
4-
<div class="ui5-slider-handle ui5-slider-handle--start" style="{{styles.startHandle}}">
4+
<div class="ui5-slider-handle ui5-slider-handle--start" style="{{styles.startHandle}}" @focusout={{_onfocusout}}
5+
@focusin={{_onfocusin}} tabindex="{{tabIndex}}">
56
{{#if showTooltip}}
67
<div class="ui5-slider-tooltip ui5-slider-tooltip--start" style="{{styles.tooltip}}">
78
<span class="ui5-slider-tooltip-value">{{tooltipStartValue}}</span>
89
</div>
910
{{/if}}
1011
</div>
11-
<div class="ui5-slider-handle ui5-slider-handle--end" style="{{styles.endHandle}}">
12+
<div class="ui5-slider-handle ui5-slider-handle--end" style="{{styles.endHandle}}" tabindex="{{tabIndex}}" @focusout={{_onfocusout}}
13+
@focusin={{_onfocusin}}>
1214
{{#if showTooltip}}
1315
<div class="ui5-slider-tooltip ui5-slider-tooltip--end" style="{{styles.tooltip}}">
1416
<span class="ui5-slider-tooltip-value">{{tooltipEndValue}}</span>

packages/main/src/RangeSlider.js

+140-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Float from "@ui5/webcomponents-base/dist/types/Float.js";
22
import { fetchI18nBundle, getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
33
import SliderBase from "./SliderBase.js";
4+
import {isEscape, isTabPrevious, isTabNext, isHome, isEnd} from "@ui5/webcomponents-base/dist/Keys.js";
45

56
// Template
67
import RangeSliderTemplate from "./generated/templates/RangeSliderTemplate.lit.js";
@@ -99,6 +100,12 @@ class RangeSlider extends SliderBase {
99100
this.i18nBundle = getI18nBundle("@ui5/webcomponents");
100101
}
101102

103+
onEnterDOM() {
104+
this._sliderStartHandle = this.shadowRoot.querySelector(".ui5-slider-handle--start");
105+
this._sliderEndHandle = this.shadowRoot.querySelector(".ui5-slider-handle--end");
106+
this._sliderProgress = this.shadowRoot.querySelector(".ui5-slider-progress");
107+
}
108+
102109
get tooltipStartValue() {
103110
const stepPrecision = this.constructor._getDecimalPrecisionOfNumber(this._effectiveStep);
104111
return this.startValue.toFixed(stepPrecision);
@@ -129,6 +136,103 @@ class RangeSlider extends SliderBase {
129136
this._updateHandlesAndRange(null);
130137
}
131138

139+
_onfocusin(event) {
140+
this.focused = true;
141+
142+
this._setInitialValue("startValue", this._prevStartValue || this.startValue);
143+
this._setInitialValue("endValue", this._prevEndValue || this.endValue);
144+
}
145+
146+
_onfocusout(event) {
147+
if (this._isFocusing()) {
148+
// Prevent focusout when the focus is getting initially set within the slider before the
149+
// slider customElement itself is finished focusing.
150+
this._preventFocusOut();
151+
} else {
152+
this.focused = false;
153+
this._valueAffected = null;
154+
155+
// Reset the stored Slider's initial value saved when it was first focused
156+
this._setInitialValue("value", null);
157+
}
158+
}
159+
160+
_preventFocusOut() {
161+
this._focusInnerElement();
162+
}
163+
164+
_onkeydown(event) {
165+
this._onKeyDownBase(event);
166+
}
167+
168+
_handleActionKeyPress(event) {
169+
if (isEscape(event)) {
170+
this.startValue = this._prevStartValue;
171+
this.endValue = this._prevEndValue;
172+
}
173+
174+
175+
if (this.shadowRoot.activeElement === this._sliderStartHandle) {
176+
this._valueAffected = "startValue"
177+
}
178+
179+
if (this.shadowRoot.activeElement === this._sliderEndHandle) {
180+
this._valueAffected = "endValue"
181+
}
182+
183+
const min = this._effectiveMin;
184+
const max = this._effectiveMax;
185+
const valueType = this._valueAffected;
186+
187+
if ((isEnd(event) || isHome(event)) && !valueType) {
188+
const eventType = isHome(event) ? "home" : "end"
189+
this._handleHomeEndKeys(event, eventType, min, max)
190+
191+
return;
192+
}
193+
194+
if (valueType) {
195+
const newValueOffset = SliderBase.prototype._handleActionKeyPress.call(this, event, valueType);
196+
197+
if (!newValueOffset) {
198+
return;
199+
}
200+
201+
const newValue = isEscape(event) ? this._getInitialValue(valueType) : this.constructor.clipValue(newValueOffset + currentValue, min, max);
202+
const currentValue = this[valueType];
203+
204+
this._updateHandlesAndRange(newValue);
205+
this.updateValue(valueType, newValue);
206+
this.storePropertyState(valueType);
207+
} else {
208+
const newValueOffset = SliderBase.prototype._handleActionKeyPress.call(this, event, null);
209+
210+
if (!newValueOffset) {
211+
return;
212+
}
213+
214+
const newStartValue = isEscape(event) ? this._getInitialValue("startValue") : this.constructor.clipValue(newValueOffset + this.startValue, min, max);
215+
const newEndValue = isEscape(event) ? this._getInitialValue("endValue") : this.constructor.clipValue(newValueOffset + this.endValue, min, max);
216+
217+
this.updateValue("startValue", newStartValue);
218+
this.updateValue("endValue", newEndValue);
219+
this._updateHandlesAndRange(null);
220+
this.storePropertyState("startValue", "endValue");
221+
}
222+
}
223+
224+
_handleHomeEndKeys(event, eventType, min, max) {
225+
const affectedValue = eventType === "home" ? "startValue" : "endValue";
226+
const newValueOffset = SliderBase.prototype._handleActionKeyPress.call(this, event, affectedValue)
227+
const newStartValue = this.constructor.clipValue(newValueOffset + this.startValue, min, max);
228+
const newEndValue = this.constructor.clipValue(newValueOffset + this.endValue, min, max);
229+
230+
this.updateValue("startValue", newStartValue);
231+
this.updateValue("endValue", newEndValue);
232+
this._updateHandlesAndRange(null);
233+
this.storePropertyState("startValue", "endValue");
234+
}
235+
132236
/**
133237
* Called when the user starts interacting with the slider
134238
*
@@ -254,9 +358,9 @@ class RangeSlider extends SliderBase {
254358
this._swapValues();
255359
this.handleUpBase();
256360

257-
this._valueAffected = null;
258361
this._prevStartValue = null;
259362
this._prevEndValue = null;
363+
this._inCurrentRange = null;
260364
}
261365

262366
/**
@@ -300,6 +404,37 @@ class RangeSlider extends SliderBase {
300404
}
301405
}
302406

407+
/* Flag the component that it is currently being in process of focusing in. When the slider is getting focused
408+
we need that focus to be delegated to the Slider's handle and to not stay on the slider's shadow root div, as it
409+
is by default. In theory this can be achieved either if the 'delegatesFocus' attribute of the .attachShadow()
410+
customElement method is set to true or if we forward it manually as part of the component logic.
411+
412+
As we use lit-element as base of our core UI5 element class that 'delegatesFocus' property is not set to 'true' and
413+
we have to manage the focus here. If at some point in the future this changes, the focus delegating logic could be
414+
removed as it will become redundant.
415+
416+
When we manually set the focus on mouseDown to the first focusable element inside the shadowDom - the slider's handle,
417+
that inside focus and subsquently the shadowRoot.activeElement are set a moment before the global document.activeElement
418+
is set to the customElement (ui5-slider) causing a 'race condition'.
419+
420+
In order for a element within the shadowRoot to be focused, the global document.activeElement MUST be the parent
421+
customElement of the shadow root, in our case the ui5-slider component. Because of that after our focusin of the handle,
422+
a focusout event fired by the browser immidiatly after, resetting the focus.
423+
424+
Note: If we set the focus to the handle a bit later in time, for example on a mouseup or click event it will
425+
work fine and we will avoid the described race condition as our customElement will be already finished focusing.
426+
However, that does not work for us as we need the focus to be set to the handle exactly on mousedown,
427+
because of the nature of the component and its available drag interactions.*/
428+
_focusInnerElement() {
429+
if (this._inCurrentRange) {
430+
this._sliderProgress.focus();
431+
} else if (this._valueAffected === "startValue") {
432+
this._sliderStartHandle.focus();
433+
} else {
434+
this._sliderEndHandle.focus();
435+
}
436+
}
437+
303438
/**
304439
* Calculates startValue/endValue properties when the whole range is moved.
305440
*
@@ -419,6 +554,10 @@ class RangeSlider extends SliderBase {
419554
}
420555
}
421556

557+
get tabIndexProgress() {
558+
return this.tabIndex;
559+
}
560+
422561
get styles() {
423562
return {
424563
progress: {

packages/main/src/Slider.js

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import SliderBase from "./SliderBase.js";
66

77
// Template
88
import SliderTemplate from "./generated/templates/SliderTemplate.lit.js";
9+
import { isEscape } from "@ui5/webcomponents-base/dist/Keys.js";
910

1011
/**
1112
* @public
@@ -114,6 +115,10 @@ class Slider extends SliderBase {
114115
this._updateHandleAndProgress(this.value);
115116
}
116117

118+
_onkeydown(event) {
119+
this._onKeyDownBase(event, "value");
120+
}
121+
117122
/**
118123
* Called when the user starts interacting with the slider
119124
*

packages/main/src/SliderBase.hbs

+5
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@
2323
{{/if}}
2424

2525
<div class="ui5-slider-progress-container">
26+
<<<<<<< HEAD
2627
<div class="ui5-slider-progress" style="{{styles.progress}}" @focusout={{_onfocusout}} @focusin={{_onfocusin}} tabindex="{{tabIndexProgress}}"></div>
28+
=======
29+
<div class="ui5-slider-progress" style="{{styles.progress}}" @focusout={{_onfocusout}}
30+
@focusin={{_onfocusin}} tabindex="{{tabIndexProgress}}"></div>
31+
>>>>>>> 0d62527d (slider commit)
2732
</div>
2833
{{> handles}}
2934
</div>

packages/main/src/SliderBase.js

+41
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,47 @@ class SliderBase extends UI5Element {
277277
}
278278
}
279279

280+
_setInitialValue(valueType, value) {
281+
this[`_${valueType}Initial`] = value;
282+
}
283+
284+
_getInitialValue(valueType) {
285+
return this[`_${valueType}Initial`];
286+
}
287+
288+
_onKeyDownBase(event) {
289+
if (this.disabled) {
290+
return;
291+
}
292+
293+
if (SliderBase._isActionKey(event)) {
294+
event.preventDefault();
295+
this._isUserInteraction = true;
296+
this._handleActionKeyPress(event);
297+
}
298+
}
299+
300+
_onkeyup(event) {
301+
if (this.disabled) {
302+
return;
303+
}
304+
305+
this._isUserInteraction = false;
306+
}
307+
308+
static _isActionKey(event) {
309+
return this.ACTION_KEYS.some(actionKey => actionKey(event));
310+
}
311+
312+
_isFocusing() {
313+
return this._isInProcessOfFocusing;
314+
}
315+
316+
_setIsFocusing(isInProcessOfFocusing) {
317+
this._isInProcessOfFocusing = isInProcessOfFocusing;
318+
}
319+
320+
280321
/**
281322
* Sets initial value when the component is focused in, can be restored with ESC key
282323
*

packages/main/src/themes/SliderBase.css

+5-9
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@
4949
position: relative;
5050
}
5151

52+
.ui5-slider-progress:focus {
53+
outline: var(--_ui5_slider_progress_outline);
54+
outline-offset: var(--_ui5_slider_progress_outline_offset);
55+
}
56+
5257
.ui5-slider-tickmarks {
5358
color: var(--_ui5_slider_tickmark_color);
5459
position: absolute;
@@ -74,10 +79,6 @@
7479
outline-offset: var(--_ui5_slider_handle_outline_offset);
7580
}
7681

77-
.ui5-slider-handle--start, .ui5-slider-handle--end {
78-
background: var(--_ui5_range_slider_handle_background);
79-
}
80-
8182
[dir="rtl"] .ui5-slider-handle {
8283
margin-right: var(--_ui5_slider_handle_margin_left);
8384
}
@@ -88,11 +89,6 @@
8889
border-color: var(--_ui5_slider_handle_hover_border);
8990
}
9091

91-
.ui5-slider-root:hover .ui5-slider-handle--start, .ui5-slider-root:hover .ui5-slider-handle--end,
92-
.ui5-slider-root:active .ui5-slider-handle--start, .ui5-slider-root:active .ui5-slider-handle--end,
93-
.ui5-slider-handle--start:active, .ui5-slider-handle--end:active {
94-
background: var(--_ui5_range_slider_handle_hover_background);
95-
}
9692

9793
.ui5-slider-tooltip {
9894
text-align: center;

packages/main/src/themes/base/SliderBase-parameters.css

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
--_ui5_slider_handle_width: 1.625rem;
1010
--_ui5_slider_handle_border: solid 0.125rem var(--sapField_BorderColor);
1111
--_ui5_slider_handle_background: var(--sapButton_Background);
12-
--_ui5_range_slider_handle_background: rgba(var(--sapButton_Background), 0.25);
1312
--_ui5_slider_handle_top: -0.825rem;
1413
--_ui5_slider_handle_margin_left: -0.9725rem;
1514
--_ui5_slider_handle_hover_background: var(--sapButton_Hover_Background);
1615
--_ui5_slider_handle_hover_border: var(--sapButton_Hover_BorderColor);
1716
--_ui5_slider_handle_outline: 1px dotted var(--sapContent_FocusColor);
1817
--_ui5_slider_handle_outline_offset: 0.075rem;
19-
--_ui5_range_slider_handle_hover_background: rgba(var(--sapButton_Background), 0.25);
18+
--_ui5_slider_progress_outline: 0.0625rem dotted var(--sapContent_FocusColor);
19+
--_ui5_slider_progress_outline_offset: 0.525rem;
2020
--_ui5_slider_tickmark_color: #89919a;
2121
--_ui5_slider_tickmark_top: -0.375rem;
2222
--_ui5_slider_disabled_opacity: 0.4;

0 commit comments

Comments
 (0)