[OH3] Main UI - New „main_widget“ - development and testing [deprecated]

Hey @Dimitris ,

thanks for sharing the stylguide. Could you enrich it with example sizes for example for font-size, height of the Header, Footer, etc…

For the meanwhile:

Next Little step - give it a try;-)

uid: test_ui
tags: []
props:
  parameters:
    - description: The label for the widget
      label: Title
      name: title
      required: false
      type: TEXT
    - context: item
      description: The light switch Item
      label: Item
      name: item
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Aug 17, 2022, 6:13:01 PM
component: f7-block
config: {}
slots:
  default:
    - component: f7-block
      config:
        style:
          height: 100%
      slots:
        default:
          - component: f7-segmented
            slots:
              default:
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: SECTION1
                    actionVariable: selectSection
                    text: Home
                    large: true
                    style:
                      font-weight: 200
                      font-size: 30px
                      text-decoration: underline
                      text-decoration-color: '=vars.selectSection=="SECTION1" ? "#F8BB00" : "transparent"'
                      text-underline-offset: 4px
                      color: '=vars.selectSection=="SECTION1" ? "black" : "#8C8C8C"'
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: SECTION2
                    actionVariable: selectSection
                    text: Rooms
                    large: true
                    style:
                      font-weight: 200
                      font-size: 30px
                      color: '=vars.selectSection=="SECTION2" ? "black" : "#8C8C8C"'
                      text-decoration: underline
                      text-decoration-color: '=vars.selectSection=="SECTION2" ? "#F8BB00" : "transparent"'
                      text-underline-offset: 4px
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: SECTION3
                    actionVariable: selectSection
                    text: Floors
                    large: true
                    style:
                      font-weight: 200
                      font-size: 30px
                      color: "#8C8C8C"
          - component: f7-segmented
            config:
              visible: =!!(vars.selectSection == "SECTION2")
            slots:
              default:
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: DIV1
                    actionVariable: selectDivision
                    text: Living Room
                    style:
                      color: '=vars.selectDivision=="DIV1" ? "black" : "#8C8C8C"'
                      text-decoration: underline
                      text-underline-offset: 4px
                      text-decoration-color: '=vars.selectSection + vars.selectDivision  == "SECTION2DIV1" ? "#F8BB00" : "transparent"'
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: DIV2
                    actionVariable: selectDivision
                    text: Bedroom
                    style:
                      color: '=vars.selectDivision=="DIV2" ? "black" : "#8C8C8C"'
                      text-decoration: underline
                      text-underline-offset: 3px
                      text-decoration-color: '=vars.selectSection + vars.selectDivision  == "SECTION2DIV2" ? "#F8BB00" : "transparent"'
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: DIV3
                    actionVariable: selectDivision
                    text: Kitchen
                    style:
                      color: '=vars.selectDivision=="DIV3" ? "black" : "#8C8C8C"'
                      text-decoration: underline
                      text-underline-offset: 3px
                      text-decoration-color: '=vars.selectSection + vars.selectDivision  == "SECTION2DIV3" ? "#F8BB00" : "transparent"'
          - component: f7-segmented
            config:
              visible: =!!(vars.selectSection == "SECTION3")
            slots:
              default:
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: OTHER1
                    actionVariable: selectOther
                    text: Other One
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: OTHER2
                    actionVariable: selectOther
                    text: Other Two
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: OTHER3
                    actionVariable: selectOther
                    text: Other Three
          - component: oh-cell
            config:
              visible: =!!(vars.selectSection + vars.selectDivision + vars.selectThing == "SECTION2DIV1Lights")
              icon: f7:lightbulb
              title: I am a light card in the Living Room
              subtitle: only for testing
          - component: oh-cell
            config:
              visible: =!!(vars.selectSection + vars.selectDivision + vars.selectThing == "SECTION2DIV2Lights")
              icon: f7:lightbulb
              title: I am a light card in the Bedroom
              subtitle: only for testing
          - component: Label
            config:
              text: =vars.selectSection + vars.selectDivision
          - component: Label
            config:
              text: =vars.SecDiv
          - component: f7-segmented
            config:
              class:
                - segmented-strong
              style:
                --f7-segmented-strong-padding: 0px
                --f7-segmented-strong-between-buttons: 0px
                --f7-segmented-strong-button-font-weight: 300
                --f7-segmented-strong-bg-color: transparent
                g--f7-segmented-strong-button-hover-bg-color: rgba(255, 255, 255, 0.15)
            slots:
              default:
                - component: oh-button
                  config:
                    class:
                      - padding-top-half
                      - display-flex
                      - flex-direction-column
                    text: Lights
                    action: variable
                    actionVariable: selectThing
                    actionVariableValue: Lights
                    icon-f7: lightbulb
                    iconSize: 20
                    iconColor: black
                    fill: '=vars.selectThing=="Lights" ? true : false'
                    style:
                      --f7-button-text-color: '=vars.selectThing=="Lights" ? "#6A6A6A" : "#8C8C8C"'
                      --f7-button-border-radius: 15px
                      --f7-button-padding-vertical: 0px
                      --f7-button-padding-horizontal: 0px
                      --f7-button-bg-color: '=vars.selectThing=="Lights" ? "#F8BB00" : "transparent"'
                      --f7-button-hover-bg-color: '=vars.selectThing=="Lights" ? "F8BB00" : "transparent"'
                      height: auto
                      font-size: 12px
                - component: oh-button
                  config:
                    class:
                      - padding-top-half
                      - display-flex
                      - flex-direction-column
                    text: Climate
                    action: variable
                    actionVariable: selectThing
                    actionVariableValue: Climate
                    icon-f7: snow
                    iconSize: 20
                    iconColor: black
                    fill: '=vars.selectThing=="Climate" ? true : false'
                    style:
                      --f7-button-text-color: '=vars.selectThing=="Climate" ? "#6A6A6A" : "#8C8C8C"'
                      --f7-button-border-radius: 15px
                      --f7-button-padding-vertical: 0px
                      --f7-button-padding-horizontal: 0px
                      --f7-button-bg-color: '=vars.selectThing=="Climate" ? "#F8BB00" : "transparent"'
                      --f7-button-hover-bg-color: '=vars.selectThing=="Climate" ? "F8BB00" : "transparent"'
                      height: auto
                      font-size: 12px
                - component: oh-button
                  config:
                    class:
                      - padding-top-half
                      - display-flex
                      - flex-direction-column
                    text: Rollers
                    action: variable
                    actionVariable: selectThing
                    actionVariableValue: Rollers
                    icon-f7: archivebox
                    iconSize: 20
                    iconColor: black
                    fill: '=vars.selectThing=="Rollers" ? true : false'
                    style:
                      --f7-button-text-color: '=vars.selectThing=="Rollers" ? "#6A6A6A" : "#8C8C8C"'
                      --f7-button-border-radius: 15px
                      --f7-button-padding-vertical: 0px
                      --f7-button-padding-horizontal: 0px
                      --f7-button-bg-color: '=vars.selectThing=="Rollers" ? "#F8BB00" : "transparent"'
                      --f7-button-hover-bg-color: '=vars.selectThing=="Rollers" ? "F8BB00" : "transparent"'
                      height: auto
                      font-size: 12px
                - component: oh-button
                  config:
                    class:
                      - padding-top-half
                      - display-flex
                      - flex-direction-column
                    text: Security
                    action: variable
                    actionVariable: selectThing
                    actionVariableValue: Security
                    icon-f7: camera
                    iconSize: 20
                    iconColor: black
                    fill: '=vars.selectThing=="Security" ? true : false'
                    style:
                      --f7-button-text-color: '=vars.selectThing=="Security" ? "#6A6A6A" : "#8C8C8C"'
                      --f7-button-border-radius: 15px
                      --f7-button-padding-vertical: 0px
                      --f7-button-padding-horizontal: 0px
                      --f7-button-bg-color: '=vars.selectThing=="Security" ? "#F8BB00" : "transparent"'
                      --f7-button-hover-bg-color: '=vars.selectThing=="Security" ? "F8BB00" : "transparent"'
                      height: auto
                      font-size: 12px

