From 4c340dda497236e7b69c63bd663cf4bf271a7100 Mon Sep 17 00:00:00 2001 From: NHristov-sap <53182443+NHristov-sap@users.noreply.github.com> Date: Mon, 22 Feb 2021 12:16:44 +0200 Subject: [PATCH] feat(ui5-step-input): inintial implementation (#2804) Fixes #2640 --- packages/base/src/Keys.js | 12 + packages/main/bundle.esm.js | 1 + packages/main/src/Input.hbs | 4 +- packages/main/src/Input.js | 12 + packages/main/src/StepInput.hbs | 79 +++ packages/main/src/StepInput.js | 661 ++++++++++++++++++ .../main/src/i18n/messagebundle.properties | 6 + packages/main/src/themes/Input.css | 11 + packages/main/src/themes/StepInput.css | 206 ++++++ packages/main/test/pages/FormSupport.html | 14 +- packages/main/test/pages/StepInput.html | 176 +++++ .../main/test/samples/StepInput.sample.html | 99 +++ packages/main/test/specs/FormSupport.spec.js | 2 +- packages/main/test/specs/StepInput.spec.js | 450 ++++++++++++ 14 files changed, 1725 insertions(+), 8 deletions(-) create mode 100644 packages/main/src/StepInput.hbs create mode 100644 packages/main/src/StepInput.js create mode 100644 packages/main/src/themes/StepInput.css create mode 100644 packages/main/test/pages/StepInput.html create mode 100644 packages/main/test/samples/StepInput.sample.html create mode 100644 packages/main/test/specs/StepInput.spec.js diff --git a/packages/base/src/Keys.js b/packages/base/src/Keys.js index 25c3ae34c602..52a30be9c0b5 100644 --- a/packages/base/src/Keys.js +++ b/packages/base/src/Keys.js @@ -127,6 +127,14 @@ const isUpCtrl = event => (event.key ? (event.key === "ArrowUp" || event.key === const isDownCtrl = event => (event.key ? (event.key === "ArrowDown" || event.key === "Down") : event.keyCode === KeyCodes.ARROW_DOWN) && checkModifierKeys(event, true, false, false); +const isUpShift = event => (event.key ? (event.key === "ArrowUp" || event.key === "Up") : event.keyCode === KeyCodes.ARROW_UP) && checkModifierKeys(event, false, false, true); + +const isDownShift = event => (event.key ? (event.key === "ArrowDown" || event.key === "Down") : event.keyCode === KeyCodes.ARROW_DOWN) && checkModifierKeys(event, false, false, true); + +const isUpShiftCtrl = event => (event.key ? (event.key === "ArrowUp" || event.key === "Up") : event.keyCode === KeyCodes.ARROW_UP) && checkModifierKeys(event, true, false, true); + +const isDownShiftCtrl = event => (event.key ? (event.key === "ArrowDown" || event.key === "Down") : event.keyCode === KeyCodes.ARROW_DOWN) && checkModifierKeys(event, true, false, true); + const isHome = event => (event.key ? event.key === "Home" : event.keyCode === KeyCodes.HOME) && !hasModifierKeys(event); const isEnd = event => (event.key ? event.key === "End" : event.keyCode === KeyCodes.END) && !hasModifierKeys(event); @@ -198,6 +206,10 @@ export { isRightCtrl, isUpCtrl, isDownCtrl, + isUpShift, + isDownShift, + isUpShiftCtrl, + isDownShiftCtrl, isHome, isEnd, isPlus, diff --git a/packages/main/bundle.esm.js b/packages/main/bundle.esm.js index 9c2ae2dbd5d7..28bdff0360cf 100644 --- a/packages/main/bundle.esm.js +++ b/packages/main/bundle.esm.js @@ -69,6 +69,7 @@ import ResponsivePopover from "./dist/ResponsivePopover.js"; import SegmentedButton from "./dist/SegmentedButton.js"; import Select from "./dist/Select.js"; import Slider from "./dist/Slider.js"; +import StepInput from "./dist/StepInput.js"; import RangeSlider from "./dist/RangeSlider.js"; import Switch from "./dist/Switch.js"; import MessageStrip from "./dist/MessageStrip.js"; diff --git a/packages/main/src/Input.hbs b/packages/main/src/Input.hbs index 026c3584b12a..7ea7dbcb3ab1 100644 --- a/packages/main/src/Input.hbs +++ b/packages/main/src/Input.hbs @@ -35,7 +35,9 @@ @focusin={{innerFocusIn}} data-sap-no-tab-ref data-sap-focus-ref - step="{{step}}" + step="{{nativeInputAttributes.step}}" + min="{{nativeInputAttributes.min}}" + max="{{nativeInputAttributes.max}}" /> {{#if icon.length}}
ui5-step-input
.
+ *
+ * @type {Float}
+ * @defaultvalue 0
+ * @public
+ */
+ value: {
+ type: Float,
+ defaultValue: 0,
+ },
+
+ /**
+ * Defines a minimum value of the ui5-step-input
.
+ *
+ * @type {Float}
+ * @public
+ */
+ min: {
+ type: Float,
+ },
+
+ /**
+ * Defines a maximum value of the ui5-step-input
.
+ *
+ * @type {Float}
+ * @public
+ */
+ max: {
+ type: Float,
+ },
+
+ /**
+ * Defines a step of increasing/decreasing the value of the ui5-step-input
.
+ *
+ * @type {Float}
+ * @defaultvalue 1
+ * @public
+ */
+ step: {
+ type: Float,
+ defaultValue: 1,
+ },
+
+ /**
+ * Defines the value state of the ui5-step-input
.
+ * None
Error
Warning
Success
Information
ui5-step-input
is required.
+ *
+ * @type {Boolean}
+ * @defaultvalue false
+ * @public
+ */
+ required: {
+ type: Boolean,
+ },
+
+ /**
+ * Determines whether the ui5-step-input
is displayed as disabled.
+ *
+ * @type {boolean}
+ * @defaultvalue false
+ * @public
+ */
+ disabled: {
+ type: Boolean,
+ },
+
+ /**
+ * Determines whether the ui5-step-input
is displayed as read-only.
+ *
+ * @type {boolean}
+ * @defaultvalue false
+ * @public
+ */
+ readonly: {
+ type: Boolean,
+ },
+
+ /**
+ * Defines a short hint, intended to aid the user with data entry when the
+ * ui5-step-input
has no value.
+ *
+ * ui5-step-input
appear empty - without placeholder or format pattern.
+ *
+ * @type {string}
+ * @defaultvalue undefined
+ * @public
+ */
+ placeholder: {
+ type: String,
+ defaultValue: undefined,
+ },
+
+ /**
+ * Determines the name with which the ui5-step-input
will be submitted in an HTML form.
+ *
+ * name
property to have effect, you must add the following import to your project:
+ * import "@ui5/webcomponents/dist/features/InputElementsFormSupport.js";
+ *
+ * input
HTML element
+ * will be created inside the ui5-step-input
so that it can be submitted as
+ * part of an HTML form. Do not use this property unless you need to submit a form.
+ *
+ * @type {string}
+ * @defaultvalue ""
+ * @public
+ */
+ name: {
+ type: String,
+ },
+
+ /**
+ * Determines the number of digits after the decimal point of the ui5-step-input
.
+ *
+ * @type {Integer}
+ * @defaultvalue 0
+ * @public
+ */
+ valuePrecision: {
+ type: Integer,
+ defaultValue: 0,
+ },
+
+ /**
+ * Defines the aria-label attribute for the ui5-step-input
.
+ *
+ * @type {String}
+ * @private
+ * @defaultvalue ""
+ */
+ ariaLabel: {
+ type: String,
+ },
+
+ /**
+ * Receives id(or many ids) of the elements that label the ui5-step-input
.
+ *
+ * @type {String}
+ * @defaultvalue ""
+ * @private
+ */
+ ariaLabelledby: {
+ type: String,
+ defaultValue: "",
+ },
+
+ _decIconDisabled: {
+ type: Boolean,
+ noAttribute: true,
+ },
+
+ _incIconDisabled: {
+ type: Boolean,
+ noAttribute: true,
+ },
+
+ _focused: {
+ type: Boolean,
+ noAttribute: true,
+ },
+
+ _inputFocused: {
+ type: Boolean,
+ noAttribute: true,
+ },
+
+ _previousValue: {
+ type: Float,
+ noAttribute: true,
+ },
+
+ _previousValueState: {
+ type: String,
+ noAttribute: true,
+ defaultValue: "",
+ },
+
+ _waitTimeout: {
+ type: Float,
+ noAttribute: true,
+ },
+
+ _speed: {
+ type: Float,
+ noAttribute: true,
+ },
+
+ _btnDown: {
+ type: Boolean,
+ noAttribute: true,
+ },
+
+ _spinTimeoutId: {
+ type: Integer,
+ noAttribute: true,
+ },
+
+ _spinStarted: {
+ type: Boolean,
+ noAttribute: true,
+ },
+ },
+ slots: /** @lends sap.ui.webcomponents.main.StepInput.prototype */ {
+ /**
+ * Defines the value state message that will be displayed as pop up under the ui5-step-input
.
+ * valueStateMessage
would be displayed,
+ * when the ui5-step-input
is in Information
, Warning
or Error
value state.
+ * @type {HTMLElement}
+ * @slot
+ * @public
+ */
+ valueStateMessage: {
+ type: HTMLElement,
+ },
+ },
+ events: /** @lends sap.ui.webcomponents.main.StepInput.prototype */ {
+ /**
+ * Fired when the input operation has finished by pressing Enter or on focusout.
+ *
+ * @event
+ * @public
+ */
+ change: {},
+ },
+};
+
+// Spin variables
+const INITIAL_WAIT_TIMEOUT = 500; // milliseconds
+const ACCELERATION = 0.8;
+const MIN_WAIT_TIMEOUT = 50; // milliseconds
+const INITIAL_SPEED = 120; // milliseconds
+
+/**
+ * @class
+ *
+ * ui5-step-input
consists of an input field and buttons with icons to increase/decrease the value
+ * with the predefined step.
+ * StepInput
.
+ * The increase/decrease button and the up/down keyboard navigation become disabled when
+ * the value reaches the max/min or a new value is entered from the input which is greater/less than the max/min.
+ * ui5-input
instead.ui5-input
instead.ui5-step-input
+ * import @ui5/webcomponents/dist/StepInput.js";
+ *
+ * @constructor
+ * @author SAP SE
+ * @alias sap.ui.webcomponents.main.StepInput
+ * @extends UI5Element
+ * @tagname ui5-step-input
+ * @since 1.0.0-rc.12
+ * @public
+ */
+class StepInput extends UI5Element {
+ constructor() {
+ super();
+ this.i18nBundle = getI18nBundle("@ui5/webcomponents");
+ }
+
+ static get metadata() {
+ return metadata;
+ }
+
+ static get render() {
+ return litRender;
+ }
+
+ static get styles() {
+ return StepInputCss;
+ }
+
+ static get template() {
+ return StepInputTemplate;
+ }
+
+ static get dependencies() {
+ return [
+ Icon,
+ Input,
+ ];
+ }
+
+ static async onDefine() {
+ await fetchI18nBundle("@ui5/webcomponents");
+ }
+
+ get type() {
+ return InputType.Number;
+ }
+
+ // icons-related
+
+ get decIconTitle() {
+ return this.i18nBundle.getText(STEPINPUT_DEC_ICON_TITLE);
+ }
+
+ get decIconName() {
+ return "less";
+ }
+
+ get incIconTitle() {
+ return this.i18nBundle.getText(STEPINPUT_INC_ICON_TITLE);
+ }
+
+ get incIconName() {
+ return "add";
+ }
+
+ get _decIconClickable() {
+ return !this._decIconDisabled && !this.readonly && !this.disabled;
+ }
+
+ get _incIconClickable() {
+ return !this._incIconDisabled && !this.readonly && !this.disabled;
+ }
+
+ get _isFocused() {
+ return this._focused;
+ }
+
+ get _valuePrecisioned() {
+ return this.value.toFixed(this.valuePrecision);
+ }
+
+ get accInfo() {
+ return {
+ "ariaRequired": this.required,
+ "ariaLabel": getEffectiveAriaLabelText(this),
+ };
+ }
+
+ get inputAttributes() {
+ return {
+ min: this.min === undefined ? undefined : this.min,
+ max: this.max === undefined ? undefined : this.max,
+ step: this.step,
+ };
+ }
+
+ onBeforeRendering() {
+ this._setButtonState();
+ if (this._previousValue === undefined) {
+ this._previousValue = this.value;
+ }
+
+ const FormSupport = getFeature("FormSupport");
+ if (FormSupport) {
+ FormSupport.syncNativeHiddenInput(this);
+ } else if (this.name) {
+ console.warn(`In order for the "name" property to have effect, you should also: import "@ui5/webcomponents/dist/features/InputElementsFormSupport.js";`); // eslint-disable-line
+ }
+ }
+
+ get input() {
+ return this.shadowRoot.querySelector("[ui5-input]");
+ }
+
+ get inputOuter() {
+ return this.shadowRoot.querySelector(".ui5-step-input-input");
+ }
+
+ _onButtonFocusOut() {
+ setTimeout(() => {
+ if (!this._inputFocused) {
+ this.inputOuter.removeAttribute("focused");
+ }
+ }, 0);
+ }
+
+ _onInputFocusIn() {
+ this._inputFocused = true;
+ }
+
+ _onInputFocusOut() {
+ this._inputFocused = false;
+ this._onInputChange();
+ }
+
+ _setButtonState() {
+ this._decIconDisabled = this.min !== undefined && this.value <= this.min;
+ this._incIconDisabled = this.max !== undefined && this.value >= this.max;
+ }
+
+ _validate() {
+ if (this._previousValueState === "") {
+ this._previousValueState = this.valueState !== "" ? this.valueState : ValueState.None;
+ }
+ this.valueState = ((this.min !== undefined && this.value < this.min)
+ || (this.max !== undefined && this.value > this.max))
+ ? ValueState.Error : this._previousValueState;
+ }
+
+ _preciseValue(value) {
+ const pow = 10 ** this.valuePrecision;
+ return Math.round(value * pow) / pow;
+ }
+
+ _fireChangeEvent() {
+ this._previousValue = this.value;
+ this.fireEvent("change", { value: this.value });
+ }
+
+ /**
+ * Value modifier - modifies the value of the component, validates the new value and enables/disables increment and
+ * decrement buttons according to the value and min/max values (if set). Fires change
event when requested
+ *
+ * @param {Float} modifier modifies the value of the component with the given modifier (positive or negative)
+ * @param {Boolean} fireChangeEvent if true
, fires change
event when the value is changed
+ */
+ _modifyValue(modifier, fireChangeEvent) {
+ let value;
+ this.value = this._preciseValue(parseFloat(this.input.value));
+ value = this.value + modifier;
+ if (this.min !== undefined && value < this.min) {
+ value = this.min;
+ }
+ if (this.max !== undefined && value > this.max) {
+ value = this.max;
+ }
+ value = this._preciseValue(value);
+ if (value !== this.value) {
+ this.value = value;
+ this._validate();
+ this._setButtonState();
+ this._focused = true;
+ this.inputOuter.setAttribute("focused", "");
+ if (fireChangeEvent) {
+ this._fireChangeEvent();
+ } else {
+ this.input.focus();
+ }
+ }
+ }
+
+ _incValue(event) {
+ if (this._incIconClickable && event.isTrusted && !this.disabled && !this.readonly) {
+ this._modifyValue(this.step, true);
+ this._previousValue = this.value;
+ }
+ }
+
+ _decValue(event) {
+ if (this._decIconClickable && event.isTrusted && !this.disabled && !this.readonly) {
+ this._modifyValue(-this.step, true);
+ this._previousValue = this.value;
+ }
+ }
+
+ _onInputChange(event) {
+ const inputValue = this._preciseValue(parseFloat(this.input.value));
+ if (this.value !== this._previousValue || this.value !== inputValue) {
+ this.value = inputValue;
+ this._validate();
+ this._setButtonState();
+ this._fireChangeEvent();
+ }
+ }
+
+ _onfocusin() {
+ this._focused = true;
+ }
+
+ _onfocusout() {
+ this._focused = false;
+ }
+
+ _onkeydown(event) {
+ let preventDefault = true;
+ if (this.disabled || this.readonly) {
+ return;
+ }
+
+ if (isUp(event)) {
+ // step up
+ this._modifyValue(this.step);
+ } else if (isDown(event)) {
+ // step down
+ this._modifyValue(-this.step);
+ } else if (isEscape(event)) {
+ // return previous value
+ this.value = this._previousValue;
+ this.input.value = this.value.toFixed(this.valuePrecision);
+ } else if (this.max !== undefined && (isPageUpShift(event) || isUpShiftCtrl(event))) {
+ // step to max
+ this._modifyValue(this.max - this.value);
+ } else if (this.min !== undefined && (isPageDownShift(event) || isDownShiftCtrl(event))) {
+ // step to min
+ this._modifyValue(this.min - this.value);
+ } else if (!isUpCtrl(event) && !isDownCtrl(event) && !isUpShift(event) && !isDownShift(event)) {
+ preventDefault = false;
+ }
+ if (preventDefault) {
+ event.preventDefault();
+ }
+ }
+
+ _decSpin() {
+ if (!this._decIconDisabled) {
+ this._spinValue(false, true);
+ }
+ }
+
+ _incSpin() {
+ if (!this._incIconDisabled) {
+ this._spinValue(true, true);
+ }
+ }
+
+ /**
+ * Calculates the time which should be waited until _spinValue function is called.
+ */
+ _calcWaitTimeout() {
+ this._speed *= ACCELERATION;
+ this._waitTimeout = ((this._waitTimeout - this._speed) < MIN_WAIT_TIMEOUT ? MIN_WAIT_TIMEOUT : (this._waitTimeout - this._speed));
+ return this._waitTimeout;
+ }
+
+ /**
+ * Called when the increment or decrement button is pressed and held to set new value.
+ * @param {boolean} increment - is this the increment button or not so the values should be spin accordingly up or down
+ * @param {boolean} resetVariables - whether to reset the spin-related variables or not
+ */
+ _spinValue(increment, resetVariables) {
+ if (resetVariables) {
+ this._waitTimeout = INITIAL_WAIT_TIMEOUT;
+ this._speed = INITIAL_SPEED;
+ this._btnDown = true;
+ }
+ this._spinTimeoutId = setTimeout(() => {
+ if (this._btnDown) {
+ this._spinStarted = true;
+ this._modifyValue(increment ? this.step : -this.step);
+ this._setButtonState();
+ if ((!this._incIconDisabled && increment) || (!this._decIconDisabled && !increment)) {
+ this._spinValue(increment);
+ } else {
+ this._resetSpin();
+ this._fireChangeEvent();
+ }
+ }
+ }, this._calcWaitTimeout());
+ }
+
+ /**
+ * Resets spin process
+ */
+ _resetSpin() {
+ clearTimeout(this._spinTimeoutId);
+ this._btnDown = false;
+ this._spinStarted = false;
+ }
+
+ /**
+ * Resets spin process when mouse outs + or - buttons
+ */
+ _resetSpinOut() {
+ if (this._btnDown) {
+ this._resetSpin();
+ this._fireChangeEvent();
+ }
+ }
+}
+StepInput.define();
+
+export default StepInput;
diff --git a/packages/main/src/i18n/messagebundle.properties b/packages/main/src/i18n/messagebundle.properties
index 0810328c60b3..a6a716c92de6 100644
--- a/packages/main/src/i18n/messagebundle.properties
+++ b/packages/main/src/i18n/messagebundle.properties
@@ -237,3 +237,9 @@ DAY_PICKER_NON_WORKING_DAY = Non-Working Day
#XBUT: Text for 'Today' in the DayPicker
DAY_PICKER_TODAY = Today
+
+#XTOL: tooltip for decrease button of the StepInput
+STEPINPUT_DEC_ICON_TITLE=Decrease
+
+#XTOL: tooltip for increase button of the StepInput
+STEPINPUT_INC_ICON_TITLE=Increase
diff --git a/packages/main/src/themes/Input.css b/packages/main/src/themes/Input.css
index 4add19371a73..d9a14aa14964 100644
--- a/packages/main/src/themes/Input.css
+++ b/packages/main/src/themes/Input.css
@@ -70,6 +70,7 @@
line-height: inherit;
letter-spacing: inherit;
word-spacing: inherit;
+ text-align: inherit;
}
[inner-input][inner-input-with-icon] {
@@ -91,6 +92,7 @@
[inner-input]::-webkit-input-placeholder {
font-style: italic;
color: var(--sapField_PlaceholderTextColor);
+ padding-right: 0.125rem;
}
:host([disabled]) [inner-input]::-moz-placeholder {
@@ -102,6 +104,7 @@
[inner-input]::-moz-placeholder {
font-style: italic;
color: var(--sapField_PlaceholderTextColor);
+ padding-right: 0.125rem;
}
:host([disabled]) [inner-input]:-ms-input-placeholder {
@@ -113,6 +116,7 @@
[inner-input]:-ms-input-placeholder {
font-style: italic;
color: var(--sapField_PlaceholderTextColor);
+ padding-right: 0.125rem;
}
.ui5-input-content {
@@ -206,3 +210,10 @@
::slotted([ui5-icon][slot="icon"]) {
padding: var(--_ui5_input_icon_padding);
}
+
+/* Chrome, Safari, Edge, Opera */
+[inner-input]::-webkit-outer-spin-button,
+[inner-input]::-webkit-inner-spin-button {
+ -webkit-appearance: inherit;
+ margin: inherit;
+}
diff --git a/packages/main/src/themes/StepInput.css b/packages/main/src/themes/StepInput.css
new file mode 100644
index 000000000000..c93aec6e3406
--- /dev/null
+++ b/packages/main/src/themes/StepInput.css
@@ -0,0 +1,206 @@
+@import "./InvisibleTextStyles.css";
+@import "./InputIcon.css";
+
+:host(:not([hidden])) {
+ display: inline-block;
+ width: 100%;
+}
+
+:host {
+ color: var(--sapField_TextColor);
+ background-color: var(--sapField_Background);
+ border: 1px solid var(--sapField_BorderColor);
+ border-radius: var(--_ui5_input_wrapper_border_radius);
+ box-sizing: border-box;
+ height: var(--_ui5_input_height);
+ position: relative;
+}
+
+:host .ui5-step-input-input {
+ text-align: inherit;
+}
+
+:host(:not([value-state]):not([readonly]):not([disabled]):hover),
+:host([value-state=None]:not([readonly]):not([disabled]):hover) {
+ background-color: var(--sapField_Hover_Background);
+ border: 1px solid var(--sapField_Hover_BorderColor);
+}
+
+:host([value-state=Success]:not([readonly]):not([disabled]))::after,
+:host([value-state=Error]:not([readonly]):not([disabled]))::after,
+:host([value-state=None]:not([readonly]):not([disabled]))::after,
+:host([value-state=Information]:not([readonly]):not([disabled]))::after,
+:host([value-state=Warning]:not([readonly]):not([disabled]))::after {
+ position: absolute;
+ content: "";
+ top: -1px;
+ right: -1px;
+ bottom: -1px;
+ left: -1px;
+ outline: none;
+ pointer-events: none;
+ border-radius: var(--_ui5_input_wrapper_border_radius);
+ border-style: var(--_ui5_input_error_warning_border_style);
+ z-index: 3;
+ border-width: 0px;
+}
+
+:host([value-state=Information]:not([readonly]):not([disabled]))::after {
+ border-color: var(--sapField_InformationColor);
+ border-width: var(--_ui5-input-information_border_width);
+}
+
+:host([value-state=Warning]:not([readonly]):not([disabled]))::after {
+ border-color: var(--sapField_WarningColor);
+ border-width: 2px;
+}
+
+:host([value-state=Success]:not([readonly]):not([disabled]))::after {
+ border-color: var(--sapField_SuccessColor);
+ border-width: 1px;
+}
+
+:host([value-state=Error]:not([readonly]):not([disabled]))::after {
+ border-color: var(--sapField_InvalidColor);
+ border-width: var(--_ui5-input-information_border_width);
+}
+
+:host([value-state])::after {
+ border-width: var(--_ui5_input_state_border_width);
+}
+
+:host([value-state=Error]:not([readonly]):not([disabled])) .ui5-step-input-input {
+ background-color: var(--sapField_InvalidBackground);
+}
+
+:host([value-state]:not([value-state="None"]) .ui5-step-input-input[focused]) {
+ outline: none;
+}
+
+:host .ui5-step-input-input {
+ width: 100%;
+ color: inherit;
+ background-color: inherit;
+ border: 1px solid transparent;
+ box-sizing: border-box;
+ vertical-align: top;
+ margin-top: -1px;
+ min-width: 8rem;
+ position: relative;
+ padding: 0px 2.5rem 0px 2.4375rem;
+ outline: none;
+}
+
+:host .ui5-step-input-input[text-align=left] {
+ text-align: left;
+}
+
+:host .ui5-step-input-input[text-align=center] {
+ text-align: center;
+}
+
+:host .ui5-step-input-input[text-align=right] {
+ text-align: right;
+}
+
+:host .ui5-step-icon {
+ position: absolute;
+ display: inline-block;
+ height: 2rem;
+ height: 100%;
+ background-color: var(--sapField_Background);
+ z-index: 2;
+}
+
+:host .ui5-step-icon[focused] {
+ border: none;
+ outline: none;
+}
+
+:host .ui5-step-icon.ui5-step-dec {
+ left: 0;
+}
+
+:host .ui5-step-icon.ui5-step-inc {
+ right: 0;
+}
+
+:host .ui5-step-icon *:not([clickable]),
+:host .ui5-step-icon *:not([clickable]):active,
+:host .ui5-step-icon *:not([clickable]):hover {
+ opacity: 0.5;
+ background-color: transparent;
+ color: var(--sapContent_IconColor);
+}
+
+:host .ui5-step-icon :not([clickable]) *:hover,
+:host .ui5-step-icon :not([clickable]) *:active {
+ background-color: var(--sapField_Background);
+ color: var(--sapContent_IconColor);
+}
+
+:host .ui5-step-input-input[focused]::after {
+ position: absolute;
+ content: "";
+ border: var(--_ui5_input_focus_border_width) dotted var(--sapContent_FocusColor);
+ top: 1px;
+ right: 0px;
+ bottom: 1px;
+ left: 0px;
+ outline: none;
+ pointer-events: none;
+ z-index: 3;
+}
+
+:host .ui5-step-input-input[focused] {
+ outline: none;
+}
+
+:host([value-state]:not([value-state=None]):not([value-state=Success]):not([readonly]):not([disabled])) .ui5-step-input-input[focused]::after {
+ top: 2px;
+ right: 1px;
+ bottom: 2px;
+ left: 1px;
+}
+
+:host([value-state=Information]:not([readonly]):not([disabled]))::after {
+ border-color: var(--sapField_InformationColor);
+ border-width: var(--_ui5-input-information_border_width);
+}
+
+:host([value-state=Warning]:not([readonly]):not([disabled]))::after {
+ border-color: var(--sapField_WarningColor);
+}
+
+:host([value-state=Success]:not([readonly]):not([disabled]))::after {
+ border-color: var(--sapField_SuccessColor);
+ border-width: 1px;
+}
+
+:host([value-state=Error]:not([readonly]):not([disabled]))::after {
+ border-color: var(--sapField_InvalidColor);
+}
+
+/* Disable spin buttons in Chrome, Safari, Edge, Opera */
+:host .ui5-step-input-input::-webkit-outer-spin-button,
+:host .ui5-step-input-input::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ margin: 0;
+}
+
+:host([disabled]) {
+ opacity: var(--_ui5_input_disabled_opacity);
+ cursor: default;
+ pointer-events: none;
+ background: var(--sapField_ReadOnly_Background);
+ border-color: var(--sapField_ReadOnly_BorderColor);
+}
+
+:host([disabled]) .ui5-step-icon {
+ background: var(--sapField_ReadOnly_Background);
+}
+
+:host([disabled]) .ui5-step-icon [ui5-icon] {
+ color: var(--sapField_ReadOnly_BorderColor);
+}
+
diff --git a/packages/main/test/pages/FormSupport.html b/packages/main/test/pages/FormSupport.html
index 603eb919a69f..3c39a87f06ee 100644
--- a/packages/main/test/pages/FormSupport.html
+++ b/packages/main/test/pages/FormSupport.html
@@ -18,22 +18,24 @@
++ + ++
++ + + +
++ + + +
++ + + + +
++ Number ++