✨ FlexDash alpha release - round 3

I appreciate its still early days for FD!

But I was just looking at replicating some of my dashboard with FD.

Can I request an LED type widget to display status ?

Something like node-red-contrib-ui-led

I'm using a stat node for now but its limited to 2 colors, also I don't need the value shown.
Unless I'm missing a better work around :wink:

Not only that, but writing is far less error-prone and you don't need to think at all about layout or formatting. HTML WYSIWYG editors all seem to have edge-cases that need manual tweaks but editing Markdown is simply plain text. Though there are a couple of really good WYSIWYG editors as well now for Markdown which can be useful for the type of people who can't even cope with text editing! Like project managers! :rofl:

I'm happy to help. Can I ask you to get started using a "FD Custom" (custom widget) and post where you get stuck? Or am I asking for too much and you'd like me to make the first steps?

I'll see how far I get then :wink:

1 Like

@smcgann99 I saw that the custom widget fails to provide sample getting-started code, so I did that for you :laughing:
I also hit a snag adding the custom widget to a grid, oops, fix coming asap.

[{"id":"be95541009d7c835","type":"inject","z":"42aa3359d7ac96f5","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":90,"y":700,"wires":[["fea875a20cb7859d"]]},{"id":"c0ecddbd9ebe5fac","type":"inject","z":"42aa3359d7ac96f5","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false","payloadType":"bool","x":90,"y":740,"wires":[["fea875a20cb7859d"]]},{"id":"d25895a46cb4e863","type":"inject","z":"42aa3359d7ac96f5","name":"null","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"null","payloadType":"json","x":90,"y":780,"wires":[["fea875a20cb7859d"]]},{"id":"fea875a20cb7859d","type":"flexdash custom","z":"42aa3359d7ac96f5","name":"LED","title":"LED","sfc_source":"<template>\n    <!-- For classes see the entire styles section of https://next.vuetifyjs.com/en/styles/border-radius/ -->\n    <div class=\"flex-grow-1 w-100 pa-2\">\n        <div :class=\"`w-100 h-100 rounded-circle bg-${color}`\"\n             style=\"border:3 solid #88a; box-shadow: 0px 0px 2px 2px #88a;\" />\n    </div>\n</template>\n\n<script>\nexport default {\n    props: {\n        payload: { default: undefined }, // type is \"any\"\n    },\n    computed: {\n        color() {\n            // use any material-design color: https://vuetifyjs.com/en/styles/colors/#material-colors\n            // (warning, it's link to the old docs 'cause that page is broken in the new ones...)\n            if (this.payload) return \"green\"\n            else if (this.payload == null) return \"grey\" // null or undefined\n            else return \"red-accent-3\"\n        }\n    }\n}\n</script>","fd_container":"a96ede31d15e8fcc","fd_cols":1,"fd_rows":1,"fd_array":false,"fd_array_max":10,"x":270,"y":740,"wires":[[]]},{"id":"a96ede31d15e8fcc","type":"flexdash container","name":"Repro","title":"","kind":"StdGrid","fd_children":",7d8110bad209c9b4,e7c4f917522b9e07,73d471ead270d371,0ca10a099f0a6969,1d8ee88fedb20405,e0dc4b448dc085ca,78673e91a6b52f72,e9b320e81e2e1981,e7b8b8127072bebb,078aa668f5bf1983,fea875a20cb7859d","tab":"1e908252f3517797","min_cols":"6","max_cols":"20","unicast":"ignore","parent":"","solid":false,"cols":"1","rows":"1"},{"id":"1e908252f3517797","type":"flexdash tab","name":"Repros","icon":"mdi-view-dashboard","title":"Repro","fd_children":",a96ede31d15e8fcc","fd":"e8f5aea52ab49500"}]

image
image


BTW, don't forget that the markdown widget has an "expand" button in the top-right corner that you can click to get a full-page view of the content. So you don't always have to size it so everything shows (plus there's scrolling, of course).


Update: I fixed the issues I encountered:

  • markdown widget when expanded doesn't show a 'shrink' button
  • custom widget doesn't have sample code when first opened
  • can't add widget to grid in certain circumstances
22 Jan 12:56:49 - [info] Node-RED FlexDash plugin version 0.4.143            
22 Jan 12:56:49 - [info] Node-RED FlexDash plugin version 0.4.143
22 Jan 12:56:49 - [info] Node-RED Vue version 0.1.12
22 Jan 12:56:52 - [info] Node-RED FlexDash version 0.4.143
22 Jan 12:56:52 - [info] Node-RED FD Core Widgets version 0.4.64
2 Likes

In the context of usage in a dashboard for relaying information, I preferred it to have no additional spacing (as it was before this update), as users would always have the option to create their own space by adding an extra blank line if they wished (like I'm doing here).

If Markdown was being used in a website or forum, you have a greater page size, so adding extra space for readability isn't really going to be a problem, but in a constrained dashboard unit IMO the focus should be in delivering the content, not aesthetic white space.

Not everyone would agree with my view, but I can always work around things by doubling the widget rows to accommodate the text...

In Markdown, I believe that there is a difference between line-breaks & paragraph breaks. See Basic Syntax | Markdown Guide but I could be wrong.

No, that is correct 2 line-feeds make a new para. 1 line-feed is just a line break and has no special meaning in the resulting output HTML.

@smcgann99 I found a little bit of time to tweak the CSS, now it looks a bit more like an LED and it auto-sizes according to the height and there's a label:
image
image

[{"id":"be95541009d7c835","type":"inject","z":"42aa3359d7ac96f5","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":90,"y":700,"wires":[["fea875a20cb7859d"]]},{"id":"c0ecddbd9ebe5fac","type":"inject","z":"42aa3359d7ac96f5","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false","payloadType":"bool","x":90,"y":740,"wires":[["fea875a20cb7859d"]]},{"id":"d25895a46cb4e863","type":"inject","z":"42aa3359d7ac96f5","name":"null","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"null","payloadType":"json","x":90,"y":780,"wires":[["fea875a20cb7859d"]]},{"id":"fea875a20cb7859d","type":"flexdash custom","z":"42aa3359d7ac96f5","name":"LED","title":"LED","sfc_source":"<template>\n    <!-- For classes see the entire styles section of https://next.vuetifyjs.com/en/styles/border-radius/ -->\n    <div class=\"flex-grow-1 w-100 pa-2 d-flex flex-row align-center justify-space-around\">\n        <div>{{ label }}</div>\n        <div :class=\"`led bg-${color}`\" />\n    </div>\n</template>\n\n<style scoped>\ndiv.led {\n    height: 80%;\n    aspect-ratio: 1;\n    width: min-content;\n    border-radius: 50%;\n    box-shadow: 0px 0px 2px 2px #88a;\n}\n</style>\n\n<script>\nexport default {\n    props: {\n        payload: { default: undefined }, // type is \"any\"\n        label: { default: \"status\", type: String }\n    },\n    computed: {\n        color() {\n            // use any material-design color: https://vuetifyjs.com/en/styles/colors/#material-colors\n            // (warning, it's link to the old docs 'cause that page is broken in the new ones...)\n            if (this.payload) return \"green\"\n            else if (this.payload == null) return \"grey\" // null or undefined\n            else return \"red-accent-3\"\n        }\n    }\n}\n</script>","fd_container":"a96ede31d15e8fcc","fd_cols":1,"fd_rows":1,"fd_array":false,"fd_array_max":10,"x":270,"y":740,"wires":[[]]},{"id":"a96ede31d15e8fcc","type":"flexdash container","name":"Repro","title":"","kind":"StdGrid","fd_children":",7d8110bad209c9b4,e7c4f917522b9e07,73d471ead270d371,0ca10a099f0a6969,1d8ee88fedb20405,e0dc4b448dc085ca,78673e91a6b52f72,e9b320e81e2e1981,e7b8b8127072bebb,078aa668f5bf1983,fea875a20cb7859d","tab":"1e908252f3517797","min_cols":"6","max_cols":"20","unicast":"ignore","parent":"","solid":false,"cols":"1","rows":"1"},{"id":"1e908252f3517797","type":"flexdash tab","name":"Repros","icon":"mdi-view-dashboard","title":"Repro","fd_children":",a96ede31d15e8fcc","fd":"e8f5aea52ab49500"}]

Enjoy fiddling :laughing: !! Let me know if you need help...

3 Likes

If it was just me everything would be 8pt font and zero margin/padding :laughing:

Anyway, there's that "Flex" in FlexDash... You be the boss! I copied the markdown widget into a custom widget, cut out some fluff, and tweaked the paragraph margins:

[{"id":"694f2b90fcd08117","type":"inject","z":"42aa3359d7ac96f5","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":100,"y":360,"wires":[["66ade629c7f44e53"]]},{"id":"66ade629c7f44e53","type":"function","z":"42aa3359d7ac96f5","name":"some text","func":"return { payload: `\n# Testing markdown\n\nThis is one paragraph with many many many many words\nthat goes on for ever and ever and ever.\n\nThis is a second paragraph.\n\nTrying again\n- and a bullet\n- second bullet\nTada!\n\n`}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":260,"y":360,"wires":[["e7b8b8127072bebb","327d0e73fdd45516"]]},{"id":"e7b8b8127072bebb","type":"fd-markdown","z":"42aa3359d7ac96f5","fd_container":"a96ede31d15e8fcc","fd_cols":"2","fd_rows":"4","fd_array":false,"fd_array_max":10,"name":"","title":"Markdown","popup_info":"","text":"- _Hello_ __!__","x":430,"y":360,"wires":[]},{"id":"327d0e73fdd45516","type":"flexdash custom","z":"42aa3359d7ac96f5","name":"MD custom","title":"My Markdown","sfc_source":"<!-- Markdown - Adaptation of the FLexDash Markdown widget -->\n<template>\n  <v-card-text class=\"pt-0 flex-grow-1 flex-shrink-1 overflow-auto\">\n    <md class=\"pt-1\" style=\"width:100%\">{{payload}}</md>\n  </v-card-text>\n</template>\n\n<style scoped>\n  :deep(p) { margin: 0px; }\n</style>\n\n<script>\nexport default {\n  full_page: true, // tells the widget-wrapper to provide a full-page button\n  props: {\n    payload: { type: String, default: \"- _Hello_ __!__\" },\n  },\n}\n</script>\n","fd_container":"a96ede31d15e8fcc","fd_cols":2,"fd_rows":4,"fd_array":false,"fd_array_max":10,"x":430,"y":400,"wires":[[]]},{"id":"a96ede31d15e8fcc","type":"flexdash container","name":"Repro","title":"","kind":"StdGrid","fd_children":",7d8110bad209c9b4,e7c4f917522b9e07,73d471ead270d371,0ca10a099f0a6969,1d8ee88fedb20405,e0dc4b448dc085ca,78673e91a6b52f72,e9b320e81e2e1981,e7b8b8127072bebb,078aa668f5bf1983,fea875a20cb7859d,327d0e73fdd45516","tab":"1e908252f3517797","min_cols":"6","max_cols":"20","unicast":"ignore","parent":"","solid":false,"cols":"1","rows":"1"},{"id":"1e908252f3517797","type":"flexdash tab","name":"Repros","icon":"mdi-view-dashboard","title":"Repro","fd_children":",a96ede31d15e8fcc","fd":"e8f5aea52ab49500"}]

Before:
image

After:
image

Not much code:

<!-- Markdown - Adaptation of the FLexDash Markdown widget -->
<template>
  <v-card-text class="pt-0 flex-grow-1 flex-shrink-1 overflow-auto">
    <md class="pt-1" style="width:100%">{{payload}}</md>
  </v-card-text>
</template>

<style scoped>
  :deep(p) { margin: 0px; }
</style>

<script>
export default {
  full_page: true, // tells the widget-wrapper to provide a full-page button
  props: {
    payload: { type: String, default: "- _Hello_ __!__" },
  },
}
</script>

The styles you're overriding are in the FlexDash markdown component. (Caveat: I've since made some small changes to use "style scoped" in the component, if the override doesn't work for you add a '!' after the CSS rule.)

The :deep(...) is there because of "style scoped" and allows the match to propagate to inner components, e.g., the <md>.

As usual, I'm here if you need help :wink:

2 Likes

I don't get this!
How does the custom widget know that it's acting as a 'markdown' widget?
You aren't loading or linking the markdown library in the widget, so how does it know that margin: 0px; relates specifically to markdown styling.

Oh the dark arts....

2 Likes

Oh, sorry for making things look magic:

  • The FlexDash markdown component is registered as md, so the <md ...>{{payload}}</md> line instantiates a markdown component and fills its content with the payload prop. (The standard markdown Widget is also just a thin wrapper around the md component.)
  • The style scoped tells Vue to make all the styles scoped to the component (i.e. your custom component). If you inspect the elements in the browser you can see that the v-card-text and md elements of the widget (which became a div and a span respectively) have a data-v-07cb5d5d attribute:
  • Looking at the styles that apply to the <p>...<p> element, you can see this:
    image
  • So the style :deep(p) { margin: 0px; } got transformed into [data-v-07cb5d5d] p { margin: 0px; } which means "a <p> element that is a descendant of an element with attribute data-v-07cb5d5d", so this is how the margin is scoped just to paragraphs within the markdown.
  • You can also see there the original 8px top margin applied by the md component which is overridden and has a different data-v-af... attribute selector
  • If you omit the :deep() around the p you'd get a selector of p[data-v-07cb5d5d], which would only apply to paragraphs directly in the custom widget (of which there are none)

Does that clarify how this works a bit? And yes, it's totally crazy...

1 Like

I don't see any update for this in pallet manager ?

There's no update for the core widgets, it's just @flexdash/node-red-flexdash that's been updated to v0.4.142.
You could try refreshing your browser

flex

what about this update ?

<style scoped>
div.led {
    height: 80%;
    aspect-ratio: 1;
    width: min-content;
    border-radius: 50%;
    box-shadow: 0px 0px 15px 15px **computed color here;**
}
</style>

How do I make use of the computed values in the style - eg like above ?

I don't know what to do about the palette manager. It has a bug which causes dependencies not to be installed/updated, which means users end up with broken stuff. Also, the "update this package" is not automatable, which means remembering to do more tedious manual clicking, which I surprisingly forget...

Yes, thank you :smiley:

v-bind(color)

1 Like

Something to consider abut colors...

The fragment you wrote above uses a style tag so you end up putting a "raw" CSS color in there. That's the "red", "cornsilk", "chartreuse", etc. colors defined in the CSS standard or a numeric value like #3487a5.

What I had in the widget I posted was

        <div :class="`led bg-${color}`" />

This applies a CSS class to the div with names like bg-red-lighten-3 or bg-blue-accent-3 from Vuetify's Material Design color palette.

I have found the material design palette to be very helpful because it provides a bunch of colors that fit well together and it's very easy to use lighter or darker versions. It avoids ending up with slight variations, like "why does the red in that LED have a different tint from the red in that button?". Of course I understand some people may want a different red, and that totally OK.

Edit: oops, I somehow missed the fact that this is for the box-shadow only and there are no classes for that in Vuetify, so while my comment is correct in general it doesn't actually apply here. Sorry for any confusion caused.

So I'm just about getting to grips with "hacking" standard dashboard CSS, so you have thrown me a bit with vue :wink:

I was looking to use the shadow as an easy way to actually create a glow effect.

Will have a play around and see what I can come up with !

2 Likes