ButtonGrid / replacement for slider by comma-separated preset buttons

After two weeks of reading lots of questions and answers here and some external documents I started my own widget “ButtonGrid” which is almost complete but need some fine-tuning from you experts out there :slight_smile:

Reason for this widget:

  • Columns and cards from the standard OH library have a spacing which prevents you from saving space on your screen
  • This widget replaces sliders by a set of comma separated buttons (I do not like sliders)

Usage:
Item name, item’s display name, preset values and their display names are provided by a semicolon-/comma-separated syntax very similiar to md€appings in .sitemap.
Example:

Livingroom_Couch_Light=Couch;0=Off,40=40%,75=75%,100=On

This widget automatically creates a label card for items and buttons for commands. If you want more items just add another line.

This screenshot is obviously not the final result. I have changed some color settings especially for this post so that my questions can be better understood:

My questions are:

Button Background expression:
the background of each button is dependent from its item state. the item is represented by the following expression:

props['row_' + loop.i.toString()].split(';')[0].split('=')[0]]

this is working fine. however, the following if-then-else expression is incorrect:

"=(items[props['row_' + loop.i.toString()].split(';')[0].split('=')[0]].state === '40') ? '#222222' : 'white'"

where is the error?

F7 background

  • syntax background: '#222222' is only accepted from oh-containers. f7-containers expect a color string like red, green, etc. Is there a specific syntax for this?
    even background: rgb(128,128,128) does also not work.
    [solved: --f7-card-bg-color: '#556677']

First column

  • where does the extra spacing around oh-label-card come from? How can I avoid this?
    [solved: --f7-card-margin-vertical: 0px]
  • text-align: left does not work even placed in an f7-card containing an oh-label-card

Buttons (2nd column and more)

  • How can I control mouse over and button-pressed color code?. again no success in putting this into an f7-container
    [solved: -f7-button-hover-bg-color: green, --f7-button-pressed-bg-color: yellow]
  • How can I set the height to fit into the column height? height: 100% is not working
  • When (temporarily) forcing the height to be e.g. 50px. the button label is aligned top. how can I set it to be in the middle? vertical-align: baseline does not work.

Grid
I have read the f7 documentation but could not find any information about how to set colors of border lines. any idea here?
[solved: in my case this workaround did the trick
--f7-button-border-radius: 0px
--f7-button-border-color: gray
--f7-button-border-width: 1px]

Calculation of non-empty rows in the properties section
the total number of button rows needs to provided by a property (props.iRows). any idea how I can calculate this by the amount of props.row_ which are not empty?

Code

Here is the YAML code:

uid: ButtonGrid
tags: []
props:
  parameters:
    - description: Syntax ItemID=itemLabel;cmdValues=cmdLabels (comma separated); Example Livingroom_Couch_Light=Couch;0=Off,40=40%,75=75%,100=On
      label: Row 1
      name: row_1
      required: false
      type: TEXT
      groupName: gRows
    - label: Row 2
      name: row_2
      required: false
      type: TEXT
      groupName: gRows
    - label: Row 3
      name: row_3
      required: false
      type: TEXT
      groupName: gRows
    - label: Row 4
      name: row_4
      required: false
      type: TEXT
      groupName: gRows
    - label: Total number of Rows
      name: iRows
      required: false
      groupName: gGeneral
    - label: Icon Name (oh or f7)
      name: iconName
      required: false
      type: TEXT
      groupName: gGeneral
    - label: Highlight Color Code
      name: highlightColor
      required: false
      type: TEXT
      groupName: gGeneral
  parameterGroups:
    - name: gGeneral
      label: General settings
    - name: gRows
      label: Define Items (each Item in a separate Row)
timestamp: Jan 18, 2021, 2:34:24 PM
component: f7-card
config:
  containerStyle:
    width: 100%
    height: 100%
    position: absolute
    top: 0px
    left: 0px
  style:
    font-size: 1em
    background: gray
  class:
    - no-padding
    - no-margin
