Flex-table-card

Use a native styling option - but use a user-defined variable instead of a color.
Define this variable dynamically by card-mod.

Update 24.11.23: added a description of this method here.

1 Like

Ah, yes – thank you!

So sorry to everyone. I did not find a chance to get more info to the dev. Maybe next week when I’ll be the only one ‘at work’ :slight_smile:

1 Like

I’m trying to do two things:

  1. I want to sort by a time field (originally created by now() ). I also display this field using hours_mins_passed. I have learned that I can’t sort the column that is formatted, so I include the column twice - once unformatted and once formatted. I sort by the unformatted column (and hide it) and this seems to work (as long as it is listed before my formatted column) - is there a better way to do this?

  2. the formatted time column has some high numbers (eg 2,975 hours). It would be nice if there was a days_hours_mins formatter. Given there isn’t, is there a way to use a relative time function instead? Is there a js equivalent to relative_time that I could use?

type: custom:flex-table-card
title: Door Changes (last)
entities:
  include:
    - sensor.*last_locked
    - sensor.*door_last_opened_or_closed
  exclude:
    - sensor.old*
sort_by:
  - state-
strict: true
clickable: true
columns:
  - data: display_name_for_table
    name: Door
  - data: state
    name: raw state
    hidden: true
  - data: state
    name: time Passed
    fmt: hours_mins_passed
  - data: master_sort_value
    name: sort value
    hidden: true
css:
  tbody tr+: 'color: black'

Here is what my table looks like:

image

Thanks,
Ken

I’m trying out the conditional cell formatting - based on a value in the grid. The example provided in the beginning of this thread uses the value of the state to determine conditional formatting (which I assume is always called ‘x’???) I’d like to do this based on the value of another column in the grid. For example if column 1 is state, column 2 is VAL2 and column 3 is VAL3. Then I want to conditionally format VAL2 based on the value of VAL3

Just for reference, the sample given above is

  - type: custom:auto-entities
    filter:
      include:
        - domain: light
    sort:
      count: 5
    card:
      type: custom:flex-table-card
      columns:
        - name: brightness
          data: brightness
        - name: "div"
          data: brightness
          modify: |-
            if (x == undefined)
              "none"
            else if (parseInt(x) >= 100 )
              '<div style="color:cyan;">more</div>'
            else
              '<div style="color:red;">less</div>'
        - name: "span"
          data: brightness
          modify: |-
            if (x == undefined)
              "none"
            else if (parseInt(x) >= 100 )
              '<span style="color:cyan;">more</span>'
            else
              '<span style="color:red;">less</span>'
      strict: true

If state is always called “x”, what do I call one of the other columns in my table when writing the conditional code?

Thanks,
Ken

I could be wrong - but imho currently there is only one way to style a column_A dependently on column_B.
Assume “x” is a value of THIS column.
Consider a sensor with an attribute which is a dictionary with sub-elements “a” & “b”.
You have 2 columns addressing this attribute.
Each column has a corr. “modify” to select “x.a” or “x.b” (or how it is possible currently for dicts). And you may use “x.b” inside “modify” with “x.a”.
To use this method you need to create a template sensor with that dict.

Ok - many thanks - but I will admit that I will need to go through this slowly - looks like another learning curve in front of me!

Is it possible to have a common header for columns? This is what I have right now:

image

type: custom:flex-table-card
entities:
  include:
    - sensor.ignacios_devices
columns:
  - name: Personal Phone Location
    data: personal_phone_location
  - name: Personal Phone Battery
    data: personal_phone_battery
    suffix: '%'
  - name: Wallet Location
    data: wallet_location
  - name: Work Phone Location
    data: work_phone_location
  - name: Work Phone Battery
    data: work_phone_battery
    suffix: '%'
  - name: Badge
    data: badge_location

I would like to add a row that for the first two columns says Personal Phone, and for columns 4 and 5 says Work phone.

Have you considered to read the thread? It was described.

1 Like

I consider it, and actually did it before posting. Alas, I clearly cannot read. Thanks for the help!

1st post → styling → styling a header row

image

Thanks. I will need to learn some css to figure out how to do this.

How to set a fixed width for a column

Assume we have 2 tables:

And these tables both have similar 4 columns - “x_1” … “x_4”.
Ofc I would like to have them placed horizontally aligned!

Can be done by setting a fixed width for these columns:

tbody tr td:nth-child(1)+: 'width: 50px;'
tbody tr td:nth-child(2)+: 'width: 80px;'
tbody tr td:nth-child(3)+: 'width: 110px;'
tbody tr td:nth-child(4)+: 'width: 140px;'