Beware of browser’s dark theme =) Black on black isn’t good to read. Try to use something like color: var(–f7-text-color), var(–f7-bars-bg-color)- they will change depending of theme used. The vars and their colors are seen in browser’s page inspector.

The only thing, in my experience, that noticeably impacts widget performance is too many oh-repeater components that require an API call, such as the itemsWithTags etc. You only have one of those and it’s not really a very complex one so there’s not much performance to optimize.

There are a few places where, if you are interested, the code can be simplified a little:

You have a few places where something like this happens:

text: =(Number.parseFloat(items[props.temp_item].state.split(".")[0]))+" °C"

There are lots of places where you properly use the Number.parseFloat method to change a string into a number for numerical comparison. Here, however, it is unnecessary. For the text property you are just creating a string so you don’t need to change the state from a string to a number first, just leave it as a string:

text: =items[props.temp_item].state.split(".")[0] + " °C"

Alternately, if you are splitting the state at the decimal point because you just want an integer value, you can use the Number object, but just use parseInt and then you don’t need the split:

text: =Number.parseInt(items[props.temp_item].state)+" °C"

You have several instance where you test a variable (or one instance where you test 4 or 5 variables) with a ternary expression such as:

visible: "=props.humidity_item ? true : false"

This is redundant. The ternary expression tests the first part for true or false and provides the first listed result in case the of true and the second in case of false. So what this says is “If this is true then true, but if it’s false, then false.” So, for properties such as the visible property that take a boolean value, you can just use the initial expression:

