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

i was waiting for the new topic to post widgets based on @Dimitris style, but i’ll post them here.
90% are complete, i’m not a skilled programmer, so i ask to who believe in this project to take these widgets and improve and complete them. please forgive me if in the code there are big mistakes or stupid things, i would like to learn from anyone has better knowns.

image

here is the security widget:

uid: test:Security_V3
tags:
  - MadeByEvil
props:
  parameters:
    - label: Icona
      name: Icon
      required: false
      type: TEXT
    - label: Titolo della card
      name: title
      required: false
      type: TEXT
    - default: Item 1
      label: Item 1 Label
      name: label1
      required: false
      type: TEXT
    - context: item
      label: Item 1
      name: item1
      required: false
      type: TEXT
    - label: Item 2 Label
      name: label2
      required: false
      type: TEXT
    - context: item
      label: Item 2
      name: item2
      required: false
      type: TEXT
    - label: Item 3 Label
      name: label3
      required: false
      type: TEXT
    - context: item
      label: Item 3
      name: item3
      required: false
      type: TEXT
    - label: Item 4 Label
      name: label4
      required: false
      type: TEXT
    - context: item
      label: Item 4
      name: item4
      required: false
      type: TEXT
    - label: Item 5 Label
      name: label5
      required: false
      type: TEXT
    - context: item
      label: Item 5
      name: item5
      required: false
      type: TEXT
  parameterGroups:
    - name: widgetAction
      context: action
      label: Action
      description: Action to perform when the element is clicked
timestamp: Aug 25, 2022, 6:46:53 PM
component: f7-card
config:
  style:
    border-radius: 10px
    height: 320px
    min-height: 320px
