Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 74 additions & 158 deletions packages/main/cypress/specs/Slider.cy.tsx

Large diffs are not rendered by default.

96 changes: 26 additions & 70 deletions packages/main/src/Slider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// Template
import SliderTemplate from "./SliderTemplate.js";
import type { SliderTooltipChangeEventDetails } from "./SliderTooltip.js";
import styles from "./generated/themes/Slider.css.js";

// Texts
import {
Expand Down Expand Up @@ -77,6 +78,7 @@
tag: "ui5-slider",
languageAware: true,
formAssociated: true,
styles: [styles],
template: SliderTemplate,
})
class Slider extends SliderBase implements IFormInputElement {
Expand Down Expand Up @@ -111,7 +113,6 @@

constructor() {
super();
this._stateStorage.value = undefined;
this._lastValidInputValue = this.min.toString();
}

Expand All @@ -127,12 +128,6 @@
*
*/
onBeforeRendering() {
if (!this.isCurrentStateOutdated()) {
return;
}

this.notResized = true;
this.syncUIAndState();
this._updateHandleAndProgress(this.value);
}

Expand All @@ -142,34 +137,8 @@
this.tooltip?.repositionTooltip();
}

syncUIAndState() {
// Validate step and update the stored state for the step property.
if (this.isPropertyUpdated("step")) {
this._validateStep(this.step);
this.storePropertyState("step");
}

// Recalculate the tickmarks and labels and update the stored state.
if (this.isPropertyUpdated("min", "max", "value")) {
this.storePropertyState("min", "max");

// Here the value props are changed programmatically (not by user interaction)
// and it won't be "stepified" (rounded to the nearest step). 'Clip' them within
// min and max bounderies and update the previous state reference.
this.value = SliderBase.clipValue(this.value, this._effectiveMin, this._effectiveMax);
this.updateStateStorageAndFireInputEvent("value");
this.storePropertyState("value");
}

// Labels must be updated if any of the min/max/step/labelInterval props are changed
if (this.labelInterval && this.showTickmarks) {
this._createLabels();
}

// Update the stored state for the labelInterval, if changed
if (this.isPropertyUpdated("labelInterval")) {
this.storePropertyState("labelInterval");
}
_handleResize() {
// TODO: Remove after refactoring Base and RangeSlider

Check warning on line 141 in packages/main/src/Slider.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected 'todo' comment: 'TODO: Remove after refactoring Base and...'
}

/**
Expand Down Expand Up @@ -232,7 +201,7 @@

_onTooltipChange(e: CustomEvent<SliderTooltipChangeEventDetails>) {
const value = parseFloat(e.detail.value);
const isInvalid = value < this._effectiveMin || value > this._effectiveMax;
const isInvalid = value < this.min || value > this.max;

if (isInvalid) {
this.tooltipValueState = "Negative";
Expand All @@ -246,7 +215,7 @@

_onTooltipFocusChange() {
const value = parseFloat(this.tooltipValue);
const isInvalid = value < this._effectiveMin || value > this._effectiveMax;
const isInvalid = value < this.min || value > this.max;

if (isInvalid) {
this.tooltipValueState = "None";
Expand All @@ -263,7 +232,7 @@

_onTooltipOpen() {
const ctor = this.constructor as typeof Slider;
const stepPrecision = ctor._getDecimalPrecisionOfNumber(this._effectiveStep);
const stepPrecision = ctor._getDecimalPrecisionOfNumber(this.step);
this.tooltipValue = this.value.toFixed(stepPrecision);
}

Expand All @@ -278,14 +247,8 @@
_handleMove(e: TouchEvent | MouseEvent) {
e.preventDefault();

// If step is 0 no interaction is available because there is no constant
// (equal for all user environments) quantitative representation of the value
if (this.disabled || this._effectiveStep === 0) {
return;
}

const ctor = this.constructor as typeof Slider;
const newValue = ctor.getValueFromInteraction(e, this._effectiveStep, this._effectiveMin, this._effectiveMax, this.getBoundingClientRect(), this.directionStart);
const newValue = ctor.getValueFromInteraction(e, this.step, this.min, this.max, this.getBoundingClientRect(), this.directionStart);

this._updateHandleAndProgress(newValue);
this.value = newValue;
Expand Down Expand Up @@ -329,8 +292,8 @@
* @private
*/
_updateHandleAndProgress(newValue: number) {
const max = this._effectiveMax;
const min = this._effectiveMin;
const max = this.max;
const min = this.min;

// The progress (completed) percentage of the slider.
this._progressPercentage = (newValue - min) / (max - min);
Expand All @@ -339,8 +302,8 @@
}

_handleActionKeyPress(e: KeyboardEvent) {
const min = this._effectiveMin;
const max = this._effectiveMax;
const min = this.min;
const max = this.max;
const currentValue = this.value;
const ctor = this.constructor as typeof Slider;
const newValue = isEscape(e) ? this._valueInitial : ctor.clipValue(this._handleActionKeyPressBase(e, "value") + currentValue, min, max);
Expand All @@ -367,20 +330,8 @@
return this.getDomRef()?.querySelector<SliderTooltip>("[ui5-slider-tooltip]");
}

get styles() {
return {
progress: {
"width": `${this._handlePositionFromStart}%`,
"border": this._handlePositionFromStart === 0 ? "none" : "",
},
handle: {
[this.directionStart]: `${this._handlePositionFromStart}%`,
},
};
}

get _sliderHandle() : HTMLElement {
return this.shadowRoot!.querySelector(".ui5-slider-handle")!;
return this.shadowRoot!.querySelector("[ui5-slider-handle]")!;
}

get _ariaDisabled() {
Expand All @@ -399,19 +350,24 @@
return Slider.i18nBundle.getText(SLIDER_TOOLTIP_INPUT_LABEL);
}

// TODO: Refactor these methods after RangeSlider is refactored

Check warning on line 353 in packages/main/src/Slider.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected 'todo' comment: 'TODO: Refactor these methods after...'
get tickmarksObject() {
const count = this._tickmarksCount;
const arr = [];
return [];
}

if (this._hiddenTickmarks) {
return [true, false];
}
_onkeydown(e: KeyboardEvent) {
const target = e.target as HTMLElement;

for (let i = 0; i <= count; i++) {
arr.push(this._effectiveMin + (i * this.step) <= this.value);
if (isF2(e) && target.hasAttribute("ui5-slider-handle")) {
(target.parentNode!.querySelector("[ui5-slider-tooltip]") as HTMLElement).focus();
}

return arr;
if (SliderBase._isActionKey(e) && target && !target.hasAttribute("ui5-slider-tooltip")) {
e.preventDefault();

this._isUserInteraction = true;
this._handleActionKeyPress(e);
}
}
}

Expand Down
91 changes: 91 additions & 0 deletions packages/main/src/SliderHandle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js";
import SliderHandleTemplate from "./SliderHandleTemplate.js";
import styles from "./generated/themes/SliderHandle.css.js";
import type { SliderScaleOrientation } from "./SliderScale.js";

/**
* @class
* The <code>ui5-slider-handle</code> component represents the handle of the <code>ui5-slider</code> component.
*
* @constructor
* @extends UI5Element
* @since 2.19.0
* @private
*/
@customElement({
tag: "ui5-slider-handle",
renderer: jsxRenderer,
template: SliderHandleTemplate,
styles,
})
class SliderHandle extends UI5Element {
/**
* Defines the value of the slider handle.
* <br><br>
* <b>Note:</b> The value should be between the <code>min</code> and <code>max</code> properties of the parent <code>ui5-slider</code>.
* @since 2.19.0
* @public
*/
@property({ type: Number })
value = 0;

/**
* Defines the minimum value of the slider handle.
* <br><br>
* <b>Note:</b> The value should be less than the <code>max</code> property of the parent <code>ui5-slider</code>.
* @since 2.19.0
* @public
*/
@property({ type: Number })
min = 0;

/**
* Defines the maximum value of the slider handle.
* <br><br>
* <b>Note:</b> The value should be greater than the <code>min</code> property of the parent <code>ui5-slider</code>.
* @since 2.19.0
* @public
*/
@property({ type: Number })
max = 100;

/**
* Defines whether the slider handle is disabled.
* <br><br>
* <b>Note:</b> A disabled slider handle cannot be interacted with.
* @since 2.19.0
* @public
*/
@property({ type: Boolean })
disabled = false;

/**
* Defines whether the slider handle is active.
* <br><br>
* <b>Note:</b> An active slider handle is currently being interacted with.
* @since 2.19.0
* @public
*/
@property({ type: Boolean })
active = false;

@property()
orientation: `${SliderScaleOrientation}` = "Horizontal";

get _handlePosition() {
const range = this.max - this.min;
const position = ((this.value - this.min) / range) * 100;
return position;
}

getFocusDomRef(): HTMLElement | undefined {
return this;
}
}

SliderHandle.define();

export default SliderHandle;
15 changes: 15 additions & 0 deletions packages/main/src/SliderHandleTemplate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import directionArrows from "@ui5/webcomponents-icons/dist/direction-arrows.js";
import Icon from "./Icon.js";
import type SliderHandle from "./SliderHandle.js";

export default function SliderHandleTemplate(this: SliderHandle) {
return (
<div class="ui5-slider-handle-container">
<Icon name={directionArrows}
mode="Decorative"
part="icon-slider"
slider-icon
></Icon>
</div>
);
}
Loading
Loading