diff --git a/packages/main/bundle.esm.js b/packages/main/bundle.esm.js
index ae5be1d7db48..876f8b7e49ba 100644
--- a/packages/main/bundle.esm.js
+++ b/packages/main/bundle.esm.js
@@ -30,6 +30,7 @@ import Link from "./dist/Link.js";
import Popover from "./dist/Popover.js";
import Panel from "./dist/Panel.js";
import RadioButton from "./dist/RadioButton.js";
+import ResponsivePopover from "./dist/ResponsivePopover.js";
import Select from "./dist/Select.js";
import Option from "./dist/Option.js";
import Switch from "./dist/Switch.js";
diff --git a/packages/main/src/ComboBox.hbs b/packages/main/src/ComboBox.hbs
index 9a39d04e4708..28a1821e88e0 100644
--- a/packages/main/src/ComboBox.hbs
+++ b/packages/main/src/ComboBox.hbs
@@ -3,7 +3,6 @@
aria-haspopup="listbox"
aria-expanded="{{open}}"
>
-
{
item.selected = (item === listItem.mappedItem);
@@ -410,15 +440,7 @@ class ComboBox extends UI5Element {
});
this._inputChange();
- this.popover.close();
- }
-
- get styles() {
- return {
- popover: {
- "min-width": `${this._width}px`,
- },
- };
+ this._respPopover.close();
}
get _filteredItems() {
@@ -427,12 +449,16 @@ class ComboBox extends UI5Element {
});
}
+ get _headerTitleText() {
+ return this.i18nBundle.getText(INPUT_SUGGESTIONS_TITLE);
+ }
+
get inner() {
- return this.shadowRoot.querySelector("[inner-input]");
+ return isPhone() ? this._respPopover.querySelector(".ui5-input-inner-phone") : this.shadowRoot.querySelector("[inner-input]");
}
- get popover() {
- return this.getStaticAreaItemDomRef().querySelector("ui5-popover");
+ get _respPopover() {
+ return this.getStaticAreaItemDomRef().querySelector("ui5-responsive-popover");
}
get editable() {
@@ -443,7 +469,7 @@ class ComboBox extends UI5Element {
await Promise.all([
ComboBoxItem.define(),
Icon.define(),
- Popover.define(),
+ ResponsivePopover.define(),
List.define(),
BusyIndicator.define(),
StandardListItem.define(),
diff --git a/packages/main/src/ComboBoxPopover.hbs b/packages/main/src/ComboBoxPopover.hbs
index 68f4f602cf16..386178841377 100644
--- a/packages/main/src/ComboBoxPopover.hbs
+++ b/packages/main/src/ComboBoxPopover.hbs
@@ -1,22 +1,48 @@
-
input
HTML element to enable form sumbit,
+ * The slot is used for native input
HTML element to enable form submit,
* when name
property is set.
* @type {HTMLElement[]}
* @private
@@ -86,7 +88,7 @@ const metadata = {
/**
* Defines whether ui5-input
is in disabled state.
* ui5-input
is completely uninteractive.
+ * Note: A disabled ui5-input
is completely non interactive.
*
* @type {boolean}
* @defaultvalue false
@@ -234,10 +236,6 @@ const metadata = {
type: Object,
},
- _popover: {
- type: Object,
- },
-
_inputAccInfo: {
type: Object,
},
@@ -342,7 +340,11 @@ class Input extends UI5Element {
}
static get styles() {
- return [styles];
+ return styles;
+ }
+
+ static get staticAreaStyles() {
+ return ResponsivePopoverCommonCss;
}
constructor() {
@@ -392,9 +394,17 @@ class Input extends UI5Element {
}
onAfterRendering() {
- if (!this.firstRendering && this.Suggestions) {
- this.Suggestions.toggle(this.shouldOpenSuggestions());
+ if (!this.firstRendering && !isPhone() && this.Suggestions) {
+ const shouldOpenSuggestions = this.shouldOpenSuggestions();
+
+ this.Suggestions.toggle(shouldOpenSuggestions);
+
+ if (!isPhone() && shouldOpenSuggestions) {
+ // Set initial focus to the native input
+ this.getInputDOMRef().focus();
+ }
}
+
this.firstRendering = false;
}
@@ -454,8 +464,20 @@ class Input extends UI5Element {
}
_onfocusout(event) {
- this.focused = false; // invalidating property
+ // if focusout is triggered by pressing on suggestion item skip invalidation, because re-rendering
+ // will happen before "itemPress" event, which will make item "active" state not visualized
+ if (this.Suggestions && event.relatedTarget && event.relatedTarget.shadowRoot.contains(this.Suggestions._respPopover)) {
+ return;
+ }
+
this.previousValue = "";
+ this.focused = false; // invalidating property
+ }
+
+ _click(event) {
+ if (isPhone() && !this.readonly && this.Suggestions) {
+ this.Suggestions.open(this);
+ }
}
_handleChange(event) {
@@ -483,6 +505,24 @@ class Input extends UI5Element {
}
}
+ _closeRespPopover() {
+ this.Suggestions.close();
+ }
+
+ _afterOpenPopover() {
+ // Set initial focus to the native input
+ if (isPhone()) {
+ this.getInputDOMRef().focus();
+ }
+ }
+
+ _afterClosePopover() {
+ // close device's keyboard and prevent further typing
+ if (isPhone()) {
+ this.blur();
+ }
+ }
+
enableSuggestions() {
if (this.Suggestions) {
return;
@@ -498,8 +538,8 @@ class Input extends UI5Element {
shouldOpenSuggestions() {
return !!(this.suggestionItems.length
- && this.showSuggestions
&& this.focused
+ && this.showSuggestions
&& !this.hasSuggestionItemSelected);
}
@@ -508,7 +548,6 @@ class Input extends UI5Element {
const fireInput = keyboardUsed
? this.valueBeforeItemSelection !== itemText : this.value !== itemText;
- item.selected = false;
this.hasSuggestionItemSelected = true;
this.fireEvent(this.EVENT_SUGGESTION_ITEM_SELECT, { item });
@@ -564,7 +603,17 @@ class Input extends UI5Element {
}
getInputDOMRef() {
- return this.getDomRef().querySelector(`#${this.getInputId()}`);
+ let inputDomRef;
+
+ if (isPhone()) {
+ inputDomRef = this.getStaticAreaItemDomRef().querySelector(".ui5-input-inner-phone");
+ }
+
+ if (!inputDomRef) {
+ inputDomRef = this.getDomRef().querySelector(`#${this.getInputId()}`);
+ }
+
+ return inputDomRef;
}
getLabelableElementId() {
@@ -604,6 +653,10 @@ class Input extends UI5Element {
return this.readonly && !this.disabled;
}
+ get _headerTitleText() {
+ return this.i18nBundle.getText(INPUT_SUGGESTIONS_TITLE);
+ }
+
get inputType() {
return this.type.toLowerCase();
}
diff --git a/packages/main/src/InputPopover.hbs b/packages/main/src/InputPopover.hbs
index 99171f01fd54..ec500335ff1e 100644
--- a/packages/main/src/InputPopover.hbs
+++ b/packages/main/src/InputPopover.hbs
@@ -1,13 +1,49 @@
{{#if showSuggestions}}
- header
and footer
are displayed only on mobile.
+ *
+ * @constructor
+ * @author SAP SE
+ * @alias sap.ui.webcomponents.main.ResponsivePopover
+ * @extends UI5Element
+ * @tagname ui5-responsive-popover
+ * @private
+ */
+class ResponsivePopover extends Popover {
+ static get metadata() {
+ return metadata;
+ }
+
+ static get render() {
+ return litRender;
+ }
+
+ static get styles() {
+ return ResponsivePopoverCss;
+ }
+
+ static get template() {
+ return ResponsivePopoverTemplate;
+ }
+
+ static async define(...params) {
+ await Dialog.define();
+
+ super.define(...params);
+ }
+
+ constructor() {
+ super();
+ this.placementType = "Bottom";
+ this.horizontalAlign = PopoverHorizontalAlign.Left;
+ }
+
+ /**
+ * @public
+ */
+ open(opener) {
+ if (!isPhone()) {
+ // make popover width be >= of the opener's width
+ if (!this.noStretch) {
+ this._minWidth = Math.max(POPOVER_MIN_WIDTH, opener.getBoundingClientRect().width);
+ }
+
+ this.openBy(opener);
+ } else {
+ this._dialog.open();
+ }
+ }
+
+ /**
+ * @public
+ */
+ close() {
+ if (!isPhone()) {
+ super.close();
+ } else {
+ this._dialog.close();
+ }
+ }
+
+ _afterDialogOpen(event) {
+ this.opened = true;
+ this._propagateDialogEvent(event);
+ }
+
+ _afterDialogClose(event) {
+ this.opened = false;
+ this._propagateDialogEvent(event);
+ }
+
+ _propagateDialogEvent(event) {
+ const type = event.type.replace("ui5-", "");
+
+ this.fireEvent(type, event.detail);
+ }
+
+ get styles() {
+ const popoverStyles = super.styles;
+
+ popoverStyles.root = {
+ "min-width": `${this._minWidth}px`,
+ };
+
+ return popoverStyles;
+ }
+
+ get _dialog() {
+ return this.shadowRoot.querySelector("ui5-dialog");
+ }
+
+ get _isPhone() {
+ return isPhone();
+ }
+
+ get _displayHeader() {
+ return this._isPhone;
+ }
+
+ get _displayFooter() {
+ return this._isPhone;
+ }
+}
+
+ResponsivePopover.define();
+
+export default ResponsivePopover;
diff --git a/packages/main/src/Select.hbs b/packages/main/src/Select.hbs
index bd459736873a..19e577ac3470 100644
--- a/packages/main/src/Select.hbs
+++ b/packages/main/src/Select.hbs
@@ -6,15 +6,13 @@
@keyup="{{_onkeyup}}"
@focusin="{{_onfocusin}}"
@focusout="{{_onfocusout}}"
+ @click="{{_toggleRespPopover}}"
>
- Input
{
- return suggestions.push(suggestion.textContent);
+ return suggestions.push({
+ text: suggestion.textContent,
+ icon: suggestion.icon,
+ });
});
return suggestions;
@@ -83,11 +86,11 @@ class Suggestions {
open() {
this._beforeOpen();
- this._getPopover().openBy(this._getComponent());
+ this._respPopover.open(this._getComponent());
}
close() {
- this._getPopover().close();
+ this._respPopover.close();
}
updateSelectedItemPosition(pos) {
@@ -104,7 +107,8 @@ class Suggestions {
this.selectedItemIndex = this._getItems().indexOf(item);
- this._getComponent().onItemSelected(item, keyboardUsed);
+ this._getComponent().onItemSelected(this._getRealItems()[this.selectedItemIndex], keyboardUsed);
+ item.selected = false;
this.close();
}
@@ -136,12 +140,12 @@ class Suggestions {
}
if (!this.attachedAfterOpened) {
- this._getPopover().addEventListener("ui5-afterOpen", this._onOpen.bind(this));
+ this._respPopover.addEventListener("ui5-afterOpen", this._onOpen.bind(this));
this.attachedAfterOpened = true;
}
if (!this.attachedAfterClose) {
- this._getPopover().addEventListener("ui5-afterClose", this._onClose.bind(this));
+ this._respPopover.addEventListener("ui5-afterClose", this._onClose.bind(this));
this.attachedAfterClose = true;
}
}
@@ -166,7 +170,7 @@ class Suggestions {
}
isOpened() {
- const popover = this._getPopover();
+ const popover = this._respPopover;
return !!(popover && popover.opened);
}
@@ -243,15 +247,14 @@ class Suggestions {
_getScrollContainer() {
if (!this._scrollContainer) {
- const popover = this._getPopover();
- this._scrollContainer = popover.getDomRef().querySelector(".ui5-popover-content");
+ this._scrollContainer = this._respPopover.getDomRef().shadowRoot.querySelector(".ui5-popover-content");
}
return this._scrollContainer;
}
_getItems() {
- return this._getComponent().getSlottedNodes(this.slotName);
+ return [].slice.call(this._respPopover.querySelectorAll("ui5-li"));
}
_getComponent() {
@@ -259,11 +262,15 @@ class Suggestions {
}
_getList() {
- return this._getComponent().getStaticAreaItemDomRef().querySelector("ui5-popover").querySelector("ui5-list");
+ return this._getComponent().getStaticAreaItemDomRef().querySelector("ui5-responsive-popover").querySelector("ui5-list");
+ }
+
+ _getRealItems() {
+ return this._getComponent().getSlottedNodes(this.slotName);
}
- _getPopover() {
- return this._getComponent().getStaticAreaItemDomRef().querySelector("ui5-popover");
+ get _respPopover() {
+ return this._getComponent().getStaticAreaItemDomRef().querySelector("ui5-responsive-popover");
}
}
@@ -272,7 +279,7 @@ Suggestions.SCROLL_STEP = 48;
// The List and Popover components would be rendered
// by the issuer component`s template.
List.define();
-Popover.define();
+ResponsivePopover.define();
// Add suggestions support to the global features registry so that Input.js can use it
diff --git a/packages/main/src/i18n/messagebundle.properties b/packages/main/src/i18n/messagebundle.properties
index 07077d71a9e2..752ecdb29b06 100644
--- a/packages/main/src/i18n/messagebundle.properties
+++ b/packages/main/src/i18n/messagebundle.properties
@@ -220,6 +220,9 @@ ICON_ZOOM_OUT=Zoom Out
#XBUT: A link that can be clicked to display more/all items
INPUT_SUGGESTIONS=Suggestions available
+#XBUT: Default title text for mobile
+INPUT_SUGGESTIONS_TITLE=Select
+
#XFLD: Subtle link description label
LINK_SUBTLE=Subtle
diff --git a/packages/main/src/themes/DatePickerPopover.css b/packages/main/src/themes/DatePickerPopover.css
new file mode 100644
index 000000000000..69765925a868
--- /dev/null
+++ b/packages/main/src/themes/DatePickerPopover.css
@@ -0,0 +1,3 @@
+:host ui5-calendar {
+ width: 100%;
+}
\ No newline at end of file
diff --git a/packages/main/src/themes/DayPicker.css b/packages/main/src/themes/DayPicker.css
index d82082fa786f..8f9ae2039159 100644
--- a/packages/main/src/themes/DayPicker.css
+++ b/packages/main/src/themes/DayPicker.css
@@ -25,6 +25,7 @@
.ui5-dp-content {
display: flex;
+ flex-basis: 87.5%;
flex-direction: column;
font-family: var(--sapFontFamily);
}
@@ -36,6 +37,7 @@
.ui5-dp-weeknumber-container {
padding-top: var(--_ui5_daypicker_weeknumbers_container_padding_top);
+ flex-basis: 12.5%;
}
.ui5-dp-weekname,
@@ -43,6 +45,7 @@
.ui5-dp-weekname-container,
.ui5-dp-item {
display: flex;
+ flex-grow: 1;
justify-content: center;
align-items: center;
font-size: var(--sapFontSmallSize);
diff --git a/packages/main/src/themes/Dialog.css b/packages/main/src/themes/Dialog.css
index d4cdf8e4e4ba..9536e427616c 100644
--- a/packages/main/src/themes/Dialog.css
+++ b/packages/main/src/themes/Dialog.css
@@ -40,3 +40,7 @@
flex: 1 1 auto;
display: flex;
}
+
+.ui5-dialog-section .ui5-popup-content {
+ flex-basis: 100%;
+}
diff --git a/packages/main/src/themes/ResponsivePopover.css b/packages/main/src/themes/ResponsivePopover.css
new file mode 100644
index 000000000000..603272dd7c07
--- /dev/null
+++ b/packages/main/src/themes/ResponsivePopover.css
@@ -0,0 +1,13 @@
+@import "./Popover.css";
+
+:host {
+ --_ui5_input_width: 100%;
+}
+
+:host(:not([with-padding])) {
+ --_ui5_popover_content_padding: 0;
+}
+
+:host([opened]) {
+ display: inline-block;
+}
\ No newline at end of file
diff --git a/packages/main/src/themes/ResponsivePopoverCommon.css b/packages/main/src/themes/ResponsivePopoverCommon.css
new file mode 100644
index 000000000000..096b437e94c0
--- /dev/null
+++ b/packages/main/src/themes/ResponsivePopoverCommon.css
@@ -0,0 +1,127 @@
+/* styles for input in dialog */
+.input-root-phone {
+ flex: 1;
+ height: var(--_ui5_input_height);
+ color: var(--sapField_TextColor);
+ font-size: var(--sapFontMediumSize);
+ font-family: var(--sapFontFamily);
+ background-color: var(--sapField_Background);
+ border: 1px solid var(--sapField_BorderColor);
+ border-radius: var(--_ui5_input_wrapper_border_radius);
+ box-sizing: border-box;
+}
+
+.input-root-phone [inner-input] {
+ padding: 0 0.5rem;
+ width: 100%;
+ height: 100%;
+}
+
+.input-root-phone[value-state]:not([value-state="None"])[focused] {
+ outline: var(--_ui5_input_focus_border_width) dotted var(--sapContent_FocusColor);
+ outline-offset: -4px;
+}
+
+.input-root-phone [value-state="Error"] [input-icon][data-ui5-compact-size],
+.input-root-phone [value-state="Success"] [input-icon][data-ui5-compact-size],
+.input-root-phone [value-state="Warning"] [input-icon][data-ui5-compact-size] {
+ padding: .1875rem .5rem
+}
+
+[inner-input] {
+ background: transparent;
+ color: inherit;
+ border: none;
+ font-style: normal;
+ -webkit-appearance: none;
+ -moz-appearance: textfield;
+ line-height: normal;
+ padding: var(--_ui5_input_inner_padding);
+ box-sizing: border-box;
+ min-width: 3rem;
+ text-overflow: ellipsis;
+ flex: 1;
+ outline: none;
+ font-size: inherit;
+ font-family: inherit;
+}
+
+[inner-input]::selection,
+[inner-input]::-moz-selection {
+ background: var(--sapSelected);
+ color: var(--sapContent_contrastTextColor);
+}
+
+/* Input placeholder */
+[inner-input]::-webkit-input-placeholder {
+ font-style: italic;
+ color: var(--sapField_PlaceholderTextColor);
+}
+
+[inner-input]::-moz-placeholder {
+ font-style: italic;
+ color: var(--sapField_PlaceholderTextColor);
+}
+
+[inner-input]:-ms-input-placeholder {
+ font-style: italic;
+ color: var(--sapField_PlaceholderTextColor);
+}
+
+.input-root-phone[value-state]:not([value-state="None"]) {
+ border-width: var(--_ui5_input_state_border_width);
+}
+
+.input-root-phone[value-state="Error"] [inner-input],
+.input-root-phone[value-state="Warning"] [inner-input] {
+ font-style: var(--_ui5_input_error_warning_font_style);
+}
+
+.input-root-phone[value-state="Error"] [inner-input] {
+ font-weight: var(--_ui5_input_error_font_weight);
+}
+
+.input-root-phone[value-state="Error"]:not([readonly]) {
+ background-color: var(--sapField_InvalidBackground);
+ border-color: var(--sapField_InvalidColor);
+}
+
+.input-root-phone[value-state="Error"]:not([readonly]):not([disabled]),
+.input-root-phone[value-state="Warning"]:not([readonly]):not([disabled]) {
+ border-style: var(--_ui5_input_error_warning_border_style);
+}
+
+.input-root-phone[value-state="Warning"]:not([readonly]) {
+ background-color: var(--sapField_WarningBackground);
+ border-color: var(--sapField_WarningColor);
+}
+
+.input-root-phone[value-state="Success"]:not([readonly]) {
+ background-color: var(--sapField_SuccessBackground);
+ border-color: var(--sapField_SuccessColor);
+}
+
+/* Remove IE's defaut clear button */
+[inner-input]::-ms-clear {
+ height: 0;
+ width: 0;
+}
+
+/* MultiComboBox specific */
+.ui5-multi-combobox-toggle-button {
+ margin-left: 0.5rem;
+}
+
+/* Header and footer layout */
+.ui5-responsive-popover-header .row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0.25rem 1rem;
+}
+
+.ui5-responsive-popover-footer {
+ display: flex;
+ justify-content: flex-end;
+ padding: 0.25rem;
+}
\ No newline at end of file
diff --git a/packages/main/test/pageobjects/DatePickerFGPage.js b/packages/main/test/pageobjects/DatePickerFGPage.js
index 816c692be069..224852341a24 100644
--- a/packages/main/test/pageobjects/DatePickerFGPage.js
+++ b/packages/main/test/pageobjects/DatePickerFGPage.js
@@ -3,7 +3,7 @@ class DatePickerFGPage {
get dpStart() { return $('#ui5-datepicker--startDate'); }
get startPopoverContent() {
const staticAreaItemClassName = browser.getStaticAreaItemClassName("#ui5-datepicker--startDate");
- return browser.$(`.${staticAreaItemClassName}`).shadow$("ui5-popover"); }
+ return browser.$(`.${staticAreaItemClassName}`).shadow$("ui5-responsive-popover"); }
get startInnerInput() { return browser.$("#ui5-datepicker--startDate").shadow$("ui5-input").shadow$("input"); }
get dpEnd() { return $('#ui5-datepicker--endDate'); }
diff --git a/packages/main/test/pageobjects/DatePickerTestPage.js b/packages/main/test/pageobjects/DatePickerTestPage.js
index f30ff0f75522..742bb883545e 100644
--- a/packages/main/test/pageobjects/DatePickerTestPage.js
+++ b/packages/main/test/pageobjects/DatePickerTestPage.js
@@ -20,11 +20,11 @@ class DatePickerTestPage {
}
get popover() {
- return browser.$(`.${this.staticAreaItemClassName}`).shadow$("ui5-popover");
+ return browser.$(`.${this.staticAreaItemClassName}`).shadow$("ui5-responsive-popover");
}
get popoverContent() {
- return browser.$(this._sut).shadow$("ui5-popover").shadow$(".ui5-popover-root");
+ return browser.$(this._sut).shadow$("ui5-responsive-popover").shadow$("ui5-popover").shadow$(".ui5-popover-root");
}
get calendar() {
diff --git a/packages/main/test/pages/ComboBox.html b/packages/main/test/pages/ComboBox.html
index 288111ffc8cd..83f631d92f68 100644
--- a/packages/main/test/pages/ComboBox.html
+++ b/packages/main/test/pages/ComboBox.html
@@ -2,9 +2,9 @@
-
-
-
+
+
+