slots:
  content:
    - component: oh-icon
      config:
        icon: iconify:bi:shield-check
        color: green
        height: 20px
        style:
          position: absolute
    - component: Label
      config:
        text: Contacts & Alarm
        top: 13px
        style:
          position: absolute
          left: 35%
          font-weight: 500
    - component: oh-icon
      config:
        icon: '=items[props.item1].state == "ON" ? "iconify:akar-icons:square-fill" : "iconify:akar-icons:square"'
        color: green
        height: 10px
        style:
          position: absolute
          top: 40px
          left: 95px
    - component: oh-icon
      config:
        icon: '=items[props.item2].state == "ON" ? "iconify:akar-icons:square-fill" : "iconify:akar-icons:square"'
        color: green
        height: 10px
        style:
          position: absolute
          top: 40px
          left: 115px
    - component: oh-icon
      config:
        icon: '=items[props.item3].state == "ON" ? "iconify:akar-icons:square-fill" : "iconify:akar-icons:square"'
        color: green
        height: 10px
        style:
          position: absolute
          top: 40px
          left: 135px
    - component: oh-icon
      config:
        icon: '=items[props.item4].state == "ON" ? "iconify:akar-icons:square-fill" : "iconify:akar-icons:square"'
        color: green
        height: 10px
        style:
          position: absolute
          top: 40px
          left: 155px
    - component: oh-icon
      config:
        icon: '=items[props.item5].state == "ON" ? "iconify:akar-icons:square-fill" : "iconify:akar-icons:square"'
        color: green
        height: 10px
        style:
          position: absolute
          top: 40px
          left: 175px
    - component: oh-icon
      config:
        icon: '=items[props.item6].state == "ON" ? "iconify:akar-icons:square-fill" : "iconify:akar-icons:square"'
        color: green
        height: 10px
        style:
          position: absolute
          top: 40px
          left: 195px
    - component: oh-icon
      config:
        icon: '=items[props.item7].state == "ON" ? "iconify:akar-icons:square-fill" : "iconify:akar-icons:square"'
        color: green
        height: 10px
        style:
          position: absolute
          top: 40px
          left: 215px
    - component: oh-icon
      config:
        icon: '=items[props.item8].state == "ON" ? "iconify:akar-icons:square-fill" : "iconify:akar-icons:square"'
        color: green
        height: 10px
        style:
          position: absolute
          top: 40px
          left: 235px
    - component: oh-icon
      config:
        icon: iconify:bi:shield-check
        color: '=items[props.item5].state == "ON" ? "green" : items[props.item4].state == "ON" ? "green" : items[props.item3].state == "ON" ? "green" : items[props.item2].state == "ON" ? "green" : items[props.item1].state == "ON" ? "green" : "grey"'
        height: 50px
        style:
          position: absolute
          top: 70px
          left: 4%
    - component: Label
      config:
        text: Stay
        style:
          position: absolute
          top: 125px
          left: 7%
          font-weight: 500
    - component: oh-icon
      config:
        icon: iconify:bi:shield-check
        color: grey
        height: 50px
        style:
          position: absolute
          top: 70px
          left: 43%
    - component: Label
      config:
        text: Off
        style:
          position: absolute
          top: 125px
          left: 48%
          font-weight: 500
    - component: oh-icon
      config:
        icon: iconify:bi:shield-check
        color: '=items[props.item5].state == "OFF" && items[props.item4].state == "OFF" && items[props.item3].state == "OFF" && items[props.item2].state == "OFF" && items[props.item1].state == "OFF" ? "red" : "grey"'
        height: 50px
        style:
          position: absolute
          top: 70px
          left: 81%
    - component: Label
      config:
        text: Arm
        style:
          position: absolute
          top: 125px
          left: 85%
          font-weight: 500
    - component: Label
      config:
        text: =props.label1
        style:
          position: absolute
          top: 180px
          left: 6%
    - component: oh-toggle
      config:
        item: =props.item1
        color: green
        style:
          --f7-toggle-height: 20px
          --f7-toggle-width: 40px
          font-size: 100%
          top: 160px
          left: 250px
    - component: Label
      config:
        visible: '=props.item2 ? "true" : "false"'
        text: =props.label2
        style:
          position: absolute
          top: 205px
          left: 6%
    - component: oh-toggle
      config:
        visible: '=props.item2 ? "true" : "false"'
        item: =props.item2
        color: green
        style:
          --f7-toggle-height: 20px
          --f7-toggle-width: 40px
          top: 185px
          left: 210px
    - component: Label
      config:
        visible: '=props.item3 ? "true" : "false"'
        text: =props.label3
        style:
          position: absolute
          top: 230px
          left: 6%
    - component: oh-toggle
      config:
        visible: '=props.item3 ? "true" : "false"'
        item: =props.item3
        color: green
        style:
          --f7-toggle-height: 20px
          --f7-toggle-width: 40px
          top: 210px
          left: 170px
    - component: Label
      config:
        visible: '=props.item4 ? "true" : "false"'
        text: =props.label4
        style:
          position: absolute
          top: 255px
          left: 6%
    - component: oh-toggle
      config:
        visible: '=props.item4 ? "true" : "false"'
        item: =props.item4
        color: green
        style:
          --f7-toggle-height: 20px
          --f7-toggle-width: 40px
          top: 235px
          left: 130px
    - component: Label
      config:
        visible: '=props.item5 ? "true" : "false"'
        text: =props.label5
        style:
          position: absolute
          top: 280px
          left: 6%
    - component: oh-toggle
      config:
        visible: '=props.item5 ? "true" : "false"'
        item: =props.item5
        color: green
        style:
          --f7-toggle-height: 20px
          --f7-toggle-width: 40px
          top: 260px
          left: 90px
  footer:
    - component: f7-block
      config:
        style:
          top: 125px
          left: 0
          position: absolute

image

and here the rollershutter widget:

uid: Rollershutter Preset
tags:
  - New UI
props:
  parameters:
    - description: Title of the card
      label: Title
      name: Title
      required: false
      type: TEXT
    - context: item
      description: Das Item mit dem die Solltemperatur eingestellt wird
      label: Rollershutter Item
      name: RollerItem
      required: true
      type: TEXT
timestamp: Aug 22, 2022, 11:41:00 PM
component: f7-card
config:
  noShadow: false
  padding: false
  style:
    padding: 0px
    border-radius: 10px
    box-shadow: 5px 5px 15px 1px rgba(0,0,0,0.05)
    --f7-card-header-border-color: transparent
    min-width: 90%