But this may not work in iOS (depends on a number of columns & their widths):

This is a working code:

tbody tr td:nth-child(1)+: 'min-width: 50px;width: 50px;'
tbody tr td:nth-child(2)+: 'min-width: 80px;width: 80px;'
tbody tr td:nth-child(3)+: 'min-width: 110px;width: 110px;'
tbody tr td:nth-child(4)+: 'min-width: 140px;width: 140px;'

i.e. we need to specify “width” & “min-width”:

P.S. Probably it depends not only a iOS - it probably depends on a viewport.
Not a css expert, using “try - fail - repeat - succeed” approach.

1 Like

I was very happy with the addition of the sorting on columns however I see an issue that I am not sure how to resolve. If you use HTML markup in the modify command, the sorting seems to be based on the HTML markup and not some value.

Example:

        - name: SOS
          data: '[[attribute]]'
          modify: |-
            if (x[8] > 3 ){
                '<div style="background-color:lightgreen;text-align:center">' + x[8] + '</div>'; }
            else if (x[8] < 3) {
                '<div style="background-color:lightcoral;text-align:center"">' + x[8] + '</div>'; }
            else {
                '<div style=";text-align:center">' + x[8] +'</div>'}

Now the values for x[8] can be 2,3 or 4 really (this is the data). If I just output x[8] the auto sorting is fine, but using the above template and sorting the column I get this:

As you can see 4 … then 2 … then 3 because the sorting is not based on the value of x[8], it must be based on something that aligns with the HTML code in the cell to get specific colors.

Am I wrong? If not, then the “sort” for a column click should be separate from the “modify” for that column.

For completeness, that is from a decluttering template so it is used like this:

      - type: custom:decluttering-card
        template: sos_settings
        variables:
          - title: NHL Strength of Schedule
          - entity: sensor.hockey_sos
          - attribute: pageProps.dataRows

So x is “dataRows” and I want to sort by x[8] when I click the sort column and not by:

<div style="background-color:lightgreen">4</div>

If this is “how it is” then I will post an enhancement request which should allow one to specifiy what to sort-by when you click the sortable header.

Now I could add:

sort_by: pageProps.dataRows-

And add a hidden column for the first column, but the instant someone sorts the column, you can never return back to that view (so that is not the answer). I could create a non-hidden column just for that original sort and display nothing in it but that is a total kludge also.

Updated KLUDGE Workaround (put the number as the first entry in the cell and set display:none):

          modify: |-
            if (x[8] > 3 ){
                '<div style="display:none;">'+ x[8] + '</div>' + '<div style="background-color:lightgreen;text-align:center">' + x[8] + '</div>'; }
            else if (x[8] < 3) {
                '<div style="display:none;">'+ x[8] + '</div>' + '<div style="background-color:lightcoral;text-align:center"">' + x[8] + '</div>'; }
            else {
                '<div style="display:none;">'+ x[8] + '</div>' + '<div style=";text-align:center">' + x[8] +'</div>'}

Does anybody know why an unavailable value is displayed as “undefinedundefinedundefined”, i.e. three times “undefined”?

I do not want to use strictbecause some values are available despite others not being available. Or is there an option I missed that allows omitting only “undefined” data?

EDIT: Found the workaround in the github issues and this thread, duh.
Just use

modify: if(x.length == 0){"-"}else{x}

It will replace all unavailable data with “-” (or whatever else you want)

Is there a way of comparing the value of column 3 with column 2 of the same row?
I think you mentioned this in another answer in this thread and said it is not diretcly possible but in another answer you mentioned that the attributes should be available.

So am I correct in thinking that I cannot “read” the value of column x but I can read the attribute that is being used in column x and it will automatically be the one from the same row?

Because x is always already the attribute of an entity. So how would I read a different attribute of that same entity? I cannot go to the parent of x, I don’t think :thinking:

I think that is why you mention dicts here Flex-table-card - #135 by Ildar_Gabdullin?

So no way without creating a helper dict?

Formatters - duration and duration_h

Two of the formatters listed to the documentation are duration and duration_h.

https://github.com/custom-cards/flex-table-card/blob/447cc0c4842a8d88f42edd5ad61ec47dc9c41552/docs/config-ref.md

I can’t find any documentation on how these work or examples of how to use them, and try as I might, I can’t get them to work. It would be interesting to see what they do and if they are different than the the two “passed” formatters. Has anyone used them?

