-
Notifications
You must be signed in to change notification settings - Fork 273
feat(ui5-slider): Add Slider component #2349
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
46 commits
Select commit
Hold shift + click to select a range
92d3e95
Initial Slider POC essentials
ndeshev c67f585
Minor formatting fixes
ndeshev 831da57
RangeSlider functionality improvements
ndeshev cd7e00d
Range selection functionality wip
ndeshev 1954979
Range Slider concepts implemented
ndeshev 415da4e
Most of the comments fixed
ndeshev f99f666
showTooltip property added for tooltiping current value, minor code c…
ndeshev 15f3eaf
Tooltip functionality is introduced
ndeshev 68d3400
Labels introduced
ndeshev b671445
Slider and Range Slider separation
ndeshev 5eb2b54
Slider separation from the Range Slider functionality
ndeshev a6c4b34
SliderBase class introduced as a foundation of the Slider and Range S…
ndeshev 5c26fc7
Dynamic manipulation of the inline styles fixed, some other minor cha…
ndeshev dd58f68
Bundling fix
ndeshev 67da54a
Inline styling fixes
ndeshev ce40132
Minor fixes
ndeshev 97afe65
Minor fixes 2
ndeshev db2f2fa
Cleanup & refactoring of the UI sync and onBeforeRendering cycle
ndeshev a825d06
Small formatting fixes
ndeshev 74ccf26
Stepiphying the values now is improved as well as some other calculat…
ndeshev 004f97b
calc improvements
ndeshev d6014de
Refactoring, resize handling implemented
ndeshev db456ef
Added support (handling) for touch and pointer event types
ndeshev 1705983
Fiori 3 and Belize theming
ndeshev 652de00
The high contrast variants of the Fiori 3 and Belize themes are added
ndeshev 577c688
Refactor private/protected methods
ndeshev 494694b
Add state storage for tracking state changes
ndeshev 0e5e881
Introduce template inheritance. Slider & RangeSlider templates inheri…
ndeshev d9c9d87
Fix template inheritance
ndeshev ecc630f
Fixes and improvements from the code reviews
ndeshev e692495
Add playground samples, add aria-labelledby attribute to Slider's handle
ndeshev 04685d6
fix touch handling bug, add since tag in the sample header, remove ar…
ndeshev da25fb0
InvisibleStyles removed from css
ndeshev e0a2350
Add RTL Support
ndeshev 2c567d8
Add more documentation
ndeshev f2397a8
Add additional validations and normalizations, refactoring
ndeshev 92055bc
Modify sample with the new name of the tickmarks prop
ndeshev dfd9375
Add tests, fix synchronization of the UI when min and max properties …
ndeshev 0e4d37c
Perform linting
ndeshev 3b68e23
Minor
ndshv 0d878b0
Fix syncUIAndState bug. Show tooltips on disabled sliders
ndshv 29f5f5f
Fix slider movement on initial mouse down of the handle
ndeshev cb80ab4
Fix tests according to the synched main repo
ndeshev c9c0e0b
Add appenddocs and since tags, sample docs fix
ndeshev a485770
Fix code reviews proposals
ndeshev 26be6ac
Remove unnecessary log file and whitespace
ndeshev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{{>include "./SliderBase.hbs"}} | ||
|
||
{{#*inline "handles"}} | ||
<div class="ui5-slider-handle" style="{{styles.handle}}"> | ||
{{#if showTooltip}} | ||
<div class="ui5-slider-tooltip" style="{{styles.tooltip}}"> | ||
<span class="ui5-slider-tooltip-value">{{tooltipValue}}</span> | ||
</div> | ||
{{/if}} | ||
</div> | ||
{{/inline}} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
import Float from "@ui5/webcomponents-base/dist/types/Float.js"; | ||
import { fetchI18nBundle, getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js"; | ||
import SliderBase from "./SliderBase.js"; | ||
|
||
// Template | ||
import SliderTemplate from "./generated/templates/SliderTemplate.lit.js"; | ||
|
||
/** | ||
* @public | ||
*/ | ||
const metadata = { | ||
tag: "ui5-slider", | ||
languageAware: true, | ||
managedSlots: true, | ||
properties: /** @lends sap.ui.webcomponents.main.Slider.prototype */ { | ||
/** | ||
* Current value of the slider | ||
* | ||
* @type {Float} | ||
* @defaultvalue 0 | ||
* @public | ||
*/ | ||
value: { | ||
ndeshev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
type: Float, | ||
defaultValue: 0, | ||
}, | ||
}, | ||
}; | ||
|
||
/** | ||
* @class | ||
* | ||
* <h3 class="comment-api-title">Overview</h3> | ||
ndeshev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* The Slider component represents a numerical range and a handle (grip). | ||
ndeshev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* The purpose of the component is to enable visual selection of a value in | ||
* a continuous numerical range by moving an adjustable handle. | ||
* | ||
* <h3>Structure</h3> | ||
* The most important properties of the Slider are: | ||
* <ul> | ||
* <li>min - The minimum value of the slider range</li> | ||
* <li>max - The maximum value of the slider range</li> | ||
* <li>value - The current value of the slider</li> | ||
* <li>step - Determines the increments in which the slider will move</li> | ||
* <li>showTooltip - Determines if a tooltip should be displayed above the handle</li> | ||
* <li>showTickmarks - Displays a visual divider between the step values</li> | ||
* <li>labelInterval - Labels some or all of the tickmarks with their values.</li> | ||
* </ul> | ||
* | ||
* <h3>Usage</h3> | ||
* The most common usecase is to select values on a continuous numerical scale (e.g. temperature, volume, etc. ). | ||
* | ||
* <h3>Responsive Behavior</h3> | ||
* The <code>ui5-slider</code> component adjusts to the size of its parent container by recalculating and | ||
* resizing the width of the control. You can move the slider handle in several different ways: | ||
* <ul> | ||
* <li>Drag and drop to the desired value</li> | ||
* <li>Click/tap on the range bar to move the handle to that location</li> | ||
* </ul> | ||
* | ||
* <h3>ES6 Module Import</h3> | ||
* | ||
* <code>import "@ui5/webcomponents/dist/Slider";</code> | ||
* | ||
* @constructor | ||
* @author SAP SE | ||
* @alias sap.ui.webcomponents.main.Slider | ||
* @extends sap.ui.webcomponents.base.UI5Element | ||
* @tagname ui5-slider | ||
* @since 1.0.0-rc.11 | ||
* @appenddocs SliderBase | ||
* @public | ||
*/ | ||
class Slider extends SliderBase { | ||
static get metadata() { | ||
return metadata; | ||
} | ||
|
||
static get template() { | ||
return SliderTemplate; | ||
} | ||
|
||
constructor() { | ||
super(); | ||
this._stateStorage.value = null; | ||
this.i18nBundle = getI18nBundle("@ui5/webcomponents"); | ||
} | ||
|
||
/** | ||
* | ||
* Check if the previously saved state is outdated. That would mean | ||
* either it is the initial rendering or that a property has been changed | ||
* programatically - because the previous state is always updated in | ||
* the interaction handlers. | ||
* | ||
* Normalize current properties, update the previously stored state. | ||
* Update the visual UI representation of the Slider | ||
* | ||
*/ | ||
onBeforeRendering() { | ||
if (!this.isCurrentStateOutdated()) { | ||
return; | ||
} | ||
|
||
this.notResized = true; | ||
this.syncUIAndState("value"); | ||
this._updateHandleAndProgress(this.value); | ||
} | ||
|
||
/** | ||
* Called when the user starts interacting with the slider | ||
* | ||
* @private | ||
*/ | ||
_onmousedown(event) { | ||
// 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.step === 0) { | ||
return; | ||
} | ||
|
||
const newValue = this.handleDownBase(event, this._effectiveMin, this._effectiveMax); | ||
|
||
// Do not yet update the Slider if press is over a handle. It will be updated if the user drags the mouse. | ||
if (!this._isHandlePressed(this.constructor.getPageXValueFromEvent(event))) { | ||
this._updateHandleAndProgress(newValue); | ||
this.updateValue("value", newValue); | ||
} | ||
} | ||
|
||
/** | ||
* Called when the user moves the slider | ||
* | ||
* @private | ||
*/ | ||
_handleMove(event) { | ||
event.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 newValue = this.constructor.getValueFromInteraction(event, this._effectiveStep, this._effectiveMin, this._effectiveMax, this.getBoundingClientRect(), this.directionStart); | ||
|
||
this._updateHandleAndProgress(newValue); | ||
this.updateValue("value", newValue); | ||
} | ||
|
||
/** Called when the user finish interacting with the slider | ||
* | ||
* @private | ||
*/ | ||
_handleUp(event) { | ||
this.handleUpBase(); | ||
} | ||
|
||
/** Determines if the press is over the handle | ||
* | ||
* @private | ||
*/ | ||
_isHandlePressed(clientX) { | ||
const sliderHandle = this.shadowRoot.querySelector(".ui5-slider-handle"); | ||
const sliderHandleDomRect = sliderHandle.getBoundingClientRect(); | ||
|
||
return clientX >= sliderHandleDomRect.left && clientX <= sliderHandleDomRect.right; | ||
} | ||
|
||
|
||
/** Updates the UI representation of the progress bar and handle position | ||
* | ||
* @private | ||
*/ | ||
_updateHandleAndProgress(newValue) { | ||
const max = this._effectiveMax; | ||
const min = this._effectiveMin; | ||
|
||
// The progress (completed) percentage of the slider. | ||
this._progressPercentage = (newValue - min) / (max - min); | ||
// How many pixels from the left end of the slider will be the placed the affected by the user action handle | ||
this._handlePositionFromStart = this._progressPercentage * 100; | ||
} | ||
|
||
get styles() { | ||
return { | ||
progress: { | ||
"transform": `scaleX(${this._progressPercentage})`, | ||
"transform-origin": `${this.directionStart} top`, | ||
}, | ||
handle: { | ||
[this.directionStart]: `${this._handlePositionFromStart}%`, | ||
}, | ||
tickmarks: { | ||
"background": `${this._tickmarks}`, | ||
}, | ||
label: { | ||
"width": `${this._labelWidth}%`, | ||
}, | ||
labelContainer: { | ||
"width": `100%`, | ||
[this.directionStart]: `-${this._labelWidth / 2}%`, | ||
}, | ||
tooltip: { | ||
"visibility": `${this._tooltipVisibility}`, | ||
}, | ||
}; | ||
} | ||
|
||
get labelItems() { | ||
return this._labelItems; | ||
} | ||
|
||
get tooltipValue() { | ||
const stepPrecision = this.constructor._getDecimalPrecisionOfNumber(this._effectiveStep); | ||
return this.value.toFixed(stepPrecision); | ||
} | ||
|
||
static async onDefine() { | ||
await fetchI18nBundle("@ui5/webcomponents"); | ||
} | ||
} | ||
|
||
Slider.define(); | ||
|
||
export default Slider; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<div | ||
ilhan007 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
class="ui5-slider-root" | ||
@mousedown="{{_onmousedown}}" | ||
@touchstart="{{_ontouchstart}}" | ||
@mouseover="{{_onmouseover}}" | ||
@mouseout="{{_onmouseout}}" | ||
dir="{{effectiveDir}}" | ||
> | ||
<div class="ui5-slider-inner"> | ||
<div class="ui5-slider-progress-container"> | ||
<div class="ui5-slider-progress" style="{{styles.progress}}"></div> | ||
</div> | ||
|
||
{{#if step}} | ||
{{#if showTickmarks}} | ||
<div class="ui5-slider-tickmarks" style="{{styles.tickmarks}}"></div> | ||
{{#if labelInterval}} | ||
<ul class="ui5-slider-labels {{classes.labelContainer}}" style="{{styles.labelContainer}}"> | ||
{{#each _labels}} | ||
<li style="{{../styles.label}}">{{this}}</li> | ||
{{/each}} | ||
</ul> | ||
{{/if}} | ||
{{/if}} | ||
{{/if}} | ||
{{> handles}} | ||
</div> | ||
</div> | ||
|
||
{{#*inline "handles"}}{{/inline}} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tooltip is not in the static area. Was that discussed? Probably the tooltip can be cut if there are overflow: hidden elements on the page. I guess it's not the biggest issue and makes the implementation much simpler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. this was not discussed previously. It will be good if it is ok to refactor this in the near future.