slots:
  default:
    - component: f7-card-header
      config:
        style:
          --f7-card-header-border-color: none
      slots:
        default:
          - component: oh-icon
            config:
              icon: iconify:mdi:window-shutter-alert
              height: 30px
              style:
                margin-top: 3%
                margin-bottom: 3%
    - component: Label
      config:
        text: =props.Title
        style:
          position: absolute
          top: 8%
          left: 17%
    - component: Label
      config:
        text: =items[props.RollerItem].state + "% " + " close"
        style:
          position: absolute
          top: 30%
          left: 17%
          background: gray
          border-radius: 9px
    - component: oh-button
      config:
        iconColor: teal
        iconF7: arrow_up_circle
        iconSize: 30
        action: command
        actionItem: =props.RollerItem
        actionCommand: UP
        style:
          position: absolute
          top: 15%
          right: 20%
          height: 33px
          background: transparent
          z-index: 98
    - component: oh-button
      config:
        iconColor: gray
        iconF7: stop_circle
        iconSize: 30
        action: command
        actionItem: =props.RollerItem
        actionCommand: STOP
        style:
          position: absolute
          top: 15%
          right: 10%
          height: 33px
          background: transparent
          z-index: 98
    - component: oh-button
      config:
        iconColor: teal
        iconF7: arrow_down_circle
        iconSize: 30
        action: command
        actionItem: =props.RollerItem
        actionCommand: DOWN
        style:
          position: absolute
          top: 15%
          right: 0%
          height: 33px
          background: transparent
          z-index: 98
    - component: f7-card-footer
      config:
        style:
          height: 60px
          background: "#B1ACA2"
          border-radius: 0 0 10px 10px
      slots:
        default:
          - component: Label
            config:
              text: "Preset positions:"
              style:
                position: absolute
                top: 5%
                color: white
    - component: oh-button
      config:
        round: true
        text: "0"
        action: command
        actionItem: =(props.RollerItem)
        actionCommand: UP
        class:
          - margin
          - display-flex
          - flex-direction-column
        style:
          position: absolute
          height: 24px
          width: 10px
          top: 63%
          left: 20%
          z-index: 98
          background: '=items[props.RollerItem].state == "0" ? "teal" : "gray"'
          color: white
    - component: oh-button
      config:
        round: true
        text: 50
        action: command
        actionItem: =(props.RollerItem)
        actionCommand: 50
        class:
          - margin
          - display-flex
          - flex-direction-column
        style:
          position: absolute
          height: 24px
          width: 10px
          top: 63%
          left: 33%
          z-index: 98
          background: '=items[props.RollerItem].state == "50" ? "teal" : "gray"'
          color: white
    - component: oh-button
      config:
        round: true
        text: 75
        action: command
        actionItem: =(props.RollerItem)
        actionCommand: 75
        class:
          - margin
          - display-flex
          - flex-direction-column
        style:
          position: absolute
          height: 24px
          width: 10px
          top: 63%
          right: 33%
          z-index: 98
          background: '=items[props.RollerItem].state == "75" ? "teal" : "gray"'
          color: white
    - component: oh-button
      config:
        round: true
        text: 100
        action: command
        actionItem: =(props.RollerItem)
        actionCommand: DOWN
        class:
          - margin
          - display-flex
          - flex-direction-column
        style:
          position: absolute
          height: 24px
          width: 10px
          top: 63%
          right: 20%
          z-index: 98
          background: '=items[props.RollerItem].state == "100" ? "teal" : "gray"'
          color: white

both are missing the function “expandable” and the “hours bars” details of when the item have been modified.
width and weight are perfect for iphone layout.
hope someone can help me/us adding functions and better screen adapt.

bye!

1 Like

Hey Master J.
Do we have some info re how the OH render/recolor mechanism works? SVG contains colored vectors . If ,for example OH can recolor only Black and White or Grey scale vectors, then the SVG must designed in that Color Space to be capable of recoloring.
Can someone try to re-color attached svg’s and post the results?

securityGreyscale
security Colored
securityB&Wsolidsvg

I do not believe that OH recolors svgs at any point. The included “classic” icons are in svg format, but do not ever get recolored. The f7 icons are included in the page as a font, if I recall, so recoloring those is as simple as changing the appropriate css color property. I have never looked into the material icons, so I don’t know how those work, but my guess would be that they are similar to the f7 icons.

1 Like

Hey @JustinG,

I think the first option (with an array) is a good starting point.

Is there a part in the docu of how to use arrays (how to iterate over the elements, how to read which position the pointer has, etc.) for widget development?

And is there also a part where looping / repeat is explained / documented? In my mind I think I have found an approach to integrate the “forward” and “back” buttons - but I need to get deeper in the technical things…

Within widgets, the only available option for looping is the oh-repeater.

My gut feeling is that to allow the most configurable widget, you want users to be able to choose which locations appear or don’t appear. To do that I would recommend that you come up with an item tag that’s distinct to your widget configuration and apply that tag to any locations that are going to be in the widget, then you can use the itemsWithTags source for the repeater to fetch those locations.

To restrict the repeater’s output to only 3 of those at a time, you’ll need to use the fitler property of the repeater and the index variable that gets created. I don’t think the index variable is documented yet, but if you search the forums for “repeater idx”, you should see some good examples of the index variable in use.