visible: =props.humidity_item

Any real value (except false and 0) is true, and undefined values are false. So if the variable exists and has a real value this will be true and if the variable is undefined, then it will be false.

Lastly, perhaps the biggest increase in clarity can come where you have all the possible icon definitions such as here:

icon: '=(Number.parseFloat(items[props.temp_item].state.split(" ")[0]) > 30) ? "my_temp_02" : (Number.parseFloat(items[props.temp_item].state.split(" ")[0]) < 0) ? "my_temp_06" : (Number.parseFloat(items[props.temp_item].state.split(" ")[0]) < 10) ? "my_temp_05" : "my_temp_10"'

There are instructions in the docs for creating icons that follow the OH system for dynamic icons. If you work out how to change the names of your icons to follow that system, then you will probably just be able to replace that whole expression with the base name of the icon.

1 Like

This looks fantastic! Is this possible in MainUI If so, would you be so kind as to share? Mine looks terrible

Hey @dastrix80,

we try put the things together - but we are still at a starting point.

A first technical draft - based on @Dimitris Design-draft is here:

https://community.openhab.org/t/oh3-main-ui-examples/117928/315?u=nic0205

At the moment it looks like this:

For now I am working to get the Menu-structure working. After that, we will develop the widgets and put all together :wink:

And yes - all this is then a combinations of widgets in the MainUI.

Help is appreciate :wink:

3 Likes

It’s great work! I really like the crisp clean layout. I agree tabs or the room/floor split out is fantastic

I’ve been freely modifying the roomCard_12 - but I have hit a barrier. I just can’t seem to get popups to work.
I have achieved it on 1 card, works as expected, on another by simply modifying the code…it’s used for weather. But generic roomCard will not popup any other page.

Yep, I’ve tried to use this initially, but for some strange reason that wasn’t working and the expression was always considered to be true.
Thanks for tips, fixed overparsing, as for icons part, this is still under construction, because I’m not happy with any iconset I can find, so i’ve just let it be as it is for now till I’ll catch icons designer.
2 minor, but strange bugs - tabs in bigger widget sometimes switch from second tap, and popup navbars is not hidden on Galaxy s8 (but hides on Galaxy s9-s20).
I’ve read you aren’t happy with tabs usage - is there anything I should change?

