You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
297 lines
9.5 KiB
297 lines
9.5 KiB
define("dojox/layout/ToggleSplitter", ["dojo", "dijit", "dijit/layout/BorderContainer"], function(dojo, dijit) { |
|
|
|
dojo.experimental("dojox.layout.ToggleSplitter"); |
|
|
|
var ToggleSplitter = dojo.declare("dojox.layout.ToggleSplitter", dijit.layout._Splitter, { |
|
// summary: |
|
// A draggable and clickable spacer between two items in a `dijit.layout.BorderContainer`. |
|
// description: |
|
// This is instantiated by `dijit.layout.BorderContainer`. Users should not |
|
// create it directly. |
|
// tags: |
|
// private |
|
|
|
|
|
// container: [const] dijit/layout/BorderContainer |
|
// Pointer to the parent BorderContainer |
|
container: null, |
|
|
|
// child: [const] dijit/layout/_LayoutWidget |
|
// Pointer to the pane associated with this splitter |
|
child: null, |
|
|
|
// region: [const] String |
|
// Region of pane associated with this splitter. |
|
// "top", "bottom", "left", "right". |
|
region: null, |
|
|
|
// state: String |
|
// the initial and current state of the splitter (and its attached pane) |
|
// It has three values: full, collapsed (optional), closed |
|
state: "full", |
|
|
|
// _closedSize: String |
|
// the css height/width value to apply by default when the attached pane is closed |
|
_closedSize: "0", |
|
|
|
baseClass: "dojoxToggleSplitter", |
|
|
|
templateString: '<div class="dijitSplitter dojoxToggleSplitter" dojoAttachEvent="onkeypress:_onKeyPress,onmousedown:_startDrag,onmouseenter:_onMouse,onmouseleave:_onMouse">' + |
|
'<div dojoAttachPoint="toggleNode" class="dijitSplitterThumb dojoxToggleSplitterIcon" tabIndex="0" role="separator" ' + |
|
'dojoAttachEvent="onmousedown:_onToggleNodeMouseDown,onclick:_toggle,onmouseenter:_onToggleNodeMouseMove,onmouseleave:_onToggleNodeMouseMove,onfocus:_onToggleNodeMouseMove,onblur:_onToggleNodeMouseMove">' + |
|
'<span class="dojoxToggleSplitterA11y" dojoAttachPoint="a11yText"></span></div>' + |
|
'</div>', |
|
|
|
postCreate: function(){ |
|
this.inherited(arguments); |
|
|
|
// add a region css hook so that it can figure out the region correctly |
|
var region = this.region; |
|
dojo.addClass(this.domNode, this.baseClass + region.charAt(0).toUpperCase() + region.substring(1)); |
|
}, |
|
|
|
startup: function(){ |
|
this.inherited(arguments); |
|
|
|
// we have to wait until startup to be sure the child exists in the dom |
|
// and has non-zero size (if its supposed to be showing) |
|
var parentPane = this.child, |
|
paneNode = this.child.domNode, |
|
intPaneSize = dojo.style(paneNode, (this.horizontal ? "height" : "width")); |
|
|
|
this.domNode.setAttribute("aria-controls", paneNode.id); |
|
|
|
// creation of splitters is an opaque process in BorderContainer, |
|
// so if we want to get init params, we have to retrieve them from the attached BC child |
|
// NOTE: for this to work we have to extend the prototype of dijit._Widget (some more) |
|
dojo.forEach(["toggleSplitterState", "toggleSplitterFullSize", "toggleSplitterCollapsedSize"], function(name){ |
|
var pname = name.substring("toggleSplitter".length); |
|
pname = pname.charAt(0).toLowerCase() + pname.substring(1); |
|
if(name in this.child){ |
|
this[pname] = this.child[name]; |
|
} |
|
}, this); |
|
|
|
if(!this.fullSize){ |
|
// Store the current size as the fullSize if none was provided |
|
// dojo.style always returns a integer (pixel) value for height/width |
|
// use an arbitrary default if a pane was initialized closed and no fullSize provided |
|
// If collapsedSize is not specified, collapsed state does not exist. |
|
this.fullSize = this.state == "full" ? intPaneSize + "px" : "75px"; |
|
} |
|
|
|
this._openStyleProps = this._getStyleProps(paneNode, "full"); |
|
|
|
// update state |
|
this._started = true; |
|
this.set("state", this.state); |
|
|
|
return this; |
|
}, |
|
|
|
_onKeyPress: function(evt){ |
|
if(this.state == "full"){ |
|
this.inherited(arguments); |
|
} |
|
if(evt.charCode == dojo.keys.SPACE || evt.keyCode == dojo.keys.ENTER){ |
|
this._toggle(evt); |
|
dojo.stopEvent(evt); |
|
} |
|
}, |
|
|
|
_onToggleNodeMouseDown: function(evt){ |
|
dojo.stopEvent(evt); |
|
this.toggleNode.focus(); |
|
}, |
|
|
|
_startDrag: function(e){ |
|
if(this.state == "full"){ |
|
this.inherited(arguments); |
|
} |
|
}, |
|
|
|
_stopDrag: function(e){ |
|
this.inherited(arguments); |
|
this.toggleNode.blur(); |
|
}, |
|
|
|
_toggle: function(evt){ |
|
var state; |
|
switch(this.state){ |
|
case "full": |
|
state = this.collapsedSize ? "collapsed" : "closed"; |
|
break; |
|
case "collapsed": |
|
state = "closed"; |
|
break; |
|
default: |
|
state = "full"; |
|
} |
|
this.set("state", state); |
|
}, |
|
|
|
_onToggleNodeMouseMove: function(evt){ |
|
var baseClass = this.baseClass, |
|
toggleNode = this.toggleNode, |
|
on = this.state == "full" || this.state == "collapsed", |
|
leave = evt.type == "mouseout" || evt.type == "blur"; |
|
|
|
dojo.toggleClass(toggleNode, baseClass + "IconOpen", leave && on); |
|
dojo.toggleClass(toggleNode, baseClass + "IconOpenHover", !leave && on); |
|
dojo.toggleClass(toggleNode, baseClass + "IconClosed", leave && !on); |
|
dojo.toggleClass(toggleNode, baseClass + "IconClosedHover", !leave && !on); |
|
}, |
|
|
|
_handleOnChange: function(preState){ |
|
// summary: |
|
// Effect the state change with the new value of this.state |
|
var paneNode = this.child.domNode, |
|
openProps, paneStyle, |
|
dim = this.horizontal ? "height" : "width"; |
|
|
|
if(this.state == "full"){ |
|
// change to full open state |
|
var styleProps = dojo.mixin({ |
|
display: "block", |
|
overflow: "auto", |
|
visibility: "visible" |
|
}, this._openStyleProps); |
|
styleProps[dim] = (this._openStyleProps && this._openStyleProps[dim]) ? this._openStyleProps[dim] : this.fullSize; |
|
|
|
dojo.style(this.domNode, "cursor", ""); |
|
dojo.style(paneNode, styleProps); |
|
}else if(this.state == "collapsed"){ |
|
paneStyle = dojo.getComputedStyle(paneNode); |
|
openProps = this._getStyleProps(paneNode, "full", paneStyle); |
|
this._openStyleProps = openProps; |
|
|
|
dojo.style(this.domNode, "cursor", "auto"); |
|
dojo.style(paneNode, dim, this.collapsedSize); |
|
}else{ |
|
// change to closed state |
|
if(!this.collapsedSize){ |
|
paneStyle = dojo.getComputedStyle(paneNode); |
|
openProps = this._getStyleProps(paneNode, "full", paneStyle); |
|
this._openStyleProps = openProps; |
|
} |
|
var closedProps = this._getStyleProps(paneNode, "closed", paneStyle); |
|
|
|
dojo.style(this.domNode, "cursor", "auto"); |
|
dojo.style(paneNode, closedProps); |
|
} |
|
this._setStateClass(); |
|
if(this.container._started){ |
|
this.container._layoutChildren(this.region); |
|
} |
|
}, |
|
|
|
_getStyleProps: function(paneNode, state, paneStyle){ |
|
// summary: |
|
// Create an object with the style property name: values |
|
// that will need to be applied to the child pane render the given state |
|
if(!paneStyle){ |
|
paneStyle = dojo.getComputedStyle(paneNode); |
|
} |
|
var styleProps = {}, |
|
dim = this.horizontal ? "height" : "width"; |
|
|
|
styleProps["overflow"] = (state != "closed") ? paneStyle["overflow"] : "hidden"; |
|
styleProps["visibility"] = (state != "closed") ? paneStyle["visibility"] : "hidden"; |
|
|
|
// Use the inline width/height style value, in preference to the computedStyle |
|
// for the open width/height |
|
styleProps[dim] = (state != "closed") ? paneNode.style[dim] || paneStyle[dim] : this._closedSize; |
|
|
|
// We include the padding, border, margin width values for restoring on state full open |
|
var edgeNames = ["Top", "Right", "Bottom", "Left"]; |
|
dojo.forEach(["padding", "margin", "border"], function(pname){ |
|
for(var i = 0; i < edgeNames.length; i++){ |
|
var fullName = pname + edgeNames[i]; |
|
if(pname == "border"){ |
|
fullName += "Width"; |
|
} |
|
if(undefined !== paneStyle[fullName]){ |
|
styleProps[fullName] = (state != "closed") ? paneStyle[fullName] : 0; |
|
} |
|
} |
|
}); |
|
|
|
return styleProps; |
|
}, |
|
|
|
_setStateClass: function(){ |
|
// summary: |
|
// Apply the appropriate classes for the current open state |
|
var arrow = "▴", region = this.region.toLowerCase(), |
|
baseClass = this.baseClass, |
|
toggleNode = this.toggleNode, |
|
on = this.state == "full" || this.state == "collapsed", |
|
focused = this.focused; |
|
|
|
dojo.toggleClass(toggleNode, baseClass + "IconOpen", on && !focused); |
|
dojo.toggleClass(toggleNode, baseClass + "IconClosed", !on && !focused); |
|
dojo.toggleClass(toggleNode, baseClass + "IconOpenHover", on && focused); |
|
dojo.toggleClass(toggleNode, baseClass + "IconClosedHover", !on && focused); |
|
|
|
// For a11y |
|
if(region == "top" && on || region == "bottom" && !on){ |
|
arrow = "▲"; |
|
}else if(region == "top" && !on || region == "bottom" && on){ |
|
arrow = "▼"; |
|
}else if(region == "right" && on || region == "left" && !on){ |
|
arrow = "▶"; |
|
}else if(region == "right" && !on || region == "left" && on){ |
|
arrow = "◀"; |
|
} |
|
|
|
this.a11yText.innerHTML = arrow; |
|
}, |
|
|
|
_setStateAttr: function(/*String*/ state){ |
|
// summary: |
|
// setter for the state property |
|
if(!this._started) { |
|
return; |
|
} |
|
var preState = this.state; |
|
this.state = state; |
|
|
|
this._handleOnChange(preState); |
|
var evtName; |
|
switch(state){ |
|
case "full": |
|
this.domNode.setAttribute("aria-expanded", true); |
|
evtName = "onOpen"; |
|
break; |
|
case "collapsed": |
|
this.domNode.setAttribute("aria-expanded", true); |
|
evtName = "onCollapsed"; |
|
break; |
|
default: |
|
this.domNode.setAttribute("aria-expanded", false); |
|
evtName = "onClosed"; |
|
} |
|
this[evtName](this.child); |
|
}, |
|
|
|
onOpen: function(pane){ /*Stub*/ }, |
|
onCollapsed: function(pane){ /*Stub*/ }, |
|
onClosed: function(pane){ /*Stub*/ } |
|
}); |
|
|
|
// As BC places no constraints on what kind of widgets can be children |
|
// we have to extend the base class to ensure the properties we need can be set (both in markup and programatically) |
|
dojo.extend(dijit._Widget, { |
|
// toggleSplitterOpen: Boolean |
|
toggleSplitterState: "full", |
|
|
|
// toggleSplitterClosedThreshold: String |
|
// A css size value (e.g. "100px") |
|
toggleSplitterFullSize: "", |
|
|
|
toggleSplitterCollapsedSize: "" |
|
}); |
|
|
|
return ToggleSplitter; |
|
|
|
});
|
|
|