Looking at the raw Javascript source should tell you everything:

    duration(data) {
        let h = (data > 3600) ? Math.floor(data / 3600).toString() + ':' : '';
        let m = (data > 60) ? Math.floor((data % 3600) / 60).toString().padStart(2, 0) + ':' : '';
        let s = (data > 0) ? Math.floor((data % 3600) % 60).toString() : '';
        if (m) s = s.padStart(2, 0);
        return h + m + s;
    }
    duration_h(data) {
        let d = (data > 86400) ? Math.floor(data / 86400).toString() + 'd ' : '';
        let h = (data > 3600) ? Math.floor((data % 86400) / 3600) : ''
        h = (d) ? h.toString().padStart(2,0) + ':' : ((h) ? h.toString() + ':' : '');

        let m = (data > 60) ? Math.floor((data % 3600) / 60).toString().padStart(2, 0) + ':' : '';
        let s = (data > 0) ? Math.floor((data % 3600) % 60).toString() : '';
        if (m) s = s.padStart(2, 0);
        return d + h + m + s;
    }

Assume some entity has attr1 and attr2 attributes.
If some column is associated to attr1 - then X is a value of this attribute.
You cannot use attr2 to modify a value of this column.

Next, assume 2 columns are associated to attr1.
In both columns you may use X (which points to same attribute) to modify values.

The only way to use ANY attribute in a column - keeping (sub)attributes in a dict as it was suggested earlier.

I created a workaround table that is more or less working.

I have not yet figured out where the small gap is coming from.
I also have not figured out how to make sure the rows are aligned because if the header of table 1 is 3 lines and that of table 2 is 2 lines, the rows no longer align.

Summary
type: custom:layout-card
layout_type: grid
layout:
  grid-template-columns: 4fr 1fr
  grid-template-rows: auto
  grid-template-areas: |
    "main1 side1"
cards:
  - type: custom:flex-table-card
    view_layout:
      grid-area: main1
    entities:
      include: climate.*
    clickable: true
    columns:
      - name: ''
        data: friendly_name
        align: left
        modify: x.replace("eQ-3 ", "").replace(" Climate", "")
      - name: <div>Target Temperature<br>°C</div>
        data: temperature
        align: center
        modify: |-
          if (x.length == 0)
            "-"
          else
            if (parseInt(x) >= 4.5 )
              '<span style="color:white;"><b>' + x + '</span>'
            else
              '<span style="color:grey;"><i>' + "Off" + '</span>'
      - name: <div>Current Temperature<br>°C</div>
        data: current_temperature
        align: center
        modify: |-
          if (x.length == 0)
            '<span style="color:grey;"><i>' + "-" + '</span>'
          else
            x
      - name: <div>Target Humidity<br>%</div>
        data: current_humidity
        align: center
        modify: |-
          if (x.length == 0)
            '<span style="color:grey;"><i>' + "-" + '</span>'
          else
            x
    css:
      table+: 'padding: 16px 0px 16px 16px;'
      tbody tr+: 'user-select: text'
      tbody tr td:nth-child(1)+: 'min-width: 1fr;width: 1fr;white-space: nowrap;'
      tbody tr td:nth-child(2)+: 'min-width: 1fr;width: 1fr;'
      tbody tr td:nth-child(3)+: 'min-width: 1fr;width: 1fr;'
      tbody tr td:nth-child(4)+: 'min-width: 1fr;width: 1fr;'
      th+: 'border-bottom: 1px solid rgb(127,127,127);'
  - type: custom:flex-table-card
    view_layout:
      grid-area: side1
    entities:
      include:
        - sensor.eq3*valve
    clickable: true
    columns:
      - name: <div>Valve<br><br>%</div>
        data: state
        align: center
        modify: |-
          if (x.length == 0)
            "-"
          else
            if (x > 0 )
              '<span style="color:white;"><b>' + x + '</span>'
            else
              '<span style="color:grey;"><i>' + x + '</span>'
    css:
      table+: 'padding: 16px 16px 16px 0px;'
      tbody tr+: 'user-select: text'
      tbody tr td:nth-child(1)+: 'min-width: 1fr;width: 1fr;white-space: nowrap;'
      tbody tr td:nth-child(2)+: 'min-width: 1fr;width: 1fr;'
      tbody tr td:nth-child(3)+: 'min-width: 1fr;width: 1fr;'
      tbody tr td:nth-child(4)+: 'min-width: 1fr;width: 1fr;'
      th+: 'border-bottom: 1px solid rgb(127,127,127);'