Hey @Dimitris,

integration our widgets works :wink:

It’s only a very first attempt - but works…

3 Likes

Congrats Pal! Great job!!

I am not opposed to using tabs in most instances. The limitation, however, is that they are very much intended and designed to be used only as a single layer of switch (i.e., not nested) and any attempt to fit them into a more complicated arrangement gets VERY cumbersome very quickly. Part of this is just that nested logic in general gets more complex exponentially and part of this is simply the limitations that are set by the design decisions that go into the tabs. If you are going only for something as simple as the tabs on the MainUI home pages then the the f7 tabs are a great solution. If you want something more complex, then there is a good chance that building your own system from some of the other available components will lead to more satisfactory results.

I can’t say, off the top of my head, that I recognize either of these issues from the brief descriptions. In general though, something that is expected to happen on one click that takes two instead is usually an issue with the initial conditions (some value that you are expecting to be initialized is not or is initialized to the wrong value) and different behavior of the same code in different devices/browsers is outside of OH control unless it is specific to how certain browsers treat css properties.

1 Like

Hey @JustinG ,

it goes further and further :wink:

Do you have a hint for me how I could “fix” the header and the footer? Header and footer should always stay at the top or bottom of the widget - independent of the content in between. I think I have to define a kind of min-height - but to be honest I do not really know how achieve this.

Is there a oh or f7-component with that I could achieve this?

Everything looks great,but when finished how many props you have to set? One for every device of every room? This will look crazy…or there will be any other way to do this?

Good point. There will be tons of props :partying_face:

In fact the amount of props would be the same as with single widgets.

My first thought is to use something like a short script to create the complete code of the menu-widget.

This isn’t so much a problem with widget components as it is a css issue. There are numerous different ways to address this with css and which is the “best” is probably a matter of some of the design parameters of the rest of the app plus a healthy does of personal preference. One way or the other though, you’re going to have to grapple with css placement and spacing.

For a first pass, I would probably put all the main pieces in an element with flex-column alignement of some sort with tight restrictions on the growth/shrinkage of the header and footer pieces and then make sure that the central area has appropriate overflow handling.

I am so sorry, but could you please give me an example-code as a base? Perhaps I am just to new to all this stuff but do not really know how to start…

Many of the widgets already available here on the forums show simple flexbox usages. I suspect that if you search for something like “display: flex” you’ll get several dozen useful results.

More important is to understand what the flexbox is doing because just trying to follow a cookie cutter approach is, sooner or later, going to lead you to tremendous frustration. This is my favorite link to use when I need a flexbox refresher: A Complete Guide to Flexbox | CSS-Tricks - CSS-Tricks

Thanks for helping again and again.

Flexbox is something totally new for me but I try to understand it.

If I understand it right then I need a Flexbox container and different flex-childs (header, content, footer in my case).

I searched the forum and the www and think I found the relevant things.
But when I try to put things together it seems my flex box is ignored. Could you have a short look if I am on the right or totally wrong path?

uid: demo:segmented
props:
  parameterGroups: []
  parameters: []
tags: []
component: f7-block
config:

  style:
    height: 100%
    display: flex
    flex-direction: column
