You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
312 lines
9.4 KiB
JavaScript
312 lines
9.4 KiB
JavaScript
dojo.provide("dijit.layout.AccordionContainer");
|
|
|
|
dojo.require("dojo.fx");
|
|
|
|
dojo.require("dijit._Container");
|
|
dojo.require("dijit._Templated");
|
|
dojo.require("dijit.layout.StackContainer");
|
|
dojo.require("dijit.layout.ContentPane");
|
|
|
|
dojo.require("dijit.layout.AccordionPane"); // for back compat
|
|
|
|
dojo.declare(
|
|
"dijit.layout.AccordionContainer",
|
|
dijit.layout.StackContainer,
|
|
{
|
|
// summary:
|
|
// Holds a set of panes where every pane's title is visible, but only one pane's content is visible at a time,
|
|
// and switching between panes is visualized by sliding the other panes up/down.
|
|
// example:
|
|
// | <div dojoType="dijit.layout.AccordionContainer">
|
|
// | <div dojoType="dijit.layout.AccordionPane" title="pane 1">
|
|
// | <div dojoType="dijit.layout.ContentPane">...</div>
|
|
// | </div>
|
|
// | <div dojoType="dijit.layout.AccordionPane" title="pane 2">
|
|
// | <p>This is some text</p>
|
|
// || ...
|
|
// | </div>
|
|
|
|
// duration: Integer
|
|
// Amount of time (in ms) it takes to slide panes
|
|
duration: dijit.defaultDuration,
|
|
|
|
// _verticalSpace: Number
|
|
// Pixels of space available for the open pane
|
|
// (my content box size minus the cumulative size of all the title bars)
|
|
_verticalSpace: 0,
|
|
|
|
baseClass: "dijitAccordionContainer",
|
|
|
|
postCreate: function(){
|
|
this.domNode.style.overflow = "hidden";
|
|
this.inherited(arguments);
|
|
dijit.setWaiRole(this.domNode, "tablist");
|
|
},
|
|
|
|
startup: function(){
|
|
if(this._started){ return; }
|
|
this.inherited(arguments);
|
|
if(this.selectedChildWidget){
|
|
var style = this.selectedChildWidget.containerNode.style;
|
|
style.display = "";
|
|
style.overflow = "auto";
|
|
this.selectedChildWidget._buttonWidget._setSelectedState(true);
|
|
}
|
|
},
|
|
|
|
_getTargetHeight: function(/* Node */ node){
|
|
// summary:
|
|
// For the given node, returns the height that should be
|
|
// set to achieve our vertical space (subtract any padding
|
|
// we may have).
|
|
//
|
|
// This is used by the animations.
|
|
//
|
|
// TODO: I don't think this works correctly in IE quirks when an elements
|
|
// style.height including padding and borders
|
|
var cs = dojo.getComputedStyle(node);
|
|
return Math.max(this._verticalSpace - dojo._getPadBorderExtents(node, cs).h, 0);
|
|
},
|
|
|
|
layout: function(){
|
|
// Implement _LayoutWidget.layout() virtual method.
|
|
// Set the height of the open pane based on what room remains.
|
|
|
|
var openPane = this.selectedChildWidget;
|
|
|
|
// get cumulative height of all the title bars
|
|
var totalCollapsedHeight = 0;
|
|
dojo.forEach(this.getChildren(), function(child){
|
|
totalCollapsedHeight += child._buttonWidget.getTitleHeight();
|
|
});
|
|
var mySize = this._contentBox;
|
|
this._verticalSpace = mySize.h - totalCollapsedHeight;
|
|
|
|
// Memo size to make displayed child
|
|
this._containerContentBox = {
|
|
h: this._verticalSpace,
|
|
w: mySize.w
|
|
};
|
|
|
|
if(openPane){
|
|
openPane.resize(this._containerContentBox);
|
|
}
|
|
},
|
|
|
|
_setupChild: function(child){
|
|
// Overrides _LayoutWidget._setupChild().
|
|
// Setup clickable title to sit above the child widget,
|
|
// and stash pointer to it inside the widget itself.
|
|
|
|
child._buttonWidget = new dijit.layout._AccordionButton({
|
|
contentWidget: child,
|
|
title: child.title,
|
|
id: child.id + "_button",
|
|
parent: this
|
|
});
|
|
dojo.place(child._buttonWidget.domNode, child.domNode, "before");
|
|
|
|
this.inherited(arguments);
|
|
},
|
|
|
|
removeChild: function(child){
|
|
// Overrides _LayoutWidget.removeChild().
|
|
child._buttonWidget.destroy();
|
|
this.inherited(arguments);
|
|
},
|
|
|
|
getChildren: function(){
|
|
// Overrides _Container.getChildren() to ignore titles and only look at panes.
|
|
return dojo.filter(this.inherited(arguments), function(child){
|
|
return child.declaredClass != "dijit.layout._AccordionButton";
|
|
});
|
|
},
|
|
|
|
destroy: function(){
|
|
dojo.forEach(this.getChildren(), function(child){
|
|
child._buttonWidget.destroy();
|
|
});
|
|
this.inherited(arguments);
|
|
},
|
|
|
|
_transition: function(/*Widget?*/newWidget, /*Widget?*/oldWidget){
|
|
// Overrides StackContainer._transition() to provide sliding of title bars etc.
|
|
|
|
//TODO: should be able to replace this with calls to slideIn/slideOut
|
|
if(this._inTransition){ return; }
|
|
this._inTransition = true;
|
|
var animations = [];
|
|
var paneHeight = this._verticalSpace;
|
|
if(newWidget){
|
|
newWidget._buttonWidget.setSelected(true);
|
|
|
|
this._showChild(newWidget); // prepare widget to be slid in
|
|
|
|
// Size the new widget, in case this is the first time it's being shown,
|
|
// or I have been resized since the last time it was shown.
|
|
// Note that page must be visible for resizing to work.
|
|
if(this.doLayout && newWidget.resize){
|
|
newWidget.resize(this._containerContentBox);
|
|
}
|
|
|
|
var newContents = newWidget.domNode;
|
|
dojo.addClass(newContents, "dijitVisible");
|
|
dojo.removeClass(newContents, "dijitHidden");
|
|
var newContentsOverflow = newContents.style.overflow;
|
|
newContents.style.overflow = "hidden";
|
|
animations.push(dojo.animateProperty({
|
|
node: newContents,
|
|
duration: this.duration,
|
|
properties: {
|
|
height: { start: 1, end: this._getTargetHeight(newContents) }
|
|
},
|
|
onEnd: dojo.hitch(this, function(){
|
|
newContents.style.overflow = newContentsOverflow;
|
|
delete this._inTransition;
|
|
})
|
|
}));
|
|
}
|
|
if(oldWidget){
|
|
oldWidget._buttonWidget.setSelected(false);
|
|
var oldContents = oldWidget.domNode,
|
|
oldContentsOverflow = oldContents.style.overflow;
|
|
oldContents.style.overflow = "hidden";
|
|
animations.push(dojo.animateProperty({
|
|
node: oldContents,
|
|
duration: this.duration,
|
|
properties: {
|
|
height: { start: this._getTargetHeight(oldContents), end: 1 }
|
|
},
|
|
onEnd: function(){
|
|
dojo.addClass(oldContents, "dijitHidden");
|
|
dojo.removeClass(oldContents, "dijitVisible");
|
|
oldContents.style.overflow = oldContentsOverflow;
|
|
if(oldWidget.onHide){
|
|
oldWidget.onHide();
|
|
}
|
|
}
|
|
}));
|
|
}
|
|
|
|
dojo.fx.combine(animations).play();
|
|
},
|
|
|
|
// note: we are treating the container as controller here
|
|
_onKeyPress: function(/*Event*/ e, /*Widget*/ fromTitle){
|
|
// summary:
|
|
// Handle keypress events
|
|
// description:
|
|
// This is called from a handler on AccordionContainer.domNode
|
|
// (setup in StackContainer), and is also called directly from
|
|
// the click handler for accordion labels
|
|
if(this._inTransition || this.disabled || e.altKey || !(fromTitle || e.ctrlKey)){
|
|
if(this._inTransition){
|
|
dojo.stopEvent(e);
|
|
}
|
|
return;
|
|
}
|
|
var k = dojo.keys,
|
|
c = e.charOrCode;
|
|
if((fromTitle && (c == k.LEFT_ARROW || c == k.UP_ARROW)) ||
|
|
(e.ctrlKey && c == k.PAGE_UP)){
|
|
this._adjacent(false)._buttonWidget._onTitleClick();
|
|
dojo.stopEvent(e);
|
|
}else if((fromTitle && (c == k.RIGHT_ARROW || c == k.DOWN_ARROW)) ||
|
|
(e.ctrlKey && (c == k.PAGE_DOWN || c == k.TAB))){
|
|
this._adjacent(true)._buttonWidget._onTitleClick();
|
|
dojo.stopEvent(e);
|
|
}
|
|
}
|
|
}
|
|
);
|
|
|
|
dojo.declare("dijit.layout._AccordionButton",
|
|
[dijit._Widget, dijit._Templated],
|
|
{
|
|
// summary:
|
|
// The title bar to click to open up an accordion pane.
|
|
// Internal widget used by AccordionContainer.
|
|
// tags:
|
|
// private
|
|
|
|
templatePath: dojo.moduleUrl("dijit.layout", "templates/AccordionButton.html"),
|
|
attributeMap: dojo.mixin(dojo.clone(dijit.layout.ContentPane.prototype.attributeMap), {
|
|
title: {node: "titleTextNode", type: "innerHTML" }
|
|
}),
|
|
|
|
baseClass: "dijitAccordionTitle",
|
|
|
|
getParent: function(){
|
|
// summary:
|
|
// Returns the parent.
|
|
// tags:
|
|
// private
|
|
return this.parent;
|
|
},
|
|
|
|
postCreate: function(){
|
|
this.inherited(arguments);
|
|
dojo.setSelectable(this.domNode, false);
|
|
this.setSelected(this.selected);
|
|
var titleTextNodeId = dojo.attr(this.domNode,'id').replace(' ','_');
|
|
dojo.attr(this.titleTextNode, "id", titleTextNodeId+"_title");
|
|
dijit.setWaiState(this.focusNode, "labelledby", dojo.attr(this.titleTextNode, "id"));
|
|
},
|
|
|
|
getTitleHeight: function(){
|
|
// summary:
|
|
// Returns the height of the title dom node.
|
|
return dojo.marginBox(this.titleNode).h; // Integer
|
|
},
|
|
|
|
_onTitleClick: function(){
|
|
// summary:
|
|
// Callback when someone clicks my title.
|
|
var parent = this.getParent();
|
|
if(!parent._inTransition){
|
|
parent.selectChild(this.contentWidget);
|
|
dijit.focus(this.focusNode);
|
|
}
|
|
},
|
|
|
|
_onTitleEnter: function(){
|
|
// summary:
|
|
// Callback when someone hovers over my title.
|
|
dojo.addClass(this.focusNode, "dijitAccordionTitle-hover");
|
|
},
|
|
|
|
_onTitleLeave: function(){
|
|
// summary:
|
|
// Callback when someone stops hovering over my title.
|
|
dojo.removeClass(this.focusNode, "dijitAccordionTitle-hover");
|
|
},
|
|
|
|
_onTitleKeyPress: function(/*Event*/ evt){
|
|
return this.getParent()._onKeyPress(evt, this.contentWidget);
|
|
},
|
|
|
|
_setSelectedState: function(/*Boolean*/ isSelected){
|
|
this.selected = isSelected;
|
|
dojo[(isSelected ? "addClass" : "removeClass")](this.titleNode,"dijitAccordionTitle-selected");
|
|
dijit.setWaiState(this.focusNode, "expanded", isSelected);
|
|
dijit.setWaiState(this.focusNode, "selected", isSelected);
|
|
this.focusNode.setAttribute("tabIndex", isSelected ? "0" : "-1");
|
|
},
|
|
|
|
_handleFocus: function(/*Event*/e){
|
|
// summary:
|
|
// Handle the blur and focus state of this widget.
|
|
dojo[(e.type=="focus" ? "addClass" : "removeClass")](this.focusNode,"dijitAccordionFocused");
|
|
},
|
|
|
|
setSelected: function(/*Boolean*/ isSelected){
|
|
// summary:
|
|
// Change the selected state on this pane.
|
|
this._setSelectedState(isSelected);
|
|
if(isSelected){
|
|
var cw = this.contentWidget;
|
|
if(cw.onSelected){ cw.onSelected(); }
|
|
}
|
|
}
|
|
});
|