Christian-Me / node-red-contrib-ui-iro-color-picker

Node-RED dashboard color picker widget utilizing the iro.js library. Can be configured as a widget or modal popup window. Individual components can be combined,
Apache License 2.0
4 stars 3 forks source link

Slider component is not working on first attempt. #1

Closed hotNipi closed 2 years ago

hotNipi commented 3 years ago

Win 10 Desktop Chrome 90.0.4430.212 Dashboard 2.28.2 iro-color-picker 0.0.3

Doing following and nothing more:

Drag node into workspace (connect to text node to see the output) configure group, configure topic (side note: shouldn't be required parameter or if, then should fallback to default "topic")

Add one component configure component type to slider

Deploy.

What happens: The slider moves on drag/click but on mouse release jumps back to initial position. Then on second drag/click it stays where it should but there is no output.
(No errors in browser console/nr log)

Go back to component configuration change the component to some other type Deploy

Component works as expected

Go back to component configuration Change component type to slider Deploy

Component works as expected.

Christian-Me commented 3 years ago

Hi, thank you for your valuable feedback ... I can reproduce the issue.

It is caused by the default saturation is 0 = white in iro.js. if you have only the hue slider this cause the problem that the result is always white ...

Have to find a elegant way to avoid this without breaking other combinations. (But I have to cut the lawn before it starts raining ...)

If you add a saturation slider it should work.

Chris

hotNipi commented 3 years ago

One more side note: For modal the content position calculations - with small screens it is possible that poitionLeft gets negative value. Should have guard like positionTop has. if (positionLeft<5) positionLeft = 5;

Christian-Me commented 3 years ago

Think I found a solution and many other bugs, included the guard, thank you.

The colour value defaulted to pure white. This don't exist in pure hue space (outer rim of the circle). I defaulted to RED {h:3060, s:100, v:100} which could be save for all (most) components

defaulted the topic type to msg, to msg.topic.

Still the iOS problems exists. I got the display OK but still all widgets below (after) the one triggering the modal catch all mouse events :(

Pushed version 0.0.4 to github if you like to give it a try (left many consol.logs in for now)

hotNipi commented 3 years ago

I'm not on path to use the widget (no RGB lights to manipulate no other color related things in my setup) but just helping out here to test cos I like it and I see high potential. So no need to rush with versions for me. :) Sadly I have no iOS devices so cant really do much. Just read the reasons and solutions on that topic and tried to see if there is something obvious but didn't found much. The z-indexing is complicated thing and may produce issues but I have no solution to provide cos I cant try it out.

hotNipi commented 3 years ago

Question: What is the idea behind the option show modal at the point of click?

Christian-Me commented 3 years ago

Thank you for your help. I like to fix things ASAP otherwise they will never be done - and I like to have this thing away form my desk - it already took to much time.

I know you don't have safari devices - as the artless gauge has still issues on safari ;) but this is perhaps related to the library itself,

I was only testing different options. Perhaps a setup where you have many buttons next to each other - to have a feedback which one you selected. I'm using many H801 (ESP8266 based 5ch pwm dimmers) and many DIY constant current dimmers for high power LEDs. So I have 5+ Values to set per device. Instead of using all my screen space I thought it would be perhaps nice to have 5 next to each other popping up with a big sliders on my phone - which does not work (currently) . But not tested it if it makes practical sense.

The developer of iro.js is planning to implement custom sliders - so perhaps usable for any kind of value input with a easy to use ui on small devices.

hotNipi commented 3 years ago

Ok. It then makes sense when you have vertical sliders on modal and they appear near the button. For wider areas as the color wheel is, it just loses the connection I think. Anyway, I asked cos I didn't find that connection in my mind. I am on trying find out and provide another solution for positioning the modal content which may be a bit more reliable. The center of window option does not need any calculations and the group connected option works also. But for the click point I wasn't sure where to go. Thing I see it missing right now is, that it needs to support at least the orientation change. It is possible to do for center window and the widget position but I cant see the solution for the click point.

Christian-Me commented 3 years ago

I made the background of the modal-content in transparent for debugging reason. I don't have problems on chrome in windows 10 and firefox.

Center Click (as expected) image Center Window (as expected) image Center Group (all fine): Vertically it is positioned on click (widget) to give the connection to the widget here too. image

On iOS (safari) all works basically the same. Center Group is best for this purpose:

image

But all finger taps below the widget opening the modal are not going to the picker. Instead the widgets below are called.

hotNipi commented 3 years ago

Yeah, it works. I was just on push it a bit to support the orientation change cos that may happen depending on how the user creates the dashboard layout. Some of them use layouts for tabs where it forces to use different orientation on small screens. And if then opened modal is in the view, it fails to hold the position. Nothing major. I saw the way but then the click point took it down.

Christian-Me commented 3 years ago

Happy for all input! The click point is not a must, I was just experimenting.

hotNipi commented 3 years ago

The idea is to use flex for the modal as for container and for modal content margin auto holds it in center. Then if option is to hold it in the widget group, the group position can be retrieved via var widgetPos = document.getElementById("iro-color-container-${config.id}").getBoundingClientRect(); and position calculation is pretty same as it is now but bases on that measured widget area positionTop = widgetPos.y + widgetPos.height/2- ${config.widgetHeightPx/2} and the position change is done via modalContent.style.marginLeft = positionLeft + "px";

That way the center of page position does not require any calculations and it works straight for orientation also, but recalculation needs to be added for the group based option.

You can try to play with it. I haven't done anything for recalculations for orientation change just to proof that the concept works as expected for regular cases

`var html;

    if (config.pickerType.startsWith('popup')) {
        html = String.raw`
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <style>
        .iro-color-container{
            display:flex;
            width:100%;
            margin:auto;
        }

        .iro-color-disabled{
            opacity: 0.4 !important;
            pointer-events: none !important;
        }

        /* The Modal (background) */
        .modal {
          display: none; /* Hidden by default */
          -webkit-transform: translate3d(0, 0, 0);
          transform: translate3d(0, 0, 0);
          position: fixed;
          z-index: 100; /* Sit on top 2147483647*/
          /* padding-top: 100px;  Location of the box */
          left: 0;
          top: 0;
          width: 100%; /* Full width */
          height: 100%; /* Full height */
          overflow: auto; /* Enable scroll if needed */
          background-color: rgb(0,0,0); /* Fallback color */
          background-color: rgba(0,0,0,0.8); /* Black w/ opacity */
        }

        /* Modal Content */
        .modal-content {
          background-color: rgba(0,0,0,0);
          position: relative;
          display: block;
          margin:auto;
          padding-top:64px;
        }
        @media (min-width: 0) and (max-width: 959px){
            .modal-content {
                padding-top:48px;
            }
        }

        </style>

        <script type='text/javascript' src='ui-iro-color-picker/js/iro.min.js'></script>

        <div class="iro-color-container" id="iro-color-container-${config.id}" style="display:flex;" ng-init='init(` + configAsJson + `)'>
            <div ng-if="${config.label != ""}" style="width:${config.labelWidth}px;">${config.label}</div>          
            <button id="colorButton-${config.id}" class="md-raised md-button md-ink-ripple" type="button" style="width:100px;" color='{{colorHex || config.site.theme["widget-backgroundColor"].value}}'>&nbsp;</Button>
        </div>

        <!-- The Modal -->
        <div id="colorModal-${config.id}" class="modal">

          <!-- Modal content -->
          <div class="modal-content" id="modal-content-${config.id}" style="width:${config.widgetWidthPx}px;">
            <!-- <span id="colorModal-close-${config.id}" class="close">&times;</span> -->
            <div id='ui_iro_color_picker-{{$id}}' style="width:${config.widgetWidthPx}px; background-color:unset; border:unset;"></div>
          </div>

        </div>

        <script>
            // When the user clicks the button, open the modal 
            document.getElementById("colorButton-${config.id}").onclick = function(e) {
                var modal = document.getElementById("colorModal-${config.id}");
                var modalContent = document.getElementById("modal-content-${config.id}");
                modal.style.backgroundColor = '${config.backgroundColor}'+Math.floor(${config.backgroundDim / 100*255}).toString(16);
                var widgetPos = document.getElementById("iro-color-container-${config.id}").getBoundingClientRect();
                var positionLeft;
                var positionTop;
                var pickerType = '${config.pickerType}';
                switch (pickerType) {
                    case 'popupCW': 
                        positionTop = undefined//(window.innerHeight /2  - ${config.widgetHeightPx/2});
                        positionLeft = undefined// (window.innerWidth /2  - ${config.widgetWidthPx/2});
                        break;
                    case 'popupCG':
                        //positionTop = (e.clientY - e.layerY - ${config.widgetHeightPx/2});
                       // positionLeft = (e.clientX - e.layerX - ${config.widgetWidthPx / 2} + 35);
                       positionTop = widgetPos.y + widgetPos.height/2- ${config.widgetHeightPx/2}
                       positionLeft = widgetPos.x + widgetPos.width/2 - ${config.widgetWidthPx / 2}
                        break;
                        case 'popupCC':
                       // positionTop = (e.clientY - ${config.widgetHeightPx/2});
                       // positionLeft = (e.clientX - ${config.widgetWidthPx/2});
                    break;
                }
                if (positionTop + ${config.widgetHeightPx} > window.innerHeight) positionTop = window.innerHeight - ${config.widgetHeightPx};
                if (positionTop<5) positionTop = 5;

                // console.log(positionLeft, e.clientX, e.layerX, ${config.widgetWidthPx/2}, e);
                // console.log(pickerType, positionTop, window.innerHeight, ${config.widgetHeightPx});

                if(positionLeft && positionTop){
                    modalContent.style.marginLeft = positionLeft + "px";
                    modalContent.style.marginTop = positionTop + "px";
                }

                modal.style.display = "flex";

                // When the user clicks on <span> (x), close the modal
                /*
                document.getElementById("colorModal-close-${config.id}").onclick = function() {
                    document.getElementById("colorModal-${config.id}").style.display = "none";
                }
                */
                // When the user clicks anywhere outside of the modal, close it
                window.onclick = function(event) {
                    var modal = document.getElementById("colorModal-${config.id}");
                    if (event.target == modal) {
                        modal.style.display = "none";
                    }
                }
            }

        </script>
        `;
        } else {
            html = String.raw`
            <script type='text/javascript' src='ui-iro-color-picker/js/iro.min.js'></script>
            <div class="iro-color-container" id="iro-color-container-${config.id}" style="display:flex;" ng-init='init(` + configAsJson + `)'>
                <div ng-if="${config.label != ""}" id="iro-color-label-${config.id}" style="width:${config.labelWidth}px;">${config.label}</div>          
                <div id='ui_iro_color_picker-{{$id}}' style="width:${config.widgetWidthPx}px; background-color:unset; border:unset;"></div>
            </div>
        `;

    }
    return html;`
Christian-Me commented 3 years ago

Thank you this - will test it ASAP. Happy to learn more in modern web development. My last experiences are more than 20 years old - long before reactive web design.

Will be useful for my next project - a chart widget including a modal popup option for closer data inspection. Something between the chart widget and grafana. Planning to use dygraphs. Zooming, panning, huge data capabilities and dual axis support are the key features I like to get out of this.

Christian-Me commented 3 years ago

Hi ... finally version 0.1.0 is on my repo ... thank you for your suggestions and insights! Helped a lot. The initial size calculation code is a mess but works ...

Christian-Me commented 3 years ago

Think we can close this? Or did you found any unfixed issues?