[CLOSED] InfoPanel alignment

  1. #1

    [CLOSED] InfoPanel alignment

    Hello support team,
    I have encountered the InfoPanel alignment issue in following test case:

    @using Ext.Net;
    @using Ext.Net.MVC;
    
    @{
        ViewBag.Title = "InfoPanel";
        Layout = null;
        var X = Html.X();
    }
    
    <!DOCTYPE html>
    
    <html>
    <head>
        <title>Ext.NET MVC Test Case</title>
    
        <script type="text/javascript">
    
            var filter = function (ui) {
                var queue = Ext.net.InfoPanelQueue.queues["messagelog"],
                    container = queue.container;
    
                if (!ui && !queue.filtered) {
                    return;
                }
    
                if (!ui) {
                    App.ClearFilterBtn.toggle(true);
                }
    
                queue.filtered = !!ui;
    
                if (container && container.items) {
                    Ext.suspendLayouts();
                    container.items.each(function (item) {
                        (!ui || ui == item.tag) ? item.show() : item.hide(false);
                    });
    
                    Ext.resumeLayouts(true);
                }
            }
    
            var showInfo = function (ui) {
                var icon;
    
                switch (ui) {
                    case "info":
                        icon = "#Information";
                        break;
                    case "success":
                        icon = "#Accept";
                        break;
                    case "danger":
                        icon = "#Exclamation";
                        break;
                    case "warning":
                        icon = "#Error";
                        break;
                    default:
                        icon = "#Application";
                }
    
                var cfgInfoPanel = {
                    queue: "infopanel",
                    title: "UI " + ui.charAt(0).toUpperCase() + ui.slice(1),
                    html: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
                    ui: ui,
                    iconCls: icon
                    //alignmentEl: App._nelisViewport_CENTER.el,
                    //alignment: 't-t'
                }
    
                Ext.Msg.info(cfgInfoPanel);
    
                var cfgLog = {
                    queue: "messagelog",
                    ui: ui,
                    tag: ui,
                    pinned: true,
                    showPin: true
                };
    
                cfgLog.html = cfgInfoPanel.html + "<p style='font-size: smaller;'>" + getTimestamp() + "</p>";
                cfgLog.title = cfgInfoPanel.title;
                cfgLog.iconCls = cfgInfoPanel.iconCls;
    
                Ext.Msg.info(cfgLog);
    
                updateLogTotal();
    
            }
    
            var getTimestamp = function () {
                var date = new Date();
                return ("0" + date.getHours()).slice(-2) + ":" + ("0" + date.getMinutes()).slice(-2) + ":" + ("0" + date.getSeconds()).slice(-2);
            }
    
            var updateLogTotal = function (num) {
                Ext.getCmp("MessageLogTotal").setValue("Total Messages: " + Ext.net.InfoPanelQueue.queues['messagelog'].getItems().length);
            }
    
            // TEMPORARY SOLUTION. Copy of Ext.net.InfoPanelQueue.removeAll().
            var removeAll = function (animate) {
    
                var queue = Ext.net.InfoPanelQueue.queues["messagelog"];
    
                var items = queue.container ? queue.container.items : queue.items,
                    item,
                    align,
                    i, len;
    
                if (!items) return;
    
                for (i = 0, len = items.length; i < len; i++) {
                    item = items.getAt(i);
    
                    if (!item.hidden && item.rendered && animate !== false) {
                        item.destroy();
                    } else {
                        if (queue.container) {
                            queue.container.remove(item);
                        } else {
                            align = true;
                        }
    
                        queue.fireEvent("remove", queue, item);
                    }
                }
    
                if (align) {
                    queue.items.removeAll();
                    queue.align();
                }
    
                console.log("number of items: ", items.length);
            }
        </script>
    </head>
    
    <body>
        @(Html.X().ResourceManager())
    
        @(Html.X().Viewport()
            .ID("_nelisViewport")
            .Layout(LayoutType.Border)
            .Items(
                Html.X().Panel()
                    .ID("_nelisViewport_TOP")
                    .Region(Region.North)
                    .Items(X.Toolbar().Items(X.Button().ID("version").Text("Toolbar Button"))),
                Html.X().Panel()
                    .ID("_nelisViewport_LEFT")
                    .Region(Region.West)
                    .Width(180)
                    .MaxWidth(300)
                    .Layout(LayoutType.Fit)
                    .Title("Allegro Menu"),
                Html.X().Panel()
                    .ID("_nelisViewport_CENTER")
                    .Region(Region.Center)
                    .BodyPadding(12)
                    .Items(
    
                        X.Container().Layout(LayoutType.Column).Items(
                            X.Button().Text("Primary").Icon(Icon.Application).Width(100).Handler("showInfo('primary')"),
                            X.Button().Text("Info").Icon(Icon.Information).Width(100).MarginSpec("0 0 0 10").Handler("showInfo('info')"),
                            X.Button().Text("Success").Icon(Icon.Accept).Width(100).MarginSpec("0 0 0 10").Handler("showInfo('success')"),
                            X.Button().Text("Danger").Icon(Icon.Exclamation).Width(100).MarginSpec("0 0 0 10").Handler("showInfo('danger')"),
                            X.Button().Text("Warning").Icon(Icon.Error).Width(100).MarginSpec("0 0 0 10").Handler("showInfo('warning')")
                        ),
    
                        X.Panel().ID("log").Flex(1).OverflowY(Overflow.Auto).LayoutConfig(new VBoxLayoutConfig() { Align = VBoxAlign.Stretch })
                            .Title("Info Panel")
                            .Width(300)
                            .Height(500)
                            .MarginSpec("20 0 0 0")
                            .AlwaysOnTop(true)
                            .DockedItems(
                                X.Toolbar().Dock(Dock.Top).Items(
                                    X.DisplayField().ID("MessageLogTotal").Text("Total Messages: 0").Flex(1),
                                    X.Button().Text("Remove All").Icon(Icon.Delete).OnClientClick("removeAll(); filter();")
                                ),
                                X.Toolbar().Dock(Dock.Top).Items(
                                    X.Button().ID("ClearFilterBtn").Text("All").Pressed(true).EnableToggle(true).ToggleGroup("log").OnClientClick("filter();"),
                                    X.Button().Text("Info").EnableToggle(true).ToggleGroup("log").Icon(Icon.BulletBlue).OnClientClick("filter('info');"),
                                    X.Button().Text("Success").EnableToggle(true).ToggleGroup("log").Icon(Icon.BulletGreen).OnClientClick("filter('success');"),
                                    X.Button().Text("Danger").EnableToggle(true).ToggleGroup("log").Icon(Icon.BulletRed).OnClientClick("filter('danger');"),
                                    X.Button().Text("Warning").EnableToggle(true).ToggleGroup("log").Icon(Icon.BulletYellow).OnClientClick("filter('warning');")
                                )
                            )
                            .Bin(
                                X.InfoPanelQueue().ID("messagelog").Name("messagelog").Container("#{log}").Sliding(false).AddToEnd(false)
                                    .Listeners(l => l.Add.Handler = "filter();")
                                    .Listeners(l => l.Remove.Handler = "updateLogTotal();"),
                                X.InfoPanelQueue().Name("infopanel").Sliding(true).SlideTo(SlideTo.Down).Vertical(true).AlignmentSpec("t-t") //.AlignmentEl("App._nelisViewport_CENTER.el")
                                    .Defaults(c =>
                                    {
                                        c.Add(new Parameter() { Name = "maxWidth", Value = "400" });
                                        c.Add(new Parameter() { Name = "alwaysOnTop", Value = "true" });
                                        c.Add(new Parameter() { Name = "alignmentEl", Value = "App._nelisViewport_CENTER.el" });
                                    })
                            )
    
                    )
            )
        )
    </body>
    </html>
    
    <script type="text/javascript">
        Ext.onReady(function () {
            Ext.getCmp("version").setText("Ext JS " + Ext.getVersion().version + "&nbsp;&nbsp;|&nbsp;&nbsp;" + "Ext.NET " + Ext.net.Version);
        });
    </script>
    1. Alignment defined as InfoPanelQueue attribute (line 194): Cannot read property 'el' of null

    2. Defined as default parameter of the queue (line 199, expected scenario): alignment is ignored (although the message itself is displayed well).

    3. When the alignment is defined directly on the InfoPanel (lines 68, 69): the message is displayed completely improperly (even if it is correctly aligned).

    Am I missing something? Thank you.

    Kind regards
    Dan
    Last edited by fabricio.murta; Jul 25, 2018 at 3:30 AM.
  2. #2
    Hello Dan!

    Very nice test case, thank you! We're investigating it now and will get back to you as soon as we have a position for the points you raised.
    Fabrício Murta
    Developer & Support Expert
  3. #3
    Hello again, Dan!

    I have investigated further your inquiry, and here are the conclusions I could get to:

    As for (1) and (2) approaches you displayed on your original thread, it can't get the center panel's element because that way, it is referenced before it even exists.

    The approach on (3) looks like working to me. The problem you mention maybe is that just the panel gets a huge width, right? What about limiting the width by adding a line 70 with width: "500px"? Your goal was just to use the initial width it gets if not aligned to anything? Or, maybe, you want to constrain the pop up within the center panel? Maybe I'm still missing essential parts of your objectives with the example.

    I hope this helps!
    Fabrício Murta
    Developer & Support Expert
  4. #4
    Hi FabrÃ*cio,
    thank you for the suggestion, but I have already tried this solution. I added maxWidth on line 70, but it only reveals other issues:

    1. The animation is gone, the message does not slide from the top to the right position (although the position is correct in this case).

    2. If multiple messages are running at the same time, they rewrite each other on the same position.

    3. I understand your explanation that "it can't get the center panel's element because that way, it is referenced before it even exists". But, in my opinion, this only applies to (1). Approach (2) should work because the default parameters are applied for each InfoPanel in the queue only at the time it is created. That's why the code does not crash. The problem is that this default parameter is ignored.

    It seems that the definition in the cfgInfoPanel will invalidate the Defaults on InfoPanel (which I prefer because, in my opinion, Defaults are designed exactly for such use).

    All I want to achieve is that message respects maxWidth and other initial settings on the queue level and starts to slide down from the _nelisViewport_CENTER top edge and not from the top of the browser's window.

    Thank you for your assistance.

    Kind regards
    Dan
    Last edited by NewLink; Jul 21, 2018 at 1:47 PM.
  5. #5
    Hello again, Dan!

    I'm afraid, when you use custom queues, it will not show the slide down animation nor position below/right/left/etc each other. They will always show by the exact position and the only animation (by default) is a fade in.

    I see this is the behavior since v2, so at least as far as that's concerned, that's how using the custom alignment works. take a look:
    - v2 example
    - v4 example

    Expand the "Custom align" item and try the options therein. In both versions they just stack over each other (you can see this via the box shadow, it gets "darker/denser" as you click one item again; and lits up as they are dismissed until last one goes away.

    Notice the example runs in latest 4.6.0, we can't really try it in other early versions, to be honest. And I see the animation; fade in, in case of the custom alignment item.

    But as far the animation is concerned, it should be easy to customize it. For example, around your lines 63-69, you can customize the animation with these config blocks (or function overrides if you prefer):

    animateShow: function () {
        this.el.animate({
            duration: 2000,
            easing: "backIn",
            from: {
                opacity: 0,
                top: this.alignmentEl.getY() - 50
            },
            to: {
                opacity: 1,
                top: this.alignmentEl.getY()
            }
        });
    },
    
    animateHide: function () {
        this.el.animate({
            duration: 2000,
            easing: "backOut",
            from: {
                opacity: 1,
                top: this.alignmentEl.getY()
            },
            to: {
                opacity: 0,
                top: this.alignmentEl.getY() + 50
            },
            listeners: {
                // this is probably important, to do some "house cleaning" so better keep this listener around.
                afteranimate: this.afterHideAnimate
            }
        });
    },
    Here's what you can specify within this animate() config object: Sencha documentation on Ext.fx.Anim.

    But it will take a little more in order to make that stacking queue. Probably you can extend the stacking mechanism of the default queues, but you'll have to take care with edge cases, etc. Or well, introduce your own queueing, by cascading the animation from the afterHideAnimate... But again, this will get into another depth level, and more like an extension/feature for the component.

    Quote Originally Posted by NewLink
    3. I understand your explanation that "it can't get the center panel's element because that way, it is referenced before it even exists". But, in my opinion, this only applies to (1). Approach (2) should work because the default parameters are applied for each InfoPanel in the queue only at the time it is created. That's why the code does not crash. The problem is that this default parameter is ignored.
    Well, your thinking makes sense but, actually, you are binding the defaults a value. The value the defaults get is undefined because, when you assign App._nelisViewport_CENTER.el to the defaults, it just does not exist. Because that have been done before the component was actually rendered. If you delay setting the defaults value to after the said viewport is rendered, then it could work -- but this may be too late to ensure the child components will inherit that (now properly fetched) setting.

    I mean, you are right, as the default value is applied only when the inner components are instantiated. But you specified the default for the parameter as being undefined simply because when you defined the default parameter's value, it didn't exist. There is a similar situation with sending parameters where we can just wrap the value in a function, and make it deliver values up to date at the time of the call, by instead making it instead of App._nelisViewport_CENTER.el, expressing it as function() { return App._nelisViewport_CENTER.el; }. But, well, unfortunately this approach does not work for default parameters' value binding out-of-the-box.

    If you really want to set it via the defaults, you'd have to, for example, bind the render() event of the central viewport to update the defaults setting of the component, adding its main .el as a parameter (now that it does exist).

    I hope this clarified the concept and why it does not work -- and how would that work. As it sounds more complicated than just using the element in the config block itself, maybe it is not worth the trouble? But possible -- as long as you delay the defaults definition -after- the center panel is defined. :)
    Fabrício Murta
    Developer & Support Expert
  6. #6
    Hi FabrÃ*cio,
    I highly appreciate your approach. Your input is always very valuable and inspirational to me.

    As far as animation is concerned, your suggestion works well and is sufficient enough for mine purpose. A minor issue is the missing stacking mechanism, but I can live with that. In my opinion, it is not worth the effort to introduce my own cascading animation, especially if the current functionality might be extended in the future.

    Well, I got the idea behind the Defaults and the definition of the alignment element.

    However, I still do not understand why - if I define this element directly in cfgInfoPanel (lines 68, 69) - the other Defaults (maxWidth and alwaysOnTop) are invalidated and all the Defaults must be redefined in cfgInfoPanel again. Since I do not want to look like a hairsplitter, it's just a little detail and when all the default parameters ​​are moved to cfgInfoPanel, everything works as expected.

    In the coming days, I will apply this solution to our application framework from which the test case was derived. If the scenario described above will work in this much more complex real-world environment, I will let you know that this thread can be closed.

    Thank you very much for your help.

    Kind regards
    Dan
  7. #7
    Hello Dan!

    Actually, I didn't cover the defaults approach in my responses at all.

    While your thinking makes complete sense, when we think the InfoPanelQueue as a container, and that your message is going thru that queue, it should really inherit these defaults. But then, let's review /what/ a defaults is. It is defined down to Ext.container.Container (see its documentation entry), and governs everything added within a container as long as via a given set of methods.

    And alright, looking at the info panel code, when you call Ext.Msg.info(), it is added to the queue via a supported method. Great! Down to this point, you are right, and it should be getting the defaults from that container, following this concept -- and it should in fact.

    Now, the -but-. But, as you specified the alignmentEl, you no longer want the message to be constrained by the queue's container, do you? In fact, the choice of specifying alignmentEl makes it so it goes completely away from the container concept of the queue, simply because it must become a floating component, aligned -- no matter what -- to the element specified.

    This means:
    (a) if alignmentEl is specified, the code will completely skip the queue, and create the message box in a default/fresh/new queue.
    (b) even if you given that InfoPanelQueue a defaults with valid alignmentEl, it would be assigned too late to the message panel, and it won't follow the usual flow it should to get aligned to the element (and not follow that queue). As it would have been to be built floating and not constrained by the container.

    So, not considering the merit, but the way the component was designed is that, whenever you provide an alignmentEl, the queue concept is completely overridden. In fact, you can just comment out the queue in the first info() call. It won't change anything. And can't inherit the queue's defaults simply because it never gets added to that queue's container -- even if it does inherit anything from the queue component or setting, it won't follow the container items' add flow if it has been given an element to align to.

    It is as if alignmentEl and queue are mutually exclusive (I didn't check for possible exceptions), but alignmentEl takes precedence over queue (and for this, is a fact).

    Well, I hope now the last question is answered! And yes, thanks for posting the follow-up, I really forgot to focus on the defaults being missed, as I was on the issue of it not considering the element in your three approached. Ended up there's yet another reason the alignment wouldn't have worked with the defaults. But really, the defaults wouldn't work even otherwise: because it was being given undefined at page definition time.

    Hope this helps! Let us know if there's still something missing in the logic of the component.
    Fabrício Murta
    Developer & Support Expert
  8. #8
    Hi FabrÃ*cio,
    thank you very much for your explanation. I seem to underestimate the role of alignmentEl which, in my thinking, was just "something" to define the position of the info panel:
    top: this.alignmentEl.getY()
    Now everything is clear. Thank you for your help.

    Kind regards
    Dan
  9. #9
    Hello Dan! Glad it helped! I believe it'd okay to mark this as closed then, but let us know if you still have more inquiries about the subject.
    Fabrício Murta
    Developer & Support Expert

Similar Threads

  1. [CLOSED] How to handle the InfoPanel duration in code behind
    By maurox in forum 3.x Legacy Premium Help
    Replies: 1
    Last Post: Dec 15, 2015, 2:18 PM
  2. [CLOSED] MessageBox > InfoPanel > Standard Queues example
    By RCN in forum 3.x Legacy Premium Help
    Replies: 1
    Last Post: Feb 20, 2015, 6:38 PM
  3. [CLOSED] InfoPanel using QueueName not working
    By exe in forum 2.x Legacy Premium Help
    Replies: 3
    Last Post: Nov 26, 2014, 3:26 PM
  4. [CLOSED] Align InfoPanel to element on server side?
    By rthiney in forum 2.x Legacy Premium Help
    Replies: 3
    Last Post: Jul 03, 2014, 1:12 PM
  5. Alignment
    By fabiomarcos in forum 1.x Help
    Replies: 1
    Last Post: Jun 30, 2008, 3:44 PM

Posting Permissions