diff --git a/packages/main/bundle.esm.js b/packages/main/bundle.esm.js index 7826b36556d6..5fb6eb14eba1 100644 --- a/packages/main/bundle.esm.js +++ b/packages/main/bundle.esm.js @@ -61,11 +61,13 @@ import TableCell from "./dist/TableCell.js"; import TextArea from "./dist/TextArea.js"; import Timeline from "./dist/Timeline.js"; import TimelineItem from "./dist/TimelineItem.js"; +import TimePicker from "./dist/TimePicker.js"; import Title from "./dist/Title.js"; import Toast from "./dist/Toast.js"; import ToggleButton from "./dist/ToggleButton.js"; + import List from "./dist/List.js"; import StandardListItem from "./dist/StandardListItem.js"; import CustomListItem from "./dist/CustomListItem.js"; diff --git a/packages/main/src/TimePicker.hbs b/packages/main/src/TimePicker.hbs new file mode 100644 index 000000000000..6231e2177dcf --- /dev/null +++ b/packages/main/src/TimePicker.hbs @@ -0,0 +1,24 @@ +
+ + {{#unless readonly}} + + {{/unless}} + +
\ No newline at end of file diff --git a/packages/main/src/TimePicker.js b/packages/main/src/TimePicker.js new file mode 100644 index 000000000000..52846e8c5a0b --- /dev/null +++ b/packages/main/src/TimePicker.js @@ -0,0 +1,753 @@ +import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js"; +import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js"; +import { fetchI18nBundle, getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js"; +import getLocale from "@ui5/webcomponents-base/dist/locale/getLocale.js"; +import ValueState from "@ui5/webcomponents-base/dist/types/ValueState.js"; +import DateFormat from "@ui5/webcomponents-localization/dist/DateFormat.js"; +import LocaleData from "@ui5/webcomponents-localization/dist/LocaleData.js"; +import { fetchCldr } from "@ui5/webcomponents-base/dist/asset-registries/LocaleData.js"; +import { isPhone } from "@ui5/webcomponents-base/dist/Device.js"; +import { + isLeft, + isRight, + isTabNext, + isTabPrevious, + isShow, +} from "@ui5/webcomponents-base/src/Keys.js"; +import "@ui5/webcomponents-icons/dist/icons/time-entry-request.js"; +import PopoverHorizontalAlign from "./types/PopoverHorizontalAlign.js"; +import ResponsivePopover from "./ResponsivePopover.js"; +import PopoverPlacementType from "./types/PopoverPlacementType.js"; +import TimePickerTemplate from "./generated/templates/TimePickerTemplate.lit.js"; +import TimePickerPopoverTemplate from "./generated/templates/TimePickerPopoverTemplate.lit.js"; +import Input from "./Input.js"; +import WheelSlider from "./WheelSlider.js"; +import { + TIMEPICKER_HOURS_LABEL, + TIMEPICKER_MINUTES_LABEL, + TIMEPICKER_SECONDS_LABEL, + TIMEPICKER_PERIODS_LABEL, + TIMEPICKER_SUBMIT_BUTTON, + TIMEPICKER_CANCEL_BUTTON, +} from "./generated/i18n/i18n-defaults.js"; + +// Styles +import TimePickerCss from "./generated/themes/TimePicker.css.js"; +import TimePickerPopoverCss from "./generated/themes/TimePickerPopover.css.js"; +import ResponsivePopoverCommonCss from "./generated/themes/ResponsivePopoverCommon.css.js"; + +/** + * @public + */ +const metadata = { + tag: "ui5-timepicker", + properties: /** @lends sap.ui.webcomponents.main.TimePicker.prototype */ { + /** + * Defines a formatted time value. + * + * @type {string} + * @defaultvalue "" + * @public + */ + value: { + type: String, + defaultValue: "", + }, + + /** + * Determines the format, displayed in the input field. + * + * Example: + * HH:mm:ss -> 11:42:35 + * hh:mm:ss a -> 2:23:15 PM + * mm:ss -> 12:04 (only minutes and seconds) + * + * @type {string} + * @defaultvalue "" + * @public + */ + formatPattern: { + type: String, + }, + + /** + * Visualizes the validation state of the Web Component, for example + * Error, Warning and + * Success. + * + * @type {string} + * @defaultvalue "None" + * @public + */ + valueState: { + type: ValueState, + defaultValue: ValueState.None, + }, + + /** + * Determines whether the ui5-timepicker is displayed as disabled. + * + * @type {boolean} + * @defaultvalue false + * @public + */ + disabled: { + type: Boolean, + }, + + /** + * Determines whether the ui5-timepicker is displayed as readonly. + * + * @type {boolean} + * @defaultvalue false + * @public + */ + readonly: { + type: Boolean, + }, + + _isPickerOpen: { + type: Boolean, + noAttribute: true, + }, + + _respPopover: { + type: Object, + }, + + _hours: { + type: String, + }, + + _minutes: { + type: String, + }, + + _seconds: { + type: String, + }, + }, + slots: /** @lends sap.ui.webcomponents.main.TimePicker.prototype */ { + // + }, + events: /** @lends sap.ui.webcomponents.main.TimePicker.prototype */ { + /** + * Fired when the input operation has finished by pressing Enter or on focusout. + * + * @event + * @public + */ + change: {}, + /** + * Fired when the value of the ui5-timepicker is changed at each key stroke. + * + * @event + * @public + */ + input: {}, + }, +}; + +/** + * @class + * + *

Overview

+ * The ui5-timepicker component provides an input field with assigned sliders which opens on user action. + * The ui5-timepicker allows users to select a localized time using touch, + * mouse, or keyboard input. It consists of two parts: the time input field and the + * sliders. + * + *

Usage

+ * The user can enter a time by: + * + *

+ * When the user makes an entry and chooses the enter key, the sliders shows the corresponding time. + * When the user directly triggers the sliders display, the actual time is displayed. + * For the ui5-timepicker + * + *

Formatting

+ * + * If a time is entered by typing it into + * the input field, it must fit to the used time format. + *

+ * Supported format options are pattern-based on Unicode LDML Date Format notation. + * For more information, see UTS #35: Unicode Locale Data Markup Language. + *

+ * For example, if the format-pattern is "HH:mm:ss", + * a valid value string is "11:42:35" and the same is displayed in the input. + * + *

ES6 Module Import

+ * + * import @ui5/webcomponents/dist/TimePicker.js"; + * + * @constructor + * @author SAP SE + * @alias sap.ui.webcomponents.main.TimePicker + * @extends UI5Element + * @tagname ui5-timepicker + * @public + * @since 1.0.0-rc.6 + */ +class TimePicker extends UI5Element { + static get metadata() { + return metadata; + } + + static get render() { + return litRender; + } + + static get styles() { + return TimePickerCss; + } + + static get staticAreaTemplate() { + return TimePickerPopoverTemplate; + } + + static get template() { + return TimePickerTemplate; + } + + static async onDefine() { + await Promise.all([ + fetchCldr(getLocale().getLanguage(), getLocale().getRegion(), getLocale().getScript()), + ResponsivePopover.define(), + fetchI18nBundle("@ui5/webcomponents"), + WheelSlider.define(), + Input.define(), + ]); + } + + static get staticAreaStyles() { + return [TimePickerPopoverCss, ResponsivePopoverCommonCss]; + } + + + constructor() { + super(); + + this.readonly = false; + this.disabled = false; + this._isPickerOpen = false; + this.i18nBundle = getI18nBundle("@ui5/webcomponents"); + + this._respPopover = { + placementType: PopoverPlacementType.Bottom, + horizontalAlign: PopoverHorizontalAlign.Left, + allowTargetOverlap: true, + stayOpenOnScroll: true, + afterClose: () => { + this._isPickerOpen = false; + this.closePicker(); + }, + }; + + this._hoursParameters = { + minHour: 0, + maxHour: 0, + isTwelveHoursFormat: false, + }; + + this._slidersDomRefs = []; + } + + onBeforeRendering() { + if (!this.formatPattern) { + this.formatPattern = LocaleData.getInstance(getLocale()).getTimePattern(this.getFormat().oFormatOptions.style); + } + + if (!this.value) { + this.value = this.getFormat().format(new Date()); + } + this._initHoursFormatParameters(); + } + + _handleInputChange() { + const nextValue = this._getInput().getInputValue(), + isValid = this.isValid(nextValue); + + this.setValue(nextValue); + this.fireEvent("change", { value: nextValue, valid: isValid }); + this.fireEvent("value-changed", { value: nextValue, valid: isValid }); + } + + _handleInputLiveChange() { + const nextValue = this._getInput().getInputValue(), + isValid = this.isValid(nextValue); + + this.value = nextValue; + this.setSlidersValue(); + this.fireEvent("input", { value: nextValue, valid: isValid }); + } + + setSlidersValue() { + const currentDate = this._getInput() ? this.getFormat().parse(this._getInput().getAttribute("value")) : null, + secondsSlider = this.secondsSlider, + minutesSlider = this.minutesSlider, + hoursSlider = this.hoursSlider, + periodsSlider = this.periodsSlider; + + if (!currentDate) { + return; + } + if (hoursSlider) { + let tempValue = ""; + if (this._hoursParameters.isTwelveHoursFormat && currentDate.getHours() > this._hoursParameters.maxHour) { + tempValue = currentDate.getHours() - 12; + } else if (this._hoursParameters.isTwelveHoursFormat && currentDate.getHours() < this._hoursParameters.minHour) { + tempValue = currentDate.getHours() + 12; + } else { + tempValue = currentDate.getHours(); + } + if (tempValue.toString().length === 1) { + hoursSlider.value = `0${tempValue}`; + } else { + hoursSlider.value = tempValue.toString(); + } + } + if (minutesSlider) { + const tempValue = currentDate.getMinutes(); + if (tempValue.toString().length === 1) { + minutesSlider.value = `0${tempValue}`; + } else { + minutesSlider.value = tempValue.toString(); + } + } + if (secondsSlider) { + const tempValue = currentDate.getSeconds(); + if (tempValue.toString().length === 1) { + secondsSlider.value = `0${tempValue}`; + } else { + secondsSlider.value = tempValue.toString(); + } + } + if (this._hoursParameters.isTwelveHoursFormat && periodsSlider && this._hoursParameters.minHour === 1) { + periodsSlider.value = currentDate.getHours() > this._hoursParameters.maxHour ? this.periodsArray[1] : this.periodsArray[0]; + } else if (this._hoursParameters.isTwelveHoursFormat && periodsSlider) { + periodsSlider.value = (currentDate.getHours() > this._hoursParameters.maxHour || currentDate.getHours() === this._hoursParameters.minHour) ? this.periodsArray[1] : this.periodsArray[0]; + } + } + + /** + * Closes the picker + * @public + */ + async closePicker() { + await this._getPopover(); + this.responsivePopover.close(); + this._isPickerOpen = false; + + for (let i = 0; i < this._slidersDomRefs.length; i++) { + this._slidersDomRefs[i].collapseSlider(); + } + } + + /** + * Opens the picker. + * { focusInput: true } By default, the focus goes in the picker after opening it. + * Specify this option to focus the input field. + * @public + */ + async openPicker() { + await this._getPopover(); + this.responsivePopover.open(this); + this._isPickerOpen = true; + this._slidersDomRefs = await this.slidersDomRefs(); + + this.setSlidersValue(); + + if (this._slidersDomRefs[0]) { + this._slidersDomRefs[0].focus(); + } + } + + togglePicker() { + if (this.isOpen()) { + this.closePicker(); + this._isPickerOpen = false; + } else if (this._canOpenPicker()) { + this.openPicker(); + this._isPickerOpen = true; + } + } + + /** + * Checks if a value is valid against the current date format of the TimePicker + * @param {string} value A value to be tested against the current date format + * @public + */ + isOpen() { + return !!this._isPickerOpen; + } + + _canOpenPicker() { + return !this.disabled && !this.readonly; + } + + async _getPopover() { + const staticAreaItem = await this.getStaticAreaItemDomRef(); + this.responsivePopover = staticAreaItem.querySelector("ui5-responsive-popover"); + return this.responsivePopover; + } + + generateTimeItemsArray(x) { + const array = []; + for (let i = 0; i < x; i++) { + let tempString = i.toString(); + if (tempString.length === 1) { + tempString = `0${tempString}`; + } + + array.push(tempString); + } + + return array; + } + + get secondsArray() { + return this.generateTimeItemsArray(60); + } + + get minutesArray() { + return this.generateTimeItemsArray(60); + } + + get hoursArray() { + let hoursValueArray = []; + + if (this._hoursParameters.isTwelveHoursFormat) { + hoursValueArray = this.generateTimeItemsArray(12); + } else { + hoursValueArray = this.generateTimeItemsArray(24); + } + + if (this._hoursParameters.minHour === 1) { + for (let i = 0; i < hoursValueArray.length; i++) { + const tempValue = hoursValueArray[i] * 1 + 1; + + if (tempValue.toString().length === 1) { + hoursValueArray[i] = `0${tempValue.toString()}`; + } else { + hoursValueArray[i] = tempValue.toString(); + } + } + } + + return hoursValueArray; + } + + get periodsArray() { + return this.getFormat().aDayPeriods.map(x => x.toUpperCase()); + } + + async slidersDomRefs() { + await this._getPopover(); + return this.responsivePopover.default.length ? [...this.responsivePopover.default[0].children].filter(x => x.isUI5Element) : this.responsivePopover.default; + } + + _getInput() { + return this.shadowRoot.querySelector("ui5-input"); + } + + get secondsSlider() { + return this.responsivePopover && this.responsivePopover.querySelector(".ui5-timepicker-seconds-wheelslider"); + } + + get minutesSlider() { + return this.responsivePopover && this.responsivePopover.querySelector(".ui5-timepicker-minutes-wheelslider"); + } + + get hoursSlider() { + return this.responsivePopover && this.responsivePopover.querySelector(".ui5-timepicker-hours-wheelslider"); + } + + get periodsSlider() { + return this.responsivePopover && this.responsivePopover.querySelector(".ui5-timepicker-period-wheelslider"); + } + + submitPickers() { + const selectedDate = new Date(), + secondsSlider = this.secondsSlider, + minutesSlider = this.minutesSlider, + hoursSlider = this.hoursSlider, + periodsSlider = this.periodsSlider, + hours = hoursSlider ? hoursSlider.getAttribute("value") : this._hoursParameters.minHour.toString(), + minutes = minutesSlider ? minutesSlider.getAttribute("value") : "0", + seconds = secondsSlider ? secondsSlider.getAttribute("value") : "0", + period = periodsSlider ? periodsSlider.getAttribute("value") : this.periodsArray[0]; + + if (period === this.periodsArray[1]) { + selectedDate.setHours(hours * 1 + 12); + } else { + selectedDate.setHours(hours); + } + selectedDate.setMinutes(minutes); + selectedDate.setSeconds(seconds); + + this.setValue(this.getFormat().format(selectedDate)); + + this.fireEvent("change", { value: this.value, valid: true }); + + this.closePicker(); + } + + /** + * Opens the picker. + * @public + */ + isValid(value = "") { + return !!(value && this.getFormat().parse(value)); + } + + normalizeValue(sValue) { + return this.getFormat().format(this.getFormat().parse(sValue)); + } + + get _formatPattern() { + return this.formatPattern || "medium"; // get from config + } + + get _isPattern() { + return this._formatPattern !== "medium" && this._formatPattern !== "short" && this._formatPattern !== "long"; + } + + get _displayFormat() { + return this.getFormat().oFormatOptions.pattern; + } + + get _placeholder() { + return this.placeholder !== undefined ? this.placeholder : this._displayFormat; + } + + handleSliderClicked(event) { + if (event.target._expanded) { + this.openSlider(event.target.label); + } + } + + openSlider(label) { + for (let i = 0; i < this._slidersDomRefs.length; i++) { + if (this._slidersDomRefs[i].label !== label) { + this._slidersDomRefs[i].collapseSlider(); + } + } + } + + async _onfocuscontainerin(e) { + if (e.target !== e.currentTarget) { + return; + } + let sliders = []; + if (this._slidersDomRefs.length) { + sliders = await this.slidersDomRefs(); + } else { + sliders = this._slidersDomRefs; + } + if (sliders[0]) { + sliders[0].focus(); + } + } + + _oncontainerkeydown(e) { + if (isLeft(e)) { + let expandedSliderIndex = 0; + for (let i = 0; i < this._slidersDomRefs.length; i++) { + if (this._slidersDomRefs[i]._expanded) { + expandedSliderIndex = i; + } + } + if (this._slidersDomRefs[expandedSliderIndex - 1]) { + this._slidersDomRefs[expandedSliderIndex - 1].focus(); + } else { + this._slidersDomRefs[this._slidersDomRefs.length - 1].focus(); + } + } else if (isRight(e)) { + let expandedSliderIndex = 0; + + for (let i = 0; i < this._slidersDomRefs.length; i++) { + if (this._slidersDomRefs[i]._expanded) { + expandedSliderIndex = i; + } + } + if (this._slidersDomRefs[expandedSliderIndex + 1]) { + this._slidersDomRefs[expandedSliderIndex + 1].focus(); + } else { + this._slidersDomRefs[0].focus(); + } + } + + if (isTabNext(e) && e.target === this._slidersDomRefs[this._slidersDomRefs.length - 1]) { + e.preventDefault(); + this.getStaticAreaItemDomRef().querySelector(".ui5-timepicker-footer").firstElementChild.focus(); + } else if (isTabPrevious(e) && e.target === this._slidersDomRefs[0]) { + e.preventDefault(); + this.getStaticAreaItemDomRef().querySelector(`.ui5-timepicker-footer`).lastElementChild.focus(); + } + } + + _onfooterkeydown(e) { + if (isTabNext(e) && e.target === e.target.parentElement.lastElementChild) { + e.preventDefault(); + this._slidersDomRefs[0].focus(); + } + + if (isTabPrevious(e) && e.target === e.target.parentElement.firstElementChild) { + e.preventDefault(); + this._slidersDomRefs[this._slidersDomRefs.length - 1].focus(); + } + } + + _ontimepickerkeydown(e) { + this._handleTimepickerKeysDown(e); + } + + _ontimepickerpopoverkeydown(e) { + this._handleTimepickerKeysDown(e); + } + + _handleTimepickerKeysDown(e) { + if (isShow(e)) { + e.preventDefault(); + this.togglePicker(); + } + } + + getFormat() { + if (this._isPattern) { + this._oDateFormat = DateFormat.getInstance({ + pattern: this._formatPattern, + }); + } else { + this._oDateFormat = DateFormat.getInstance({ + style: this._formatPattern, + }); + } + + return this._oDateFormat; + } + + setValue(value) { + if (this.isValid(value)) { + this.value = this.normalizeValue(value); + this.setSlidersValue(); + this.valueState = ValueState.None; + } else { + this.valueState = ValueState.Error; + } + } + + _getSlidersContained() { + const formatArray = this.getFormat().aFormatArray, + slidersBuildArray = [false, false, false, false]; // hours minutes seconds am/pm + + for (let i = 0; i < formatArray.length; i++) { + if (this._hoursParameters.maxHour !== 0) { + slidersBuildArray[0] = true; + } + if (this._hoursParameters.maxHour !== 0 && this._hoursParameters.isTwelveHoursFormat) { + slidersBuildArray[0] = true; + } + if (formatArray[i].type === "minute") { + slidersBuildArray[1] = true; + } + if (formatArray[i].type === "second") { + slidersBuildArray[2] = true; + } + if (formatArray[i].type === "amPmMarker") { + slidersBuildArray[3] = true; + } + } + + return slidersBuildArray; + } + + _initHoursFormatParameters() { + const formatArray = this.getFormat().aFormatArray; + + if (formatArray[0].type === "hour0_23") { + this._hoursParameters.minHour = 0; + this._hoursParameters.maxHour = 23; + this._hoursParameters.isTwelveHoursFormat = false; + } else if (formatArray[0].type === "hour1_24") { + this._hoursParameters.minHour = 1; + this._hoursParameters.maxHour = 24; + this._hoursParameters.isTwelveHoursFormat = false; + } else if (formatArray[0].type === "hour0_11") { + this._hoursParameters.minHour = 0; + this._hoursParameters.maxHour = 11; + this._hoursParameters.isTwelveHoursFormat = true; + } else if (formatArray[0].type === "hour1_12") { + this._hoursParameters.minHour = 1; + this._hoursParameters.maxHour = 12; + this._hoursParameters.isTwelveHoursFormat = true; + } + } + + /** + * Currently selected date represented as JavaScript Date instance + * + * @readonly + * @type { Date } + * @public + */ + get dateValue() { + return this.getFormat().parse(this.value); + } + + get shouldBuildHoursSlider() { + return this._getSlidersContained()[0]; + } + + get shouldBuildMinutesSlider() { + return this._getSlidersContained()[1]; + } + + get shouldBuildSecondsSlider() { + return this._getSlidersContained()[2]; + } + + get shouldBuildPeriodsSlider() { + return this._getSlidersContained()[3]; + } + + get hoursSliderTitle() { + return this.i18nBundle.getText(TIMEPICKER_HOURS_LABEL); + } + + get minutesSliderTitle() { + return this.i18nBundle.getText(TIMEPICKER_MINUTES_LABEL); + } + + get secondsSliderTitle() { + return this.i18nBundle.getText(TIMEPICKER_SECONDS_LABEL); + } + + get periodSliderTitle() { + return this.i18nBundle.getText(TIMEPICKER_PERIODS_LABEL); + } + + get submitButtonLabel() { + return this.i18nBundle.getText(TIMEPICKER_SUBMIT_BUTTON); + } + + get cancelButtonLabel() { + return this.i18nBundle.getText(TIMEPICKER_CANCEL_BUTTON); + } + + get classes() { + return { + container: { + "ui5-timepicker-sliders-container": true, + "ui5-phone": isPhone(), + }, + }; + } +} + +TimePicker.define(); + +export default TimePicker; diff --git a/packages/main/src/TimePickerPopover.hbs b/packages/main/src/TimePickerPopover.hbs new file mode 100644 index 000000000000..fdb6bd6b44a8 --- /dev/null +++ b/packages/main/src/TimePickerPopover.hbs @@ -0,0 +1,52 @@ + +
+ {{#if shouldBuildHoursSlider}} + + {{/if}} + {{#if shouldBuildMinutesSlider}} + + {{/if}} + {{#if shouldBuildSecondsSlider}} + + {{/if}} + {{#if shouldBuildPeriodsSlider}} + + {{/if}} +
+ +
\ No newline at end of file diff --git a/packages/main/src/WheelSlider.hbs b/packages/main/src/WheelSlider.hbs new file mode 100644 index 000000000000..2d5b825edf6e --- /dev/null +++ b/packages/main/src/WheelSlider.hbs @@ -0,0 +1,40 @@ +
+
+
{{label}}
+
+ +
+
+
+
+
+ {{#if _expanded}} +
    + {{#each items}} +
  • {{this}}
  • + {{/each}} +
+ {{else}} +
    +
  • {{value}}
  • +
+ {{/if}} +
+
+ +
\ No newline at end of file diff --git a/packages/main/src/WheelSlider.js b/packages/main/src/WheelSlider.js new file mode 100644 index 000000000000..ebebcfde613e --- /dev/null +++ b/packages/main/src/WheelSlider.js @@ -0,0 +1,285 @@ +import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js"; +import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js"; +import { isPhone } from "@ui5/webcomponents-base/dist/Device.js"; +import { + isDown, + isUp, +} from "@ui5/webcomponents-base/src/Keys.js"; +import "@ui5/webcomponents-icons/dist/icons/navigation-up-arrow.js"; +import "@ui5/webcomponents-icons/dist/icons/navigation-down-arrow.js"; +import WheelSliderTemplate from "./generated/templates/WheelSliderTemplate.lit.js"; +import Button from "./Button.js"; + +// Styles +import WheelSliderCss from "./generated/themes/WheelSlider.css.js"; + +/** + * @private + */ +const metadata = { + tag: "ui5-wheelslider", + properties: /** @lends sap.ui.webcomponents.main.WheelSlider.prototype */ { + /** + * Defines whether the ui5-wheelslider is disabled + * (default is set to false). + * A disabled ui5-wheelslider can't be pressed or + * focused, and it is not in the tab chain. + * + * @type {boolean} + * @defaultvalue false + * @public + */ + disabled: { + type: Boolean, + }, + + /** + * Defines the currently selected value + * @type {string} + * @defaultvalue "" + * @public + */ + value: { + type: String, + }, + + /** + * Defines the label of the wheelslider. + * @type {string} + * @defaultvalue "" + * @public + */ + label: { + type: String, + defaultValue: "", + }, + + /** + * Indicates if the wheelslider is expanded. + * @type {boolean} + * @defaultvalue false + * @private + */ + _expanded: { + type: Boolean, + }, + + _items: { + type: Object, + }, + }, + slots: /** @lends sap.ui.webcomponents.main.WheelSlider.prototype */ { + + }, + events: /** @lends sap.ui.webcomponents.main.WheelSlider.prototype */ { + /** + * Fires when the wheel slider is expanded. + */ + expand: {}, + + /** + * Fires when the wheel slider is collapsed. + */ + collapse: {}, + + /** + * Fires when new value is selected. + */ + valueSelect: { + value: { + type: String, + }, + }, + }, +}; + +/** + * @class + * + *

Overview

+ * + * + *

Usage

+ * + * For the ui5-wheelslider + *

ES6 Module Import

+ * + * import @ui5/webcomponents/dist/WheelSlider.js"; + * + * @constructor + * @author SAP SE + * @alias sap.ui.webcomponents.main.WheelSlider + * @extends UI5Element + * @tagname ui5-wheelslider + * @public + * @since 1.0.0-rc.6 + */ +class WheelSlider extends UI5Element { + static get metadata() { + return metadata; + } + + static get render() { + return litRender; + } + + static get styles() { + return WheelSliderCss; + } + + static get template() { + return WheelSliderTemplate; + } + + constructor() { + super(); + this._currentElementIndex = 0; + this._itemCellHeight = 0; + } + + onBeforeRendering() { + this._updateItemCellHeight(); + } + + _updateItemCellHeight() { + this._itemCellHeight = this.shadowRoot.querySelectorAll(".ui5-wheelslider-item").length && Number(getComputedStyle(this.shadowRoot.querySelector(".ui5-wheelslider-item")).getPropertyValue("--_ui5_wheelslider_item_height").replace("rem", "")); + } + + static async onDefine() { + await Button.define(); + } + + onAfterRendering() { + if (this._expanded) { + const elements = this.shadowRoot.querySelectorAll(".ui5-wheelslider-item"); + for (let i = 0; i < elements.length; i++) { + if (elements[i].textContent === this.value) { + this._selectElement(elements[i]); + return true; + } + } + + this._selectElement(elements[0]); + } + } + + get items() { + return this._items || []; + } + + get classes() { + return { + root: { + "ui5-wheelslider-root": true, + "ui5-phone": isPhone(), + }, + }; + } + + _handleWheel(e) { + if (!e) { + return; + } + + e.stopPropagation(); + e.preventDefault(); + + if (e.timeStamp === this._prevWheelTimestamp || !this._expanded) { + return; + } + + if (e.deltaY > 0) { + this._onArrowUp(e); + } else if (e.deltaY < 0) { + this._onArrowDown(e); + } + + this._prevWheelTimestamp = e.timeStamp; + } + + _onclick(e) { + if (!e.target.classList.contains("ui5-wheelslider-item")) { + return; + } + + if (this._expanded) { + this.value = e.target.textContent; + this._selectElement(e.target); + this.fireEvent("valueSelect", { value: this.value }); + } else { + this._expanded = true; + } + } + + expandSlider() { + this._expanded = true; + this.fireEvent("expand", {}); + } + + collapseSlider() { + this._expanded = false; + this.fireEvent("collapse", {}); + } + + _selectElement(element) { + if (element && this._items.indexOf(element.textContent) > -1) { + this._currentElementIndex = this._items.indexOf(element.textContent); + this._selectElementByIndex(this._currentElementIndex); + } + } + + _selectElementByIndex(index) { + const sliderElement = this.shadowRoot.getElementById(`${this._id}--items-list`); + const itemsCount = this._items.length; + const itemCellHeight = this._itemCellHeight ? this._itemCellHeight : 2.875; + const offsetStep = isPhone() ? 4 : 2; + + if (index < itemsCount && index > -1) { + const offsetSelectedElement = offsetStep * itemCellHeight - (index * itemCellHeight); + sliderElement.setAttribute("style", `top:${offsetSelectedElement}rem`); + this.value = this._items[index]; + this._currentElementIndex = index; + this.fireEvent("valueSelect", { value: this.value }); + } + } + + _onArrowDown(e) { + e.preventDefault(); + const nextElementIndex = this._currentElementIndex + 1; + this._selectElementByIndex(nextElementIndex); + } + + _onArrowUp(e) { + e.preventDefault(); + const nextElementIndex = this._currentElementIndex - 1; + this._selectElementByIndex(nextElementIndex); + } + + _onkeydown(event) { + if (!this._expanded) { + return; + } + + if (isUp(event)) { + this._onArrowUp(event); + } + + if (isDown(event)) { + this._onArrowDown(event); + } + } + + _onfocusin(e) { + e.preventDefault(); + this.expandSlider(); + } + + _onfocusout(e) { + e.preventDefault(); + this.collapseSlider(); + } +} + +WheelSlider.define(); + +export default WheelSlider; diff --git a/packages/main/src/i18n/messagebundle.properties b/packages/main/src/i18n/messagebundle.properties index a8be709d1e0f..256b34f605d0 100644 --- a/packages/main/src/i18n/messagebundle.properties +++ b/packages/main/src/i18n/messagebundle.properties @@ -264,6 +264,24 @@ TEXTAREA_CHARACTERS_LEFT={0} characters remaining #XTXT: Text for characters over TEXTAREA_CHARACTERS_EXCEEDED={0} characters over limit +#XFLD: Timepicker slider header +TIMEPICKER_HOURS_LABEL=Hours + +#XFLD: Timepicker slider header +TIMEPICKER_MINUTES_LABEL=Minutes + +#XFLD: Timepicker slider header +TIMEPICKER_SECONDS_LABEL=Seconds + +#XFLD: Timepicker slider header +TIMEPICKER_PERIODS_LABEL=AM/PM + +#XFLD: Timepicker popover button +TIMEPICKER_SUBMIT_BUTTON=Ok + +#XFLD: Timepicker popover button +TIMEPICKER_CANCEL_BUTTON=Cancel + #XACT: ARIA announcement for token deletable TOKEN_ARIA_DELETABLE=Deletable diff --git a/packages/main/src/themes/TimePicker.css b/packages/main/src/themes/TimePicker.css new file mode 100644 index 000000000000..ca5b6d52ed40 --- /dev/null +++ b/packages/main/src/themes/TimePicker.css @@ -0,0 +1,18 @@ +:host(:not([hidden])) { + display: inline-block; +} + +.ui5-timepicker-input-icon-button:hover { + cursor: pointer; + background: var(--sapButton_Hover_Background); +} + +.ui5-timepicker-input-icon-button:active { + background-color: var(--sapButton_Active_Background); + color: var(--sapButton_Active_TextColor); +} + +.ui5-timepicker-input-icon-button[pressed] { + background-color: var(--sapButton_Active_Background); + color: var(--sapButton_Active_TextColor); +} \ No newline at end of file diff --git a/packages/main/src/themes/TimePickerPopover.css b/packages/main/src/themes/TimePickerPopover.css new file mode 100644 index 000000000000..09659675aa1a --- /dev/null +++ b/packages/main/src/themes/TimePickerPopover.css @@ -0,0 +1,29 @@ +.ui5-timepicker-sliders-container { + display: flex; + justify-content: center; + align-items: stretch; + direction: ltr; + padding: 0.5rem; + min-width: 18rem; +} + +.ui5-timepicker-sliders-container.ui5-phone{ + height: 90vh; +} + +.ui5-timepicker-footer { + height: fit-content; + display: flex; + justify-content: flex-end; + width: 100%; +} + +.ui5-timepicker-wheelslider { + padding-left: 0.25rem; + padding-right: 0.25rem; +} + +.ui5-timepicker-footer > ui5-button { + margin: 1%; + width: 20%; +} diff --git a/packages/main/src/themes/WheelSlider.css b/packages/main/src/themes/WheelSlider.css new file mode 100644 index 000000000000..07d3a4f757ca --- /dev/null +++ b/packages/main/src/themes/WheelSlider.css @@ -0,0 +1,191 @@ +.ui5-wheelslider-root { + overflow: hidden; + height: 100%; + vertical-align: middle; + text-align: center; + box-sizing: border-box; + font-family: var(--sapFontFamily); + justify-content: space-between; + flex-direction: column; + display: inline-flex; + width: var(--_ui5_wheelslider_item_width); +} + +.ui5-wheelslider-root .ui5-wheelslider-label { + box-sizing: border-box; + height: 2rem; + line-height: 2rem; + width: var(--_ui5_wheelslider_item_width); + font-size: var(--sapFontSmallSize); + color: var(--_ui5_wheelslider_label_text_color); + text-align: center; + vertical-align: middle; + visibility: hidden; +} + +.ui5-wheelslider-root .ui5-wheelslider-arrow { + visibility: hidden; + box-sizing: border-box; + height: 1.5rem; + user-select: none; + outline: none +} + +.ui5-wheelslider-root[expanded] .ui5-wheelslider-arrow { + visibility: var(--_ui5_wheelslider_arrows_visibility); + box-sizing: border-box; + border-color: transparent; + cursor: pointer; +} + +.ui5-wheelslider-root[expanded] .ui5-wheelslider-arrow:hover { + visibility: var(--_ui5_wheelslider_arrows_visibility); + box-sizing: border-box; + border-color: inherit; + cursor: pointer; +} + +.ui5-wheelslider-root .ui5-wheelslider-inner { + overflow: hidden; + height: 100%; + box-sizing: border-box; + user-select: none; + list-style: none; + margin-top: 0rem; +} + +.ui5-wheelslider-root[expanded] .ui5-wheelslider-inner { + margin-top: 0; +} + +.ui5-wheelslider-root .ui5-wheelslider-inner .ui5-wheelslider-item { + height: var(--_ui5_wheelslider_item_height); + line-height: var(--_ui5_wheelslider_item_height); + width: var(--_ui5_wheelslider_item_width); + box-sizing: border-box; + background: var(--_ui5_wheelslider_selected_item_background_color); + border: 0.625px solid var(--_ui5_wheelslider_item_border_color); + font-size: var(--_ui5_wheelslider_item_text_size); + color: var(--_ui5_wheelslider_collapsed_item_text_color); + text-align: center; + border-radius: var(--_ui_wheelslider_item_border_radius); + cursor: pointer; +} + +.ui5-wheelslider-root .ui5-wheelslider-inner .ui5-wheelslider-item:hover { + background: var(--_ui5_wheelslider_selected_item_hover_background_color); +} + +.ui5-wheelslider-root .ui5-wheelslider-inner .ui5-wheelslider-item:focus { + outline: 1px dotted black; + outline-offset: -3px; +} + +.ui5-wheelslider-root .ui5-wheelslider-inner .ui5-wheelslider-selection-frame { + width: var(--_ui5_wheelslider_item_width); + height: var(--_ui5_wheelslider_item_height); + position: absolute; + box-sizing: border-box; + visibility: hidden; + z-index: 1; +} + +.ui5-wheelslider-root .ui5-wheelslider-inner .ui5-wheelslider-wrapper > ul { + transition: all 400ms; + margin: 0; + padding: 0; + position: absolute; + top: var(--_ui5_wheelslider_selection_frame_margin_top); + cursor: pointer; + list-style-type: none; +} + +.ui5-wheelslider-root.ui5-phone .ui5-wheelslider-inner .ui5-wheelslider-wrapper > ul { + top: var(--_ui5_wheelslider_mobile_selection_frame_margin_top); +} + +.ui5-wheelslider-root[expanded] .ui5-wheelslider-inner .ui5-wheelslider-wrapper > ul { + list-style-type: none; + top: 0; +} + +.ui5-wheelslider-root .ui5-wheelslider-inner .ui5-wheelslider-wrapper { + height: var(--_ui5_wheelslider_height); + position: relative; + overflow: hidden; + outline: none; +} + +.ui5-wheelslider-root.ui5-phone .ui5-wheelslider-inner .ui5-wheelslider-wrapper { + height: var(--_ui5_wheelslider_mobile_height); +} + +.ui5-wheelslider-root[expanded] { + height: 100%; + cursor: default; + margin: 0; + justify-content: space-between; + flex-direction: column; + display: inline-flex; +} + +.ui5-wheelslider-root[expanded] .ui5-wheelslider-label { + display: block; + visibility: visible; +} + +.ui5-wheelslider-root .ui5-wheelslider-inner { + max-height: 100%; + height: var(--_ui5_wheelslider_height); +} + +.ui5-wheelslider-root.ui5-phone .ui5-wheelslider-inner { + height: var(--_ui5_wheelslider_mobile_height); +} + +.ui5-wheelslider-root[expanded] .ui5-wheelslider-inner .ui5-wheelslider-item { + background: var(--_ui5_wheelslider_item_background_color); + color: var(--_ui5_wheelslider_item_text_color); + border: 1px solid var(--_ui5_wheelslider_item_border_color); + border-radius: var(--_ui_wheelslider_item_border_radius); + offset-position: auto; + cursor: auto; +} + +.ui5-wheelslider-root[expanded] .ui5-wheelslider-inner .ui5-wheelslider-item:hover { + background: var(--_ui_wheelslider_item_hover_color); +} + +.ui5-wheelslider-root[expanded] .ui5-wheelslider-inner .ui5-wheelslider-item:active { + background: var(--_ui5_wheelslider_active_item_background_color); + color: var(--_ui5_wheelslider_active_item_text_color); +} + +.ui5-wheelslider-root[expanded] .ui5-wheelslider-inner .ui5-wheelslider-item:focus { + outline: 1px dotted black; + outline-offset: -3px; +} + +.ui5-wheelslider-root[expanded] .ui5-wheelslider-inner .ui5-wheelslider-selection-frame { + visibility: visible; + -webkit-box-shadow: inset 0px 0px 0px 2px var(--_ui5_wheelslider_selection_frame_color); + -moz-box-shadow: inset 0px 0px 0px 2px var(--_ui5_wheelslider_selection_frame_color); + box-shadow: inset 0px 0px 0px 2px var(--_ui5_wheelslider_selection_frame_color); + border-radius: var(--_ui_wheelslider_item_border_radius); + margin-top: var(--_ui5_wheelslider_selection_frame_margin_top); +} + +.ui5-wheelslider-root.ui5-phone[expanded] .ui5-wheelslider-inner .ui5-wheelslider-selection-frame { + margin-top: var(--_ui5_wheelslider_mobile_selection_frame_margin_top); +} + +.ui5-wheelslider-root[expanded] .ui5-wheelslider-inner .ui5-wheelslider-selection-frame:hover + ul > li.ui5-wheelslider-item.ui5-wheelslider-itemSelected { + background: var(--_ui5_wheelslider_selected_item_background_color); +} +.ui5-wheelslider-root[expanded] .ui5-wheelslider-inner .ui5-wheelslider-selection-frame:active + ul > li.ui5-wheelslider-item.ui5-wheelslider-itemSelected { + background: var(--_ui5_wheelslider_selected_item_background_color); + color: lightgray; +} +.ui5-wheelslider-root:focus { + outline: none; +} \ No newline at end of file diff --git a/packages/main/src/themes/base/WheelSlider-parameters.css b/packages/main/src/themes/base/WheelSlider-parameters.css new file mode 100644 index 000000000000..736be12fe348 --- /dev/null +++ b/packages/main/src/themes/base/WheelSlider-parameters.css @@ -0,0 +1,10 @@ +:root{ + --_ui5_wheelslider_item_text_size: var(--sapFontMediumSize); + --_ui5_wheelslider_label_text_size: var(--sapFontSmallSize); + --_ui5_wheelslider_selection_frame_margin_top: calc(var(--_ui5_wheelslider_item_height) * 2); + --_ui5_wheelslider_mobile_selection_frame_margin_top: calc(var(--_ui5_wheelslider_item_height) * 4); + --_ui5_wheelslider_label_text_color: var(--sapContent_LabelColor); + --_ui5_wheelslider_height: 15rem; + --_ui5_wheelslider_mobile_height: 27rem; + --_ui5_wheelslider_arrows_visibility: hidden; +} \ No newline at end of file diff --git a/packages/main/src/themes/base/sizes-parameters.css b/packages/main/src/themes/base/sizes-parameters.css index f24c36026cd1..bf2c652131b1 100644 --- a/packages/main/src/themes/base/sizes-parameters.css +++ b/packages/main/src/themes/base/sizes-parameters.css @@ -135,6 +135,12 @@ --_ui5_rb_label_width: calc(100% - 2rem + 1px); --_ui5_rb_rtl_focus_right: 0.375rem; + --_ui5_wheelslider_item_width: 4rem; + --_ui5_wheelslider_item_height: 2rem; + --_ui5_wheelslider_height: 14rem; + --_ui5_wheelslider_selection_frame_margin_top: calc(var(--_ui5_wheelslider_item_height) * 2); + --_ui5_wheelslider_arrows_visibility: visible; + --_ui5_switch_height: var(--_ui5_switch_compact_height); --_ui5_switch_width: var(--_ui5_switch_compact_width); --_ui5_switch_handle_height: var(--_ui5_switch_handle_compact_height); diff --git a/packages/main/src/themes/sap_belize/WheelSlider-parameters.css b/packages/main/src/themes/sap_belize/WheelSlider-parameters.css new file mode 100644 index 000000000000..554dfd93b94d --- /dev/null +++ b/packages/main/src/themes/sap_belize/WheelSlider-parameters.css @@ -0,0 +1,17 @@ +@import "../base/WheelSlider-parameters.css"; + +:root{ + --_ui5_wheelslider_item_background_color: var(--sapLegend_WorkingBackground); + --_ui5_wheelslider_item_text_color: var(--sapTextColor); + --_ui_wheelslider_item_hover_color: var(--sapList_Hover_Background); + --_ui5_wheelslider_item_border_color: var(--sapList_Background); + --_ui5_wheelslider_collapsed_item_text_color: var(--_ui5_wheelslider_item_border_color); + --_ui5_wheelslider_selected_item_background_color: var(--sapContent_Selected_Background); + --_ui5_wheelslider_selected_item_hover_background_color: var(--sapButton_Emphasized_Hover_BorderColor); + --_ui5_wheelslider_active_item_background_color:var(--sapContent_Selected_Background); + --_ui5_wheelslider_active_item_text_color: var(--sapContent_Selected_TextColor); + --_ui5_wheelslider_item_width: 4.5rem; + --_ui5_wheelslider_item_height: 3rem; + --_ui5_wheelslider_selection_frame_color: var(--sapList_SelectionBorderColor); + --_ui_wheelslider_item_border_radius: var(--_ui5_button_border_radius); +} \ No newline at end of file diff --git a/packages/main/src/themes/sap_belize/parameters-bundle.css b/packages/main/src/themes/sap_belize/parameters-bundle.css index ee3372211e2a..a83f6b561022 100644 --- a/packages/main/src/themes/sap_belize/parameters-bundle.css +++ b/packages/main/src/themes/sap_belize/parameters-bundle.css @@ -28,6 +28,7 @@ @import "../base/Title-parameters.css"; @import "../base/Toast-parameters.css"; @import "../base/ToggleButton-parameters.css"; +@import "./WheelSlider-parameters.css"; @import "../base/YearPicker-parameters.css"; @import "../base/Token-parameters.css"; @import "../base/MultiComboBox-parameters.css"; diff --git a/packages/main/src/themes/sap_belize_hcb/WheelSlider-parameters.css b/packages/main/src/themes/sap_belize_hcb/WheelSlider-parameters.css new file mode 100644 index 000000000000..012ad3938232 --- /dev/null +++ b/packages/main/src/themes/sap_belize_hcb/WheelSlider-parameters.css @@ -0,0 +1,17 @@ +@import "../base/WheelSlider-parameters.css"; + +:root{ + --_ui5_wheelslider_item_background_color: var(--sapList_Background); + --_ui5_wheelslider_item_text_color: var(--sapTextColor); + --_ui_wheelslider_item_hover_color: var(--sapHighlightColor); + --_ui5_wheelslider_item_border_color: var(--sapList_BorderColor); + --_ui5_wheelslider_collapsed_item_text_color: var(--sapContent_ContrastTextColor); + --_ui5_wheelslider_selected_item_background_color: var(--sapSelectedColor); + --_ui5_wheelslider_selected_item_hover_background_color: var(--sapHighlightColor); + --_ui5_wheelslider_active_item_background_color: var(--sapHighlightColor); + --_ui5_wheelslider_active_item_text_color: var(--sapTextColor); + --_ui5_wheelslider_item_width: 4.5rem; + --_ui5_wheelslider_item_height: 3rem; + --_ui5_wheelslider_selection_frame_color: var(--sapContent_ForegroundBorderColor); + --_ui_wheelslider_item_border_radius: 0; +} \ No newline at end of file diff --git a/packages/main/src/themes/sap_belize_hcb/parameters-bundle.css b/packages/main/src/themes/sap_belize_hcb/parameters-bundle.css index 6ada5f0ec0c0..56756f1e0a88 100644 --- a/packages/main/src/themes/sap_belize_hcb/parameters-bundle.css +++ b/packages/main/src/themes/sap_belize_hcb/parameters-bundle.css @@ -28,6 +28,7 @@ @import "../base/Title-parameters.css"; @import "../base/Toast-parameters.css"; @import "./ToggleButton-parameters.css"; +@import "./WheelSlider-parameters.css"; @import "./YearPicker-parameters.css"; @import "./Token-parameters.css"; @import "../base/MultiComboBox-parameters.css"; diff --git a/packages/main/src/themes/sap_belize_hcw/WheelSlider-parameters.css b/packages/main/src/themes/sap_belize_hcw/WheelSlider-parameters.css new file mode 100644 index 000000000000..012ad3938232 --- /dev/null +++ b/packages/main/src/themes/sap_belize_hcw/WheelSlider-parameters.css @@ -0,0 +1,17 @@ +@import "../base/WheelSlider-parameters.css"; + +:root{ + --_ui5_wheelslider_item_background_color: var(--sapList_Background); + --_ui5_wheelslider_item_text_color: var(--sapTextColor); + --_ui_wheelslider_item_hover_color: var(--sapHighlightColor); + --_ui5_wheelslider_item_border_color: var(--sapList_BorderColor); + --_ui5_wheelslider_collapsed_item_text_color: var(--sapContent_ContrastTextColor); + --_ui5_wheelslider_selected_item_background_color: var(--sapSelectedColor); + --_ui5_wheelslider_selected_item_hover_background_color: var(--sapHighlightColor); + --_ui5_wheelslider_active_item_background_color: var(--sapHighlightColor); + --_ui5_wheelslider_active_item_text_color: var(--sapTextColor); + --_ui5_wheelslider_item_width: 4.5rem; + --_ui5_wheelslider_item_height: 3rem; + --_ui5_wheelslider_selection_frame_color: var(--sapContent_ForegroundBorderColor); + --_ui_wheelslider_item_border_radius: 0; +} \ No newline at end of file diff --git a/packages/main/src/themes/sap_belize_hcw/parameters-bundle.css b/packages/main/src/themes/sap_belize_hcw/parameters-bundle.css index bdf6398a4916..906cae4d8e92 100644 --- a/packages/main/src/themes/sap_belize_hcw/parameters-bundle.css +++ b/packages/main/src/themes/sap_belize_hcw/parameters-bundle.css @@ -29,5 +29,6 @@ @import "../base/Toast-parameters.css"; @import "../base/ToggleButton-parameters.css"; @import "./YearPicker-parameters.css"; +@import "./WheelSlider-parameters.css"; @import "./Token-parameters.css"; @import "../base/MultiComboBox-parameters.css"; diff --git a/packages/main/src/themes/sap_fiori_3/WheelSlider-parameters.css b/packages/main/src/themes/sap_fiori_3/WheelSlider-parameters.css new file mode 100644 index 000000000000..a34ef150a752 --- /dev/null +++ b/packages/main/src/themes/sap_fiori_3/WheelSlider-parameters.css @@ -0,0 +1,17 @@ +@import "../base/WheelSlider-parameters.css"; + +:root{ + --_ui5_wheelslider_item_background_color: var(--sapLegend_WorkingBackground); + --_ui5_wheelslider_item_text_color: var(--sapTextColor); + --_ui_wheelslider_item_hover_color: var(--sapList_Hover_Background); + --_ui5_wheelslider_item_border_color: var(--sapList_Background); + --_ui5_wheelslider_collapsed_item_text_color: var(--_ui5_wheelslider_item_border_color); + --_ui5_wheelslider_selected_item_background_color: var(--sapContent_Selected_Background); + --_ui5_wheelslider_selected_item_hover_background_color: var(--sapButton_Emphasized_Hover_BorderColor); + --_ui5_wheelslider_active_item_background_color:var(--sapContent_Selected_Background); + --_ui5_wheelslider_active_item_text_color: var(--sapContent_Selected_TextColor); + --_ui5_wheelslider_item_width: 3rem; + --_ui5_wheelslider_item_height: 2.875rem; + --_ui5_wheelslider_selection_frame_color: var(--sapList_SelectionBorderColor); + --_ui_wheelslider_item_border_radius: var(--_ui5_button_border_radius); +} \ No newline at end of file diff --git a/packages/main/src/themes/sap_fiori_3/parameters-bundle.css b/packages/main/src/themes/sap_fiori_3/parameters-bundle.css index 97e1ecfd0574..321186707a94 100644 --- a/packages/main/src/themes/sap_fiori_3/parameters-bundle.css +++ b/packages/main/src/themes/sap_fiori_3/parameters-bundle.css @@ -23,6 +23,7 @@ @import "../base/TextArea-parameters.css"; @import "../base/TimelineItem-parameters.css"; @import "../base/Toast-parameters.css"; +@import "./WheelSlider-parameters.css"; @import "../base/ToggleButton-parameters.css"; @import "./YearPicker-parameters.css"; @import "./CalendarHeader-parameters.css"; diff --git a/packages/main/src/themes/sap_fiori_3_dark/WheelSlider-parameters.css b/packages/main/src/themes/sap_fiori_3_dark/WheelSlider-parameters.css new file mode 100644 index 000000000000..8c9b7645b14c --- /dev/null +++ b/packages/main/src/themes/sap_fiori_3_dark/WheelSlider-parameters.css @@ -0,0 +1,17 @@ +@import "../base/WheelSlider-parameters.css"; + +:root{ + --_ui5_wheelslider_item_background_color: var(--sapLegend_WorkingBackground); + --_ui5_wheelslider_item_text_color: var(--sapTextColor); + --_ui_wheelslider_item_hover_color: var(--sapList_Hover_Background); + --_ui5_wheelslider_item_border_color: var(--sapList_Background); + --_ui5_wheelslider_collapsed_item_text_color: var(--_ui5_wheelslider_item_border_color); + --_ui5_wheelslider_selected_item_background_color: var(--sapContent_Selected_Background); + --_ui5_wheelslider_selected_item_hover_background_color: var(--sapButton_Emphasized_Hover_BorderColor); + --_ui5_wheelslider_active_item_background_color: var(--sapContent_Selected_Background); + --_ui5_wheelslider_active_item_text_color: var(--sapContent_Selected_TextColor); + --_ui5_wheelslider_item_width: 3rem; + --_ui5_wheelslider_item_height: 2.875rem; + --_ui5_wheelslider_selection_frame_color: var(--sapList_SelectionBorderColor); + --_ui_wheelslider_item_border_radius: var(--_ui5_button_border_radius); +} \ No newline at end of file diff --git a/packages/main/src/themes/sap_fiori_3_dark/parameters-bundle.css b/packages/main/src/themes/sap_fiori_3_dark/parameters-bundle.css index 97e1ecfd0574..321186707a94 100644 --- a/packages/main/src/themes/sap_fiori_3_dark/parameters-bundle.css +++ b/packages/main/src/themes/sap_fiori_3_dark/parameters-bundle.css @@ -23,6 +23,7 @@ @import "../base/TextArea-parameters.css"; @import "../base/TimelineItem-parameters.css"; @import "../base/Toast-parameters.css"; +@import "./WheelSlider-parameters.css"; @import "../base/ToggleButton-parameters.css"; @import "./YearPicker-parameters.css"; @import "./CalendarHeader-parameters.css"; diff --git a/packages/main/test/pages/TimePicker.html b/packages/main/test/pages/TimePicker.html new file mode 100644 index 000000000000..fd88d0d9d5a2 --- /dev/null +++ b/packages/main/test/pages/TimePicker.html @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/main/test/pages/WheelSlider_Test_Page.html b/packages/main/test/pages/WheelSlider_Test_Page.html new file mode 100644 index 000000000000..0e1854bd3d94 --- /dev/null +++ b/packages/main/test/pages/WheelSlider_Test_Page.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/packages/main/test/samples/TimePicker.sample.html b/packages/main/test/samples/TimePicker.sample.html new file mode 100644 index 000000000000..c5371cac83da --- /dev/null +++ b/packages/main/test/samples/TimePicker.sample.html @@ -0,0 +1,42 @@ +
+

TimePicker

+
+ +
+
+ +
@ui5/webcomponents
+ +
<ui5-timepicker>
+ +
+

Basic TimePicker

+
+ +
+
{% highlight html %}
+	
+{% endhighlight %}
+
+ +
+

TimePicker in twelve hours format

+
+ +
+
{% highlight html %}
+		
+	{% endhighlight %}
+
+ +
+

TimePicker with only minutes and seconds

+
+ +
+
{% highlight html %}
+		
+	{% endhighlight %}
+
+ + diff --git a/packages/main/test/specs/TimePicker.spec.js b/packages/main/test/specs/TimePicker.spec.js new file mode 100644 index 000000000000..70e49270e692 --- /dev/null +++ b/packages/main/test/specs/TimePicker.spec.js @@ -0,0 +1,39 @@ +const assert = require("chai").assert; + +describe("TimePicker general interaction", () => { + browser.url("http://localhost:8080/test-resources/pages/TimePicker.html"); + + it("Check sliders value", () => { + browser.$("#timepicker").setProperty("value", "11:12:13"); + browser.$("#timepicker").shadow$("ui5-input").$(".ui5-timepicker-input-icon-button").click(); + const hoursSliderValue = browser.$$(".ui5wc_1")[0].shadow$$(".ui5-timepicker-popover")[0].$(".ui5-timepicker-hours-wheelslider").getValue(); + const minutesSliderValue = browser.$$(".ui5wc_1")[0].shadow$$(".ui5-timepicker-popover")[0].$(".ui5-timepicker-minutes-wheelslider").getValue(); + const secondsSliderValue = browser.$$(".ui5wc_1")[0].shadow$$(".ui5-timepicker-popover")[0].$(".ui5-timepicker-seconds-wheelslider").getValue(); + assert.strictEqual(hoursSliderValue, "11", "Hours are equal"); + assert.strictEqual(minutesSliderValue, "12", "Minutes are equal"); + assert.strictEqual(secondsSliderValue, "13", "Minutes are equal"); + }); + + it("Sliders submit value", () => { + browser.$$(".ui5wc_1")[0].shadow$$(".ui5-timepicker-popover")[0].setProperty("opened",true); + browser.$$(".ui5wc_1")[0].shadow$$(".ui5-timepicker-popover")[0].$(".ui5-timepicker-hours-wheelslider").setProperty("value","14"); + browser.$$(".ui5wc_1")[0].shadow$$(".ui5-timepicker-popover")[0].$(".ui5-timepicker-minutes-wheelslider").setProperty("value","15"); + browser.$$(".ui5wc_1")[0].shadow$$(".ui5-timepicker-popover")[0].$(".ui5-timepicker-seconds-wheelslider").setProperty("value","16"); + + // browser.$("#timepicker").shadow$$(".ui5-timepicker-popover")[0].setProperty("opened",true); + browser.$$(".ui5wc_1")[0].shadow$$(".ui5-timepicker-popover")[0].$("#submit").click(); + + const textValue = browser.$("#timepicker").shadow$$("#ui5wc_1-inner")[0].getValue(); + + assert.strictEqual(textValue.substring(0,2), "14", "Hours are equal"); + assert.strictEqual(textValue.substring(3,5), "15", "Minutes are equal"); + }); + + it("Wrong value submit", () => { + browser.$("#timepicker").click(); + browser.$("#timepicker").keys("123123123"); + browser.$("#timepicker").keys("Enter"); + + assert.strictEqual(browser.$("#timepicker").shadow$("ui5-input").getProperty("valueState"), "Error", "The value state is on error"); + }); +}); \ No newline at end of file diff --git a/packages/main/test/specs/WheelSlider.spec.js b/packages/main/test/specs/WheelSlider.spec.js new file mode 100644 index 000000000000..bf1bfcf0a43d --- /dev/null +++ b/packages/main/test/specs/WheelSlider.spec.js @@ -0,0 +1,61 @@ +const assert = require("chai").assert; + +describe("Wheel Slider general interaction", () => { + browser.url("http://localhost:8080/test-resources/pages/WheelSlider_Test_Page.html"); + + before(() => { + browser.$("#wheelslider").setProperty("_items",["1","2","3","4","5","6","7"]); + browser.$("#wheelslider").setProperty("value","1"); + browser.$("#wheelslider").setProperty("_expanded",true); + browser.$("body").setAttribute("class", "sapUiSizeCompact"); + }); + + it("tests slider's label rendering", () => { + const textValue = browser.$("#wheelslider").shadow$$(".ui5-wheelslider-label")[0].getText(); + + assert.strictEqual(textValue, "Test", "Slider text is rendered"); + }); + + it("Arrow down button is working", () => { + const slider = browser.$("#wheelslider"); + const button = slider.shadow$$(".ui5-wheelslider-arrow")[1]; + button.click(); + + assert.strictEqual(slider.getValue(), "2", "Wheel Slider button arrow down is working"); + }); + + it("Arrow up button is working", () => { + const slider = browser.$("#wheelslider"); + + const button = slider.shadow$$(".ui5-wheelslider-arrow")[0]; + + button.click(); + + assert.strictEqual(slider.getValue(), "1", "Wheel Slider button arrow up is working"); + }); + + it("Keyboard arrow down is working", () => { + const slider = browser.$("#wheelslider"); + + slider.keys("ArrowDown"); + + assert.strictEqual(slider.getValue(), "2", "Wheel Slider keyboard handling for arrow down is working"); + }); + + it("Keyboard Arrow up is working", () => { + const slider = browser.$("#wheelslider"); + + slider.keys("ArrowUp"); + + assert.strictEqual(slider.getValue(), "1", "Wheel Slider keyboard handling for arrow up is working"); + }); + + it("Click on element selects element", () => { + const slider = browser.$("#wheelslider"); + const sliderElements = slider.shadow$$(".ui5-wheelslider-item"); + + sliderElements[5].click(); + + assert.strictEqual(slider.getValue(), "6", "Wheel Slider pick elements with click works"); + }); +}); \ No newline at end of file diff --git a/packages/theme-base/src/themes/sap_belize/base-parameters-vars.less b/packages/theme-base/src/themes/sap_belize/base-parameters-vars.less index d8e3c45f3ec7..f571bb0c70d6 100644 --- a/packages/theme-base/src/themes/sap_belize/base-parameters-vars.less +++ b/packages/theme-base/src/themes/sap_belize/base-parameters-vars.less @@ -298,8 +298,6 @@ --sapInfobar_Background: @sapInfobar_Background; --sapToolbar_SeparatorColor: @sapToolbar_SeparatorColor; --sapHighlightTextColor: @sapHighlightTextColor; - - // custom added params as no base ("sap" prefix) params equivalent exist --sapToggleButton_Pressed_HoverBackground: @sapToggleButton_Pressed_HoverBackground; --sapToggleButton_Pressed_HoverBorderColor: @sapToggleButton_Pressed_HoverBorderColor; } diff --git a/packages/theme-base/src/themes/sap_belize/base-parameters.less b/packages/theme-base/src/themes/sap_belize/base-parameters.less index e88545f1e647..3056cee5bdaf 100644 --- a/packages/theme-base/src/themes/sap_belize/base-parameters.less +++ b/packages/theme-base/src/themes/sap_belize/base-parameters.less @@ -295,6 +295,5 @@ @sapToolbar_SeparatorColor: fade(@sapPrimary1, 20); @sapHighlightTextColor: contrast(@sapHighlightColor, @sapTextColor, @sapContent_ContrastTextColor, @sapContent_ContrastTextThreshold); -// custom added params as no base ("sap" prefix) params equivalent exist @sapToggleButton_Pressed_HoverBackground: lighten(@sapButton_Selected_Background, 10); @sapToggleButton_Pressed_HoverBorderColor: @sapToggleButton_Pressed_HoverBackground; \ No newline at end of file diff --git a/packages/theme-base/src/themes/sap_fiori_3/base-parameters-vars.less b/packages/theme-base/src/themes/sap_fiori_3/base-parameters-vars.less index 18287201fb2a..8dfa79c8e00e 100644 --- a/packages/theme-base/src/themes/sap_fiori_3/base-parameters-vars.less +++ b/packages/theme-base/src/themes/sap_fiori_3/base-parameters-vars.less @@ -296,7 +296,6 @@ --sapTile_IconColor: @sapTile_IconColor; --sapToolbar_SeparatorColor: @sapToolbar_SeparatorColor; - // custom added params as no base ("sap" prefix) params equivalent exist --sapToggleButton_Pressed_HoverBackground: @sapToggleButton_Pressed_HoverBackground; --sapToggleButton_Pressed_HoverBorderColor: @sapToggleButton_Pressed_HoverBorderColor; } \ No newline at end of file diff --git a/packages/theme-base/src/themes/sap_fiori_3/base-parameters.less b/packages/theme-base/src/themes/sap_fiori_3/base-parameters.less index ce7f552f3c03..03d650d6a5a2 100644 --- a/packages/theme-base/src/themes/sap_fiori_3/base-parameters.less +++ b/packages/theme-base/src/themes/sap_fiori_3/base-parameters.less @@ -293,6 +293,6 @@ @sapTile_IconColor: contrast(@sapTile_Background, lighten(@sapShellColor ,20), @sapContent_ContrastIconColor, @sapContent_ContrastTextThreshold); @sapToolbar_SeparatorColor: darken(@sapBaseColor, 15); -// custom added params as no base ("sap" prefix) params equivalent exist + @sapToggleButton_Pressed_HoverBackground: lighten(@sapSelectedColor, 3); @sapToggleButton_Pressed_HoverBorderColor: @sapToggleButton_Pressed_HoverBackground; \ No newline at end of file diff --git a/packages/theme-base/src/themes/sap_fiori_3_dark/base-parameters-vars.less b/packages/theme-base/src/themes/sap_fiori_3_dark/base-parameters-vars.less index 6d7860bf6b35..c3a32a92a2f8 100644 --- a/packages/theme-base/src/themes/sap_fiori_3_dark/base-parameters-vars.less +++ b/packages/theme-base/src/themes/sap_fiori_3_dark/base-parameters-vars.less @@ -293,7 +293,6 @@ --sapTile_IconColor: @sapTile_IconColor; --sapObjectHeader_BorderColor: @sapObjectHeader_BorderColor; - // custom added params as no base ("sap" prefix) params equivalent exist --sapToggleButton_Pressed_HoverBackground: @sapToggleButton_Pressed_HoverBackground; --sapToggleButton_Pressed_HoverBorderColor: @sapToggleButton_Pressed_HoverBorderColor; } diff --git a/packages/theme-base/src/themes/sap_fiori_3_dark/base-parameters.less b/packages/theme-base/src/themes/sap_fiori_3_dark/base-parameters.less index 257c4a4ece64..1594a856ee84 100644 --- a/packages/theme-base/src/themes/sap_fiori_3_dark/base-parameters.less +++ b/packages/theme-base/src/themes/sap_fiori_3_dark/base-parameters.less @@ -292,6 +292,5 @@ @sapTile_IconColor: contrast(@sapTile_Background, lighten(@sapShellColor, 35), @sapContent_ContrastIconColor, @sapContent_ContrastTextThreshold); @sapObjectHeader_BorderColor: lighten(@sapObjectHeader_Background, 8); -// custom added params as no base ("sap" prefix) params equivalent exist @sapToggleButton_Pressed_HoverBackground: darken(@sapSelectedColor, 3); @sapToggleButton_Pressed_HoverBorderColor: @sapToggleButton_Pressed_HoverBackground; \ No newline at end of file