I was correct. The svgs loaded as resources will not be included in the scope of the page’s css (learn something new every day).

There is, however, a fun workaround using svg sprites. It’ll be instructive and interesting for more people than just the readers of this thread, so I’ll post it as new thread in the next couple of days when I get a chance.

Edit: Here’s a link to the svg icon method.

1 Like

Are you hiding from me Mr OH Developer? :smiling_imp:
Hue_overview.pdf (95.6 KB)
I’m joking: This is just to share some latest (graphic) experiments. Ahh, and to push a liiiittle bit the navbar sub-project!

With color bars - dizzy layout, but with better “what-if” understanding.

Hue_overviewv1.pdf (45.7 KB)

Sorry, last days have been turbulent.

I still work on the navbar and make efforts - but this week i am Limited a Little bit with time…

Hey @JustinG ,

I am testing different approaches.

With this code I have the problem, that the flex-row option is ignored.
Could you have - once again - a look, what’s going wrong here:

uid: test_Arr
tags: []
props: {}
timestamp: Aug 29, 2022, 11:11:07 PM
component: f7-card
config: {}
slots:
  content:
    - component: f7-segmented
      config:
        style:
          display: flex
          flex-direction: row
      slots:
        default:
          - component: oh-repeater
            config:
              for: rooms
              sourceType: array
              in:
                - name: Room1
                - name: Room2
                - name: Room3
                - name: Room4
                - name: Room5
              map: loop.rooms.name

            slots:
              default:
                - component: oh-button
                  config:
                    action: variable
                    actionVariable: selectSection
                    actionVariableValue: SECTION1
                    large: true
                    style:
                      color: '=vars.selectSection=="SECTION1" ? "black" : "#8C8C8C"'
                      font-size: 30px
                      font-weight: 200
                      text-decoration: underline
                      text-decoration-color: '=vars.selectSection=="SECTION1" ? "#F8BB00" : "transparent"'
                      text-underline-offset: 4px
                    text: =loop.rooms


You need to add fragment: true to the repeater. In it’s normal setting, the repeater wraps all of the elements it produces in a div element container. So your f7-segmented is flex spacing correctly, it’s just spacing one div element (which just happens to contains lots of buttons that the segmented doesn’t care about at all). When you add the fragment property to the repeater it strips that extra container from around the repeated elements and makes them direct children of the repeater’s parent element.

@JustinG

… Oh my dear… I still have a lot to learn - but to be honest it’s a cool thing to learn all this stuff.

Just another question (please tell me when to stop asking questions to you…):

How could I determine where the oh-repeater should start and where it should end - or in other words could I use an index for iterating over the elements?.
let me say it in words:

I have for example 8 Rooms in an array.

The oh-repeater should only show me the first 3 Rooms (index 1 to 3)
I click on “>” and the oh-repeater now should show me show 3 Rooms with the index 2 to 4.
How could I tell the repeater where to start, where to end and how could I know where the index stands?

I hope I described it well.

here my first attempt in code - simplified to just decrease the variable of the Range:

uid: ButtonGridd2
tags: []
props: {}
timestamp: Aug 30, 2022, 9:44:07 AM
component: f7-card
config: {}
slots:
  content:
    - component: f7-segmented
      config:
        style:
          display: flex
          flex-direction: row
      slots:
        default:
          - component: oh-repeater
            config:
              fragment: true
              for: rooms
              rangeStart: 1
              rangeStop: =vars.rstp
              sourceType: array
              in:
                - name: Room1
                - name: Room2
                - name: Room3
                - name: Room4
                - name: Room5

              map: loop.rooms.name
            slots:
              default:
                - component: oh-button
                  config:
                    action: variable
                    actionVariable: selectSection
                    actionVariableValue: =loop.rooms
                    large: true
                    style:
                      color: '=vars.selectSection=="SECTION1" ? "black" : "#8C8C8C"'
                    text: =loop.rooms
    - component: Label
      config:
        style:
          flex: 1 1 auto
          overflow-y: auto
          position: relative
        text: =vars.selectSection 

    - component: Label
      config:
        style:
          flex: 1 1 auto
          overflow-y: auto
          position: relative
        text: =vars.rstp 
        
    - component: oh-button
      config:
        text: ">"
        action: variable
        actionVariable: rstp
        actionVariableValue: =vars.rstp + 1
 

Hey Pal. Is there any chance to create a “Dynamic Index”? In the original design the displayed rooms may vary from 1 to 3 (or more if they can fit!) depending on the width of the text -see attachment. I don’t want to add more complexity, but i think this is decision time for the navbar!