slots:
  default:
    - component: f7-block
      config:

      slots:
        default:
          - component: f7-segmented
            config:
              style:
                flex: 0 0 auto
                background-color: green
              
            slots:
              default:
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: SECTION1
                    actionVariable: selectSection
                    text: One
                    large: true
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: SECTION2
                    actionVariable: selectSection
                    text: Two
                    large: true
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: SECTION3
                    actionVariable: selectSection
                    text: Three
                    large: true
                    
          - component: f7-segmented
            config:
              visible: =!!(vars.selectSection == "SECTION2")
              style: 
                flex: 1 1 auto
                position: relative
                overflow-y: auto
            slots:
              default:
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: DIV1
                    actionVariable: selectDivision
                    text: Division One
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: DIV2
                    actionVariable: selectDivision
                    text: Division Two
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: DIV3
                    actionVariable: selectDivision
                    text: Division Three
          - component: f7-segmented
            config:
              visible: =!!(vars.selectSection == "SECTION3")
            slots:
              default:
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: OTHER1
                    actionVariable: selectOther
                    text: Other One
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: OTHER2
                    actionVariable: selectOther
                    text: Other Two
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: OTHER3
                    actionVariable: selectOther
                    text: Other Three
          - component: oh-toggle-card
            config:
              style:
                flex: 1 1 auto
                position: relative
                overflow-y: auto
          - component: oh-toggle-card    
            config:
              style:
                flex: 1 1 auto
                position: relative
                overflow-y: auto          
          - component: oh-toggle-card  
            config:
              style:
                flex: 1 1 auto
                position: relative
                overflow-y: auto          
          - component: oh-toggle-card  
            config:
              style:
                flex: 1 1 auto
                position: relative
                overflow-y: auto          
          
          
          
          - component: f7-segmented
            config:
              style:
                flex: 0 0 auto
                background-color: yellow
            slots:
              default:
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: THING1
                    actionVariable: selectThing
                    text: Thing One
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: THING2
                    actionVariable: selectThing
                    text: Thing Two
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: THING3
                    actionVariable: selectThing
                    text: Thing Three

And in this context: I found examples where the flex “commands” are put under style
and if found other examples where they are put under class could you explain where the right place is?

Yep, that’s exactly correct. The only caveat is that you have to make sure that the elements you want spaced using the flexbox are the direct children of the flexbox. That seems to be where your first attempt is getting tripped up. For example, your base element is the f7-block with the flex spacing in a column direction. The next element is just a blank f7-block with no flex styling applied. Then you get to the segmented buttons. So the segmented buttons are not being spaced following the rules of the first block because they are direct children of the second block instead. That second block is, at least int he code you’ve put here, completely extraneous. Just get rid of it, and you should start to seem some of the flex spacing in action.

1 Like

Hey,

for sure I am just to stupid…

I tried to decomplicate my example and follow your hints - but to be honest I fear that I did not really understand it.

I now declared every component as a child - but this seems also not to work.

uid: demo:segmented
tags: []
props:
  parameters: []
  parameterGroups: []
timestamp: Aug 19, 2022, 11:28:29 AM
component: f7-block


slots:
  default:
    - component: f7-block
      config:
        style:
          height: 100%
          display: flex
          flex-direction: column
    
      slots:
        default:
          - component: f7-segmented
            config:
              style:
                flex: 0 0 auto
                background-color: green
            slots:
              default:
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: SECTION1
                    actionVariable: selectSection
                    text: One
                    large: true
                    style:
                      flex: 1 1 auto


          - component: f7-segmented
            config:
              visible: =!!(vars.selectSection == "SECTION2")
              style:
                flex: 0 0 auto
                position: relative
                overflow-y: auto
            slots:
              default:
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: DIV1
                    actionVariable: selectDivision
                    text: Division One
                    style:
                      flex: 0 0 auto
                      position: relative
                      overflow-y: auto                    
          - component: f7-segmented
            config:
              visible: =!!(vars.selectSection == "SECTION3")
            slots:
              default:
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: OTHER1
                    actionVariable: selectOther
                    text: Other One
                    style:
                      flex: 0 0 auto
                      position: relative
                      overflow-y: auto 
          - component: oh-toggle-card
            config:
              style:
                flex: 0 0 auto
                position: relative
                overflow-y: auto

          - component: f7-segmented
            config:
              style:
                flex: 0 0 auto
                background-color: yellow
            slots:
              default:
                - component: oh-button
                  config:
                    action: variable
                    actionVariableValue: THING1
                    actionVariable: selectThing
                    text: Thing One
                    style:
                      flex: 1 1 auto
                      position: relative
                      overflow-y: auto