slots:
  default:
    - component: oh-repeater
      config:
        fragment: true
        sourceType: range
        for: i
        rangeStart: 1
        rangeStop: "=(props.iRows == NULL) ? 1 : Number.parseInt(props.iRows)"
      slots:
        default:
          - component: f7-row
            config:
              noGap: true
            slots:
              default:
                - component: f7-col
                  slots:
                    default:
                      - component: oh-label-card
                        config:
                          label: =props['row_' + loop.i.toString()].split(";")[0].split("=")[1]
                          icon: oh:slider
                          item: =props['row_' + loop.i.toString()].split(";")[0].split("=")[0]
                          iconUseState: true
                          outline: true
                          background: "#111111"
                          text-color: white
                          fontSize: 12px
                          style:
                            text-align: left
                - component: oh-repeater
                  config:
                    fragment: true
                    sourceType: array
                    for: lbl
                    in: =props['row_' + loop.i.toString()].split(";")[1].split(",")
                  slots:
                    default:
                      - component: f7-col
                        slots:
                          default:
                            - component: oh-button
                              config:
                                text: =loop.lbl.split("=")[1]
                                outline: true
                                text-color: white
                                border-color: black
                                raised: false
                                large: true
                                action: command
                                actionItem: =props['row_' + loop.i.toString()].split(";")[0].split("=")[0]
                                actionCommand: =loop.lbl.split("=")[0]
                                accordionList: true
                                background: "=(items[props['row_' + loop.i.toString()].split(';')[0].split('=')[0]].state === '40') ? '#222222' : 'white'"
                                style:
                                  vertical-align: baseline
                                  height: 50px

Hi @Oliver2 ,

Late last night I was working on something virtually identical to this myself, but I was struggling to get my version of the split statement to work!

I’ve avoided using # color definitions except when they are safely inside a property. # is YAML’s comment character and so weird behaviour is possible if your not careful. Guess how I know :sob:

The following is useful for setting colours generally. It takes any valid colour setting method RGB/RGBA/HEX/HSL) as well as CSS styles, also sets a default is not specified. The use of the var() is the trick for applying named styles.

background-color: "=(props.backgroundColor === undefined ? 'var(--f7-color-white)' : (props.backgroundColor.substring(0,2) === '--' ? 'var(' + props.backgroundColor + ')' : props.backgroundColor))"

Setting the class -button-active is also specifically intended to highlight the specific button in an f7 segmented group of buttons. I was trying to work out how to conditionally set a class when the value matched the assigned button value, but I couldn’t work that out either. Possibly something wrong with my split statement.

The class - button-fill color-red is the other way to change button colour, but only seems to work with the named colours in the example.

You can change the oh-button background and border lines overriding the inherited f7 styles:

--f7-button-bg-color: red
--f7-button-border-color: green
--f7-button-border-width: 1px

The following is how you specify the hover and pressed highlights:

--f7-button-hover-bg-color: var(--f7-color-white-shade)
--f7-button-pressed-bg-color: var(--f7-color-white-shade)

I don’t think it is possible to set border lines on your grid rows/columns. Rather you add borders to the objects within the grid.

You used a second oh-card to hold you label data. Consider just using a Label object.