See Allign_v1.pdf (71.4 KB) or

No worries, just be sure, as you learn more and more, to pass on what you know to others.

The repeater not only gives you a single element of its array at a time (loop.whatever) it also creates two other variables you can use in the scope of the repeater: loop.whatever_idx and loop.whatever_source. Whereas the regular variable is just the current element of the overall array, the _idx variable is the index of that current element and the _source variable is the entire array that the repeater is working form.

The filter property filters the entire array down to a subset based on whatever expression (without the = in front) that you give it. So here you want the subset of the original array where the index is equal to some position variable or that position plus 1 or 2. Your filter statement would look something like this:

filter: (loop.whatever_idx >= vars.buttonIndex) && (loop.whatever_idx <= (vars.buttonIndex +2))

As far as I know, there is really only one reliable way to do this with css (I’d be interested to hear if other’s know of a different way). You have to keep the container from expanding vertically (i.e., set a fixed height), let it use block spacing for the child elements and set it to hide overflow elements. Then you have to set the child elements to float: left. Here’s a quick example. If you put this in your editor and adjust the window width you should see the right-hand buttons disappear as they run out of room.

uid: demo:hide_buttons
props:
  parameterGroups: []
  parameters: []
tags: []
component: f7-block
config:
slots:
  default:
    - component: f7-row
      config:
        style:
          overflow: hidden
          display: block
          height: 28px
      slots:
        default:
          - component: oh-button
            config:
              text: Very very very long button here
              outline: true
              style:
                float: left
                width: auto
          - component: oh-button
            config:
              text: Short button
              outline: true
              style:
                float: left
                width: auto
          - component: oh-button
            config:
              text: Regular button here
              outline: true
              style:
                float: left
                width: auto

There’s actually an advantage to this setup: the filter property for the repeater becomes easier because you just have to rule out buttons before the position variable and then let the css do the rest of figuring out how many buttons are visible.

1 Like

Definitively, this is the most beautiful reply that i have read here. The definition of the Community. :raised_hands:

2 Likes

that’s cool - but for our use case the overflow should scroll horizontal.
But if I set overflow: scroll it scrolls vertical. Could we get rid of this?

Until we manage this I will go further with the array approach…

That sound like the thing I am looking for. But if I want so show for example the length of source I get an TypeError: undefined is not an object (evaluating 'n[e.property.name]') .

Did I understand it wrong?

uid: ButtonGridd2
tags: []
props: {}
timestamp: Aug 30, 2022, 9:44:16 AM
component: f7-card
config: {}
slots:
  content:
    - component: f7-segmented
      config:
        style:
          display: flex
          flex-direction: row
      slots:
        default:
          - component: oh-repeater
            config:
              fragment: true
              for: rooms
              sourceType: array
              in:
                - name: Room1
                - name: Room2
                - name: Room3
                - name: Room4
                - name: Room5
              map: loop.rooms.name
            slots:
              default:
                - component: oh-button
                  config:
                    action: variable
                    actionVariable: selectSection
                    actionVariableValue: =loop.rooms_source.length
                    large: true
                    style:
                      color: '=vars.selectSection=="SECTION1" ? "black" : "#8C8C8C"'
                    text: =loop.rooms
    - component: Label
      config:
        style:
          flex: 1 1 auto
          overflow-y: auto
          position: relative
        text: =loop.rooms_source.length
       # text: =loop.rooms_source.length

Hey @Nic0205 I need to enlighten me on this:

While I’m trying to add

component: f7-navbar

in an OH Page, the OH draws a Navbar. Maybe it thinks that i’m just another Mac User and reacts accordingly :rofl:

My thought is, -if this works and you can configure it- maybe it’s an alternative for what we are trying to do. PLS don’t spend more than of 2-5 minutes on this, I just wanted to show to @JustinG that I’m reading (…not studying) code!!!

Overflow is the general property. There are also specific versions to separately control the different directions: overflow-x, and overflow-y.

loop.rooms_source.length does, indeed, give you the length of the array that the repeater is using. However, that variable is only scoped for the repeater and it’s children; it can’t be accessed by any elements at the same level as or higher than the repeater.

Sure - but neither overflow-x nor overflow-y does a horizontal scrolling … :wink:

Hi,

I several times tried to use f7-navbar - but the only thing I figured out is how to define the title:

component: f7-navbar
config:
  title: Test

Using link: ABC or icon: f7-zzz did not show up…