diff --git a/bundles/org.openhab.ui.basic/snippets-src/buttons.html b/bundles/org.openhab.ui.basic/snippets-src/buttons.html index 23eac2e086..5502192bc2 100644 --- a/bundles/org.openhab.ui.basic/snippets-src/buttons.html +++ b/bundles/org.openhab.ui.basic/snippets-src/buttons.html @@ -2,7 +2,7 @@ %icon_snippet% - + %label% diff --git a/bundles/org.openhab.ui.basic/src/main/java/org/openhab/ui/basic/internal/render/SwitchRenderer.java b/bundles/org.openhab.ui.basic/src/main/java/org/openhab/ui/basic/internal/render/SwitchRenderer.java index 6b131e85f4..9b527e6b95 100644 --- a/bundles/org.openhab.ui.basic/src/main/java/org/openhab/ui/basic/internal/render/SwitchRenderer.java +++ b/bundles/org.openhab.ui.basic/src/main/java/org/openhab/ui/basic/internal/render/SwitchRenderer.java @@ -133,9 +133,17 @@ public EList renderWidget(Widget w, StringBuilder sb, String sitemap) th snippet = preprocessSnippet(snippet, w); - snippet = snippet.replaceAll("%height_auto%", multiline ? "mdl-form__row--height-auto" : ""); - snippet = snippet.replaceAll("%buttons_class%", - multiline ? "mdl-form__buttons-multiline" : "mdl-form__buttons"); + if (multiline) { + snippet = snippet // + .replaceAll("%height_auto%", "mdl-form__row--height-auto") + .replaceAll("%buttons_class%", "mdl-form__buttons-multiline") + .replaceAll("%label_class%", "mdl-form__label-multiline"); + } else { + snippet = snippet // + .replaceAll("%height_auto%", "") // + .replaceAll("%buttons_class%", "mdl-form__buttons") // + .replaceAll("%label_class%", ""); + } StringBuilder buttons = new StringBuilder(); if (s.getMappings().isEmpty() && item != null) { diff --git a/bundles/org.openhab.ui.basic/web-src/_layout.scss b/bundles/org.openhab.ui.basic/web-src/_layout.scss index 7c4f1f42ef..ea4ec39ae4 100644 --- a/bundles/org.openhab.ui.basic/web-src/_layout.scss +++ b/bundles/org.openhab.ui.basic/web-src/_layout.scss @@ -168,7 +168,7 @@ @media screen and (max-width: $layout-tablet-size-threshold) { padding-left: $form-row-mobile-padding; html.ui-layout-condensed & { - padding-left: $form-row-mobile-padding-condensed; + padding-left: 0; // there's already padding in .mdl-form__row } } html:not(.ui-icons-enabled) & { @@ -212,16 +212,16 @@ } } &__label { + padding-left: $form-row-desktop-padding; + html.ui-layout-condensed & { + padding-left: $form-row-desktop-padding-condensed; + } @media screen and (max-width: $layout-tablet-size-threshold) { padding-left: $form-row-mobile-padding; html.ui-layout-condensed & { padding-left: $form-row-mobile-padding-condensed; } } - padding-left: $form-row-desktop-padding; - html.ui-layout-condensed & { - padding-left: $form-row-desktop-padding-condensed; - } @include flex-shrink(0); @include flex-grow(2); @include flex-2011(2 2 auto); @@ -232,18 +232,22 @@ font-size: 18px; } } + &__label-multiline { + min-width: 3em; + } &__control { + padding-right: $form-row-desktop-padding; + padding-left: 4px; + html.ui-layout-condensed & { + padding-left: 0; + padding-right: $form-row-desktop-padding-condensed; + } @media screen and (max-width: $layout-tablet-size-threshold) { padding-right: $form-row-mobile-padding; html.ui-layout-condensed & { - padding-left: $form-row-mobile-padding-condensed; + padding-right: $form-row-mobile-padding-condensed; } } - padding-right: $form-row-desktop-padding; - padding-left: 4px; - html.ui-layout-condensed & { - padding-left: $form-row-desktop-padding-condensed; - } font-weight: 700; html.ui-capitalize-values & { text-transform: uppercase; @@ -270,7 +274,7 @@ .buttongrid-cell { height: 36px; .buttongrid-button { - min-width: 100%; + min-width: 100% !important; text-transform: none; } } @@ -279,6 +283,12 @@ box-shadow: none; -webkit-box-shadow: none; text-transform: unset; + min-width: 54px; + html.ui-layout-condensed & { + min-width: 40px; + padding-left: 4px; + padding-right: 4px; + } } .mdl-button-text { html.ui-bigger-font & { @@ -502,16 +512,20 @@ } &__buttons { padding-top: 2px; + padding-bottom: 2px; } &__buttons-multiline { margin: 6px 0; + gap: 4px; html.ui-layout-condensed & { margin: 0; + gap: 3px; } - max-width: 60%; + padding-top: 2px; + padding-bottom: 2px; display: flex; flex-wrap: wrap; - gap: 4px; + justify-content: end; } &__buttongrid { padding: 0; diff --git a/bundles/org.openhab.ui.basic/web-src/smarthome.js b/bundles/org.openhab.ui.basic/web-src/smarthome.js index 2fb704bbe8..804ddcfda8 100644 --- a/bundles/org.openhab.ui.basic/web-src/smarthome.js +++ b/bundles/org.openhab.ui.basic/web-src/smarthome.js @@ -168,6 +168,12 @@ }); } + function getElementWidth(element) { + var style = getComputedStyle(element); + return element.offsetWidth + parseFloat(style.marginLeft) + parseFloat(style.marginRight) + + parseFloat(style.paddingLeft) + parseFloat(style.paddingRight); + } + function EventMapper() { var _t = this; @@ -1177,6 +1183,7 @@ Control.call(this, parentNode); var + maxButtonWidth = 0, _t = this; _t.ignoreState = _t.parentNode.getAttribute("data-ignore-state") === "true"; @@ -1239,6 +1246,7 @@ ) { _t.valueMap[itemState].classList.add(o.buttonActiveClass); } + _t.minimizeWidth(); }; _t.setValueColor = function(color) { @@ -1337,8 +1345,72 @@ icon.addEventListener("load", _t.convertToInlineSVG); icon.addEventListener("error", _t.replaceImageWithNone); } + + if (maxButtonWidth < button.offsetWidth) { + maxButtonWidth = button.offsetWidth; + } }); + if (_t.buttons.length > 1 && _t.parentNode.classList.contains(o.buttonsMultilineClass)) { + var labelMinWidth = 0; + if (_t.label.textContent.trim().length === 0) { + _t.label.style.paddingLeft = 0; + _t.label.style.minWidth = 0; + } else { + // Try to see if setting min-width: min-content would result in a narrower min-width + // than the one set in _layout.scss, e.g. when the label is short. + // If it does make it narrower, it frees up more space for the buttons. + // If it is not narrower, un-set it, so that the min-width from _layout.scss can take effect. + + // To measure the min-width using offsetWidth, + // we need to make the neighbouring element (buttons) as wide as possible + // to force the label to shrink to its min-width + // Note that _t.parentNode.style.width will get readjusted inside minimizeWidth() + // so setting it to 100% here wouldn't affect the final layout. + _t.parentNode.style.width = "100%"; + + var defaultMinWidth = parseFloat(getComputedStyle(_t.label).minWidth); + _t.label.style.minWidth = "min-content"; + var minContentWidth = _t.label.offsetWidth; + if (minContentWidth > defaultMinWidth) { + _t.label.style.removeProperty("min-width"); + labelMinWidth = defaultMinWidth; + } else { + labelMinWidth = minContentWidth; + } + } + + _t.minimizeWidth = function() { + // Minimize the width taken by the buttons without adding extra rows. + // Start from the maximum width the buttons can take, + // then shrink it down to the minimum without causing additional wrapping. + var buttons = _t.parentNode; + var buttonsStyle = getComputedStyle(buttons); + var labelStyle = getComputedStyle(_t.label); + // Calculate the maximum width the buttons can take + var width = buttons.parentElement.offsetWidth - + parseFloat(buttonsStyle.paddingLeft) - parseFloat(buttonsStyle.paddingRight) - + getElementWidth(_t.iconContainer) - getElementWidth(_t.value); + if (labelMinWidth) { + width -= labelMinWidth + parseFloat(labelStyle.paddingLeft) + parseFloat(labelStyle.paddingRight); + } + buttons.style.width = width + "px"; + width = buttons.offsetWidth; + var height = buttons.offsetHeight; + while (buttons.offsetHeight === height && width >= maxButtonWidth) { + buttons.style.width = --width + "px"; + } + buttons.style.width = (width+1) + "px"; + }; + + _t.minimizeWidth(); + // Wait until after all the icons are loaded before running minimizeWidth() + window.addEventListener("load", _t.minimizeWidth); + window.addEventListener("resize", _t.minimizeWidth); + } else { + _t.minimizeWidth = function() {}; + } + _t.destroy = function() { _t.buttons.forEach(function(button) { var @@ -1361,6 +1433,8 @@ } }); componentHandler.downgradeElements(_t.buttons); + window.removeEventListener("load", _t.minimizeWidth); + window.removeEventListener("resize", _t.minimizeWidth); }; _t.setValueColor(_t.valueColor); @@ -3936,6 +4010,7 @@ buttonTextClass: "mdl-button-text", buttonIconText: ".mdl-button-icon-text", buttonIconTextClass: "mdl-button-icon-text", + buttonsMultilineClass: "mdl-form__buttons-multiline", modal: ".mdl-modal", modalContainer: ".mdl-modal__content", selectionRows: ".mdl-form__selection-rows",