dijit.Dialog out and within the boundaries…

Hello guys and girls :)

My first code post will be about my first contribution attempt at the dojotoolkit framework today :)

# edit : 06/06/2011 : note that this fix should be pushed to the 1.7 release of dojo, so if you plan to migrate to 1.7, you could wait for the fixed version ;) #
# edit !!!!!!!!!!!!! : 06/06/2011 : seems wordpress killed my & to write some & amp; instead, should be fine now… #

The problem ?
the standard (1.6.1 as I write those lines) dijit.Dialog can be dragged out of the browser’s viewport boundaries (top, bottom, left and right).
While right and bottom can create some scrollbars, it’s still possible to reproduce the annoying behaviour which is : the dragbar get masked and once dropped, the dialog can never be dragged back into the viewport… hence losing completely control over the dijit !

How to solve the problem ?
There are many ways to solve this in reality. One option was to use a constrained Moveable, but I deemed that option not representative of what the initial intent of the dialog was. Using a constrained Moveable would mean having your dialog’s dragging blocked within the boundaries of a set parent… tough one reason for moving a dialog could be it is in front of something you want to check, behind…
Also I believe that the initial Dialog dijit was meant to be tossed around to allow max visibility of what was behind. SO… eventhough it IS a solution (which is used by JQuery and YUI), I decided to try something else. The idea I had was to recalculate the dialog’s coordinates onDrop, thus allowing one to get the dialog out of the boundaries as long as the left button was kept down. Then onMouseUp recalculate where the dialog should have been blocked and reposition it.

How did it go ?
Pretty well in fact, the change was lighter than I initially thought.
First I had to connect to the drop event and rather than subscribing to the dnd events, then check the dialog’s coordinate against the viewport’s ones, and recalculates the position if needed. Last but not least, set the new positions according to the calculations.

here are the changes I made to Dialog.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
_endDrag: function(e){
  // summary:
  //        Called after dragging the Dialog. Saves the position of the dialog in the viewport.
  // tags:
  //        private
  if(e && e.node && e.node === this.domNode){
    var nodePosition = dojo.position(e.node);
    var viewport = dojo.window.getBox();
    nodePosition.y = Math.min(Math.max(nodePosition.y, 0), (viewport.h - nodePosition.h));
    nodePosition.x = Math.min(Math.max(nodePosition.x, 0), (viewport.w - nodePosition.w));
    this._relativePosition = nodePosition;
    this._position();
  }
},

_setup: function(){
  // summary:
  //        Stuff we need to do before showing the Dialog for the first
  //        time (but we defer it until right beforehand, for
  //        performance reasons).
  // tags:
  //        private
  var node = this.domNode;
  if(this.titleBar && this.draggable){
    this._moveable = (dojo.isIE == 6) ?
    new dojo.dnd.TimedMoveable(node, { handle: this.titleBar }) :   // prevent overload, see #5285
    new dojo.dnd.Moveable(node, { handle: this.titleBar, timeout: 0 });
    this.connect(this._moveable, "onMoveStop", "_endDrag");
  }else{
    dojo.addClass(node,"dijitDialogFixed");
  }
  this.underlayAttrs = {
    dialogId: this.id,
    "class": dojo.map(this["class"].split(/\s/), function(s){ return s+"_underlay"; }).join(" ")
  };
},

_position: function(){
  // summary:
  //        Position modal dialog in the viewport. If no relative offset
  //        in the viewport has been determined (by dragging, for instance),
  //        center the node. Otherwise, use the Dialog's stored relative offset,
  //        and position the node to top: left: values based on the viewport.
  // tags:
  //        private
  if(!dojo.hasClass(dojo.body(), "dojoMove")){    // don't do anything if called during auto-scroll
    var node = this.domNode,
    viewport = dojo.window.getBox(),
    p = this._relativePosition,
    bb = p ? null : dojo._getBorderBox(node),
    l = Math.floor(viewport.l + (p ? p.x : (viewport.w - bb.w) / 2)),
    t = Math.floor(viewport.t + (p ? p.y : (viewport.h - bb.h) / 2));
    dojo.style(node,{
      left: l + "px",
      top: t + "px"
    });
  }
},

destroy: function(){
    if(this._fadeInDeferred){
        this._fadeInDeferred.cancel();
    }
    if(this._fadeOutDeferred){
        this._fadeOutDeferred.cancel();
    }
    if(this._moveable){
        this._moveable.destroy();
    }
    dojo.forEach(this._modalconnects, dojo.disconnect);

    dijit._DialogLevelManager.hide(this);

    this.inherited(arguments);
}

I also decided to keep the calculations within the _endDrag function rather than _position so that IF one need to show/load the dialog outside of the viewport to make it “slide in” with an animation it is still possible. With this new system, only dropping the dialog outside the viewport recalculates it’s position. So far it seems to work with several dialogs and even with scrolls.

You can see a test case here : http://dojo-sandbox.net/public/427eb/7

Share and Enjoy

This entry was posted in Dojo and tagged , , , , , , . Bookmark the permalink.

8 Responses to dijit.Dialog out and within the boundaries…

  1. PEM says:

    Thank you guys on #dojo freenode for pointing at some updates to do on the code :)
    WP had thrown in some & instead of & should be corrected by now, thanks @steveoh and the destroy function has changed back to it’s former state, so no need to show it anymore, no change have been made to it in the current version fo the patch, thanks @kgf :)

  2. PEM says:

    Actually, I was wrong when I said the destroy changed back to it’s former state, I removed the unsubscribe that was on _dndListener, since there is no longer _dndListener :)
    So i’ve edited yet again my code and put back the modification on destroy !

    The fix is being shiopped with 1.7, you can already benefit from it form the 1.7.0b version available on dojotoolkit.org ! :)

  3. cbarrett says:

    Very nice PEM, congrats on your fix! :)

  4. cax says:

    So the following bug is fixed now ?
    http://bugs.dojotoolkit.org/ticket/10117

    • PEM says:

      Yes it is, starting with 1.7 releases :)
      Sorry for the late answer, your comment was in between spam comments, did not see it earlier ! thank you for reading and commenting :)
      For earlier versions of Dojo, you might have to apply the patch yourself.

  5. Prem says:

    Pem,I am using dojo 1.7 now.In that i have implemented popup using dijit/dialog.But still while dragging the dialog,it goes out of the viewport but repositioning is not working for me.What might be the issue?

    • PEM says:

      you are using a stock dialog, or your own one? are you inheriting from 1.7.x dialog? are you overriding the position functions ? Have you tried with a normal Dijit/Dialog ?

Leave a Reply

Your email address will not be published. Required fields are marked *