Here is my version of what you were trying to do. Search for the commented (#) lines which show the impact of some of the settings above.

NOTE: this is work in progress and it DOES NOT WORK yet(!), but may help see how someone else has done things… That’s what I’m going to do with your code now :stuck_out_tongue_winking_eye:

uid: list_radio_buttons
tags: []
props:
  parameters:
    - context: item
      description: Item that stores the required data linked to the radio buttons.
      label: Item Name
      name: item1
      required: false
      type: TEXT
      groupName: items
    - description: Label to display on the list.
      label: Label
      name: label
      required: false
      type: TEXT
      groupName: general
    - description: Number of option buttons to display i.e. one per possible value e.g. 3.
      label: Number of buttons
      name: buttNumber
      required: true
      type: TEXT
      groupName: general
    - description: Comma seperated list of labels to be dispalyed on each button e.g. Home,Away,Alarm Active.
      label: Button Labels
      name: buttLabels
      required: false
      type: TEXT
      groupName: general
    - description: Comma seperated list of icons to be dispalyed on each button e.g. f7:sun_max,f7:moon,f7:alarm.
      label: Button Icons
      name: buttIcons
      required: false
      type: TEXT
      groupName: general
    - description: Comma separated list of values associated with each button e.g. 1,2,3. These are the values retreived from and written to the selected item.
      label: Button / Item values
      name: buttValues
      required: true
      type: TEXT
      groupName: general
    - description: Background image URL e.g. http://10.1.0.1/static/fibaromotion.png
      label: Background image URL
      name: backgroundUrl
      required: false
      type: TEXT
      groupName: general
    - description: Intensity of the background-blur (0 - 10)
      label: Background image blur
      name: backgroundBlur
      required: false
      type: TEXT
      groupName: general
    - context: color
      description: Specify the background color for card. Any valid <a class="link external" href="https://www.w3schools.com/css/css_colors.asp" target="_blank">HTML color format (RGB/RGBA/HEX/HSL)</a> or <a class="link external" href="https://framework7.io/docs/css-variables.html" target="_blank">CSS theme</a> color. e.g. rgb(255,255,255), --f7-color-white. <b>Default = --f7-color-white</b>
      label: Background color
      name: backgroundColor
      required: false
      type: TEXT
      groupName: general
    - description: Specify the foreground color for text and icons. Any valid <a class="link external" href="https://www.w3schools.com/css/css_colors.asp" target="_blank">HTML color format (RGB/RGBA/HEX/HSL)</a> or <a class="link external" href="https://framework7.io/docs/css-variables.html" target="_blank">CSS theme</a> color. e.g. rgb(255,255,255), --f7-color-black. <b>Default = --f7-color-black</b>
      label: Foreground color (normal)
      name: foregroundColor
      required: false
      type: TEXT
      groupName: general
    - description: Specify the foreground color for highlighted text / icons (when warning condition is true). Any valid <a class="link external" href="https://www.w3schools.com/css/css_colors.asp" target="_blank">HTML color format (RGB/RGBA/HEX/HSL)</a> or <a class="link external" href="https://framework7.io/docs/css-variables.html" target="_blank">CSS theme</a> color. e.g. rgb(255,255,255), --f7-color-red. <b>Default = --f7-theme-color</b>
      label: Foreground color (warning)
      name: foregroundColorWarn
      required: false
      type: TEXT
      groupName: general
  parameterGroups:
    - name: items
      label: Items
    - name: general
      label: Display options
timestamp: Jan 18, 2021, 8:18:41 PM
component: f7-card
config:
  title: =props.title
  class:
    #- padding
  style:
    background-image: ='url(' + props.backgroundUrl + ')'
    backdrop-filter: ='blur(' + props.backgroundBlur + 'px)'
    background-size: contain
    background-repeat: no-repeat
    background-position: 100% 100%
    background-color: "=(props.backgroundColor === undefined ? 'var(--f7-color-white)' : (props.backgroundColor.substring(0,2) === '--' ? 'var(' + props.backgroundColor + ')' : props.backgroundColor))"
    #border-radius: 20px
    overflow: hidden
    -ms-user-select: none
    -moz-user-select: none
    -webkit-user-select: none
    --f7-card-content-padding-horizontal: 40px
    --f7-card-content-padding-vertical: 40px
    user-select: none
    --normal-txt-color: "=(props.foregroundColor === undefined ? 'var(--f7-color-black)' : (props.foregroundColor.substring(0,2) === '--' ? 'var(' + props.foregroundColor + ')' : props.foregroundColor))"
    --warn-txt-color: "=(props.foregroundColorWarn === undefined ? 'var(--f7-theme-color)' : (props.foregroundColorWarn.substring(0,2) === '--' ? 'var(' + props.foregroundColorWarn + ')' : props.foregroundColorWarn))"
slots:
  default:
    - component: f7-block
      config:
        class:
          - no-padding
          - no-margin
        style:
          width: 100%
          height: 100%
          position: absolute
          top: 0
          left: 0
    - component: f7-row
      slots:
        default:
          - component: Label
            config:
              class:
                - align-content-flex-start
              text: =props.label
              style:
                font-size: 14px
                font-weight: 400
                color: '=(items[props.item1].state === props.warn1) ? "var(--warn-txt-color)" : "var(--normal-txt-color)"'
          - component: oh-repeater
            config:
              sourceType: range
              for: iLoop
              rangeStart: 1
              rangeStop: "=(props.buttNumber === undefined) ? 0 : Number(props.buttNumber)"
              fragment: true
            slots:
              default:
                - component: oh-button
                  config:
                    action: analyzer
                    actionAnalyzerItems: =[props.item1
                    class:
                      - align-content-flex-end
                      - justify-content-left
                      - align-items-right
                      - segmented-raised
                    style:
                      align: right
                      height: 100%
                      --f7-button-hover-bg-color: var(--f7-color-white-shade)
                      --f7-button-pressed-bg-color: var(--f7-color-white-shade)
                      #--f7-button-bg-color: red
                      #--f7-button-border-color: green
                      #--f7-button-border-width: 10px
                  slots:
                    default:
                      - component: f7-icon
                        config:
                          f7: sun_max
                          size: 14
                          style:
                            color: '=(items[props.item1].state === props.warn1) ? "var(--warn-txt-color)" : "var(--normal-txt-color)"'
                      - component: Label
                        config:
                          text: =loop.iLoop_idx
                          style:
                            font-size: 14px
                            color: '=(items[props.item1].state === props.warn1) ? "var(--warn-txt-color)" : "var(--normal-txt-color)"'

Hope some of this helps. It took me a while to realise that the oh versions of objects where inheriting the styles of the F7 equivalent.

Andy

Hi Andy,
thanks for your answer. This brings my a step further. Good to know that I am not alone with my weird idea :wink:

it happens to me that after MainUi did not respond anymore to any changes all my #-comments were gone - deleted…

probably that was happening when I tested the background color. In the end --f7-card-bg-color: '#556677' is working fine now.

hmm. the intention is to make use of iconUseState: true which reflects the item state to the icon. does a label object also provide this feature?

funny that this is working for you. I have to use ‘text-align: right’.
however, this does not work for oh-label-card

except of the following the layout stuff is done:
how can I get rid of the space at the top and bottom of the label-card (first column) and how to set the button height to 100%? any idea?

ok, another problem solved:
--f7-card-margin-vertical: 0px sets the spacing for oh-label-card

for those who might have a similiar requirement for a button grid, this is how it looks like, finally

and here is the yaml code:

uid: ButtonGrid
tags: []
props:
  parameters:
    - description: Syntax ItemID=itemLabel;cmdValues=cmdLabels (comma separated); Example Livingroom_Couch_Light=Couch;0=Off,40=40%,75=75%,100=On or Livingroom_Window1_Rollershutter;0.0=△,85.0=85%,DOWN=▽,STOP=✕
      label: Row 1
      name: row_1
      required: false
      type: TEXT
      groupName: gRows
    - label: Row 2
      name: row_2
      required: false
      type: TEXT
      groupName: gRows
    - label: Row 3
      name: row_3
      required: false
      type: TEXT
      groupName: gRows
    - label: Row 4
      name: row_4
      required: false
      type: TEXT
      groupName: gRows
    - label: Total number of Rows
      name: iRows
      required: false
      groupName: gGeneral
    - description: oh:slider, oh:rollershutter
      label: Icon Name
      name: iconName
      required: false
      type: TEXT
      groupName: gGeneral
    - description: e.g. "#0db9f0"
      label: Active label color
      name: lblColor
      required: false
      type: TEXT
      groupName: gGeneral
  parameterGroups:
    - name: gGeneral
      label: General settings
    - name: gRows
      label: Define Items (each Item in a separate Row)
timestamp: Jan 21, 2021, 10:35:26 PM
component: f7-card
config:
  textColor: gray
  style:
    --f7-card-margin-vertical: 0px
    --f7-card-margin-horizontal: 0px
    --f7-card-content-padding-horizontal: 0px
    --f7-card-content-padding-vertical: 0px
    --f7-card-border-radius: 0px
    --f7-card-outline-border-color: "#333333"
slots:
  default:
    - component: f7-card-content
      slots:
        default:
          - component: oh-repeater
            config:
              fragment: true
              sourceType: range
              for: i
              rangeStart: 1
              rangeStop: "=(props.iRows == NULL) ? 1 : Number.parseInt(props.iRows)"
            slots:
              default:
                - component: f7-row
                  config:
                    noGap: true
                    style:
                      align-items: stretch
                  slots:
                    default:
                      - component: f7-col
                        config:
                          style:
                            width: 25%
                        slots:
                          default:
                            - component: oh-label-card
                              config:
                                label: '=(props.row_1 == NULL) ? "" : props["row_" + loop.i.toString()].split(";")[0].split("=")[1]'
                                icon: =props.iconName
                                item: =props["row_" + loop.i.toString()].split(";")[0].split("=")[0]
                                iconUseState: true
                                outline: true
                                fontSize: 1.3em
                      - component: oh-repeater
                        config:
                          fragment: true
                          sourceType: array
                          for: lbl
                          in: =props["row_" + loop.i.toString()].split(";")[1].split(",")
                        slots:
                          default:
                            - component: f7-col
                              config:
                                style:
                                  flex: 1
                              slots:
                                default:
                                  - component: oh-button
                                    config:
                                      text: =loop.lbl.split("=")[1]
                                      action: command
                                      actionItem: =props["row_" + loop.i.toString()].split(";")[0].split("=")[0]
                                      actionCommand: =loop.lbl.split("=")[0]
                                      outline: true
                                      style:
                                        --f7-button-text-color: '=(items[props["row_" + loop.i.toString()].split(";")[0].split("=")[0]].state == loop.lbl.split("=")[0]) ? props.lblColor : "#EEEEEE"'
                                        --f7-button-font-size: 1.3em
                                        --f7-button-height: 100%
                                        --f7-button-bg-color: '=(items[props["row_" + loop.i.toString()].split(";")[0].split("=")[0]].state == loop.lbl.split("=")[0]) ? "#222222" : "#111111"'
                                        --f7-button-hover-bg-color: "#222222"
                                        --f7-button-pressed-bg-color: "#222222"
                                        --f7-button-border-radius: 0px
                                        --f7-button-outline-border-color: "#333333"
                                        --f7-button-outline-border-width: 1px
                                        display: flex

1 Like

This widget is great I am hoping to use it to simulate a multi state switch like the old sitemap one. Im close but have issues with borders and spacing of the words and icons.

I want to make it look and line up with the other list items

Any clues?

You seem to have a similar problem as described here: OH3 Alignment to match existing list widget - #3 by Julian_Divett

The thread starter has not confirmed that the proposed solution does work for him but you can give it a try.

Glad you like it.
not sure what you mean, but if you want those 3 buttons to like the rest above you need to adjust these settings:

style:
  --f7-button-text-color: '=(items[props["row_" + loop.i.toString()].split(";")[0].split("=")[0]].state == loop.lbl.split("=")[0]) ? props.lblColor : "#EEEEEE"'
  --f7-button-font-size: 1.3em
  --f7-button-height: 100%
  --f7-button-bg-color: '=(items[props["row_" + loop.i.toString()].split(";")[0].split("=")[0]].state == loop.lbl.split("=")[0]) ? "#222222" : "#111111"'
  --f7-button-hover-bg-color: "#222222"
  --f7-button-pressed-bg-color: "#222222"
  --f7-button-border-radius: 0px
  --f7-button-outline-border-color: "#333333"
  --f7-button-outline-border-width: 1px

start by deleting these styles so that the theme’s default styles are not being overridden

When I added this widget directly in OH via the marketplace, the yaml code was corrupted.

then simply add it directly by copy/paste the yaml code

Yes, I did that. Just wanted to let you know that there is an issue with the marketplace.