devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
552 lines (482 loc) • 17.2 kB
JavaScript
"use strict";
var $ = require("../core/renderer"),
window = require("../core/utils/window").getWindow(),
devices = require("../core/devices"),
registerComponent = require("../core/component_registrator"),
extend = require("../core/utils/extend").extend,
Widget = require("./widget/ui.widget"),
Button = require("./button"),
Popover = require("./popover"),
DataHelperMixin = require("../data_helper"),
List = require("./list"),
themes = require("./themes"),
ChildDefaultTemplate = require("./widget/child_default_template");
var DROP_DOWN_MENU_CLASS = "dx-dropdownmenu",
DROP_DOWN_MENU_POPUP_CLASS = "dx-dropdownmenu-popup",
DROP_DOWN_MENU_POPUP_WRAPPER_CLASS = "dx-dropdownmenu-popup-wrapper",
DROP_DOWN_MENU_LIST_CLASS = "dx-dropdownmenu-list",
DROP_DOWN_MENU_BUTTON_CLASS = "dx-dropdownmenu-button";
var POPUP_OPTION_MAP = {
"popupWidth": "width",
"popupHeight": "height",
"popupMaxHeight": "maxHeight"
};
var BUTTON_OPTION_MAP = {
"buttonIcon": "icon",
"buttonText": "text",
"buttonWidth": "width",
"buttonHeight": "height",
"buttonTemplate": "template"
};
/**
* @name dxdropdownmenu
* @publicName dxDropDownMenu
* @inherits Widget
* @module ui/drop_down_menu
* @export default
* @hidden
*/
var DropDownMenu = Widget.inherit({
_supportedKeys: function _supportedKeys() {
var extension = {};
if (!this.option("opened") || !this._list.option("focusedElement")) {
extension = this._button._supportedKeys();
}
return extend(this.callBase(), extension, {
tab: function tab() {
this._popup && this._popup.hide();
}
});
},
_getDefaultOptions: function _getDefaultOptions() {
return extend(this.callBase(), {
/**
* @name dxDropDownMenuOptions.items
* @publicName items
* @type Array<Object>
*/
items: [],
/**
* @name dxDropDownMenuOptions.onItemClick
* @publicName onItemClick
* @type function(e)|string
* @extends Action
* @type_function_param1 e:object
* @type_function_param1_field4 itemData:object
* @type_function_param1_field5 itemElement:dxElement
* @type_function_param1_field6 itemIndex:number
* @action
*/
onItemClick: null,
/**
* @name dxDropDownMenuOptions.dataSource
* @publicName dataSource
* @type string|Array<Object>|DataSource|DataSourceOptions
* @default null
*/
dataSource: null,
/**
* @name dxDropDownMenuOptions.itemTemplate
* @publicName itemTemplate
* @type template|function
* @default "item"
* @type_function_param1 itemData:object
* @type_function_param2 itemIndex:number
* @type_function_param3 itemElement:dxElement
* @type_function_return string|Node|jQuery
*/
itemTemplate: "item",
/**
* @name dxDropDownMenuOptions.buttontext
* @publicName buttonText
* @type string
* @default ""
*/
buttonText: "",
/**
* @name dxDropDownMenuOptions.buttonIcon
* @publicName buttonIcon
* @type string
* @default "overflow"
*/
buttonIcon: "overflow",
buttonWidth: undefined,
buttonHeight: undefined,
buttonTemplate: "content",
/**
* @name dxDropDownMenuOptions.onButtonClick
* @publicName onButtonClick
* @type function(e)|string
* @extends Action
* @type_function_param1 e:object
* @type_function_param1_field4 jQueryEvent:jQuery.Event:deprecated(event)
* @type_function_param1_field5 event:event
* @action
*/
onButtonClick: null,
/**
* @name dxDropDownMenuOptions.usePopover
* @publicName usePopover
* @type boolean
* @default false
*/
usePopover: false,
/**
* @name dxDropDownMenuOptions.popupWidth
* @publicName popupWidth
* @type number|string|function
* @default auto
*/
popupWidth: "auto",
/**
* @name dxDropDownMenuOptions.popupHeight
* @publicName popupHeight
* @type number|string|function
* @default auto
*/
popupHeight: "auto",
/**
* @name dxDropDownMenuOptions.activeStateEnabled
* @publicName activeStateEnabled
* @type boolean
* @default true
* @inheritdoc
*/
activeStateEnabled: true,
/**
* @name dxDropDownMenuOptions.hoverStateEnabled
* @publicName hoverStateEnabled
* @type boolean
* @default true
* @inheritdoc
*/
hoverStateEnabled: true,
/**
* @name dxDropDownMenuOptions.opened
* @publicName opened
* @type boolean
* @default false
*/
opened: false,
deferRendering: false,
popupPosition: { my: "top center", at: "bottom center", collision: "fit flip", offset: { v: 1 } },
popupAnimation: undefined,
onItemRendered: null,
menuWidget: List,
popupMaxHeight: undefined,
closeOnClick: true,
useInkRipple: false
});
},
_defaultOptionsRules: function _defaultOptionsRules() {
return this.callBase().concat([{
device: { platform: "ios" },
options: {
usePopover: true
}
}, {
device: { platform: "generic" },
options: {
popupPosition: { offset: { v: 4 } }
}
}, {
device: function device() {
return devices.real().deviceType === "desktop" && !devices.isSimulator();
},
options: {
focusStateEnabled: true
}
}, {
device: { platform: "android" },
options: {
popupPosition: {
my: "top " + (this.option("rtlEnabled") ? "left" : "right"),
at: "top " + (this.option("rtlEnabled") ? "left" : "right"),
collision: "flipfit"
},
popupAnimation: {
show: {
type: "pop",
duration: 200,
from: { scale: 0 },
to: { scale: 1 }
},
hide: {
type: "pop",
duration: 200,
from: { scale: 1 },
to: { scale: 0 }
}
}
}
}, {
device: function device() {
return themes.isMaterial();
},
options: {
useInkRipple: true
}
}]);
},
_initOptions: function _initOptions(options) {
if (devices.current().platform === "android") {
if (!options.popupPosition) {
options.popupPosition = {
at: (options.usePopover ? "bottom " : "top ") + (options.rtlEnabled ? "left" : "right")
};
}
}
this.callBase(options);
},
_dataSourceOptions: function _dataSourceOptions() {
return {
paginate: false
};
},
_init: function _init() {
this.callBase();
this.$element().addClass(DROP_DOWN_MENU_CLASS);
this._initDataSource();
this._initItemClickAction();
this._initButtonClickAction();
},
_initItemClickAction: function _initItemClickAction() {
this._itemClickAction = this._createActionByOption("onItemClick");
},
_initButtonClickAction: function _initButtonClickAction() {
this._buttonClickAction = this._createActionByOption("onButtonClick");
},
_initTemplates: function _initTemplates() {
this.callBase();
this._defaultTemplates["item"] = new ChildDefaultTemplate("item", this);
this._defaultTemplates["content"] = new ChildDefaultTemplate("content", this);
},
_initMarkup: function _initMarkup() {
this._renderButton();
this.callBase();
},
_render: function _render() {
this.callBase();
this.setAria({
"role": "menubar",
"haspopup": true,
"expanded": this.option("opened")
});
},
_renderContentImpl: function _renderContentImpl() {
if (this.option("opened")) {
this._renderPopup();
}
},
_clean: function _clean() {
this._cleanFocusState();
if (this._popup) {
this._popup.$element().remove();
delete this._$popup;
}
},
_renderButton: function _renderButton() {
var $button = this.$element().addClass(DROP_DOWN_MENU_BUTTON_CLASS),
config = this._buttonOptions();
this._button = this._createComponent($button, Button, config);
},
_toggleActiveState: function _toggleActiveState($element, value, e) {
this._button._toggleActiveState($element, value, e);
},
_buttonOptions: function _buttonOptions() {
return {
text: this.option("buttonText"),
icon: this.option("buttonIcon"),
width: this.option("buttonWidth"),
height: this.option("buttonHeight"),
useInkRipple: this.option("useInkRipple"),
template: this.option("buttonTemplate"),
focusStateEnabled: false,
onClick: function (e) {
this.option("opened", !this.option("opened"));
this._buttonClickAction(e);
}.bind(this)
};
},
_toggleMenuVisibility: function _toggleMenuVisibility(opened) {
var state = opened === undefined ? !this._popup.option("visible") : opened;
if (opened) {
this._renderPopup();
}
this._popup.toggle(state);
this.setAria("expanded", state);
},
_renderPopup: function _renderPopup() {
if (this._$popup) {
return;
}
var $popup = this._$popup = $("<div>").appendTo(this.$element()),
config = this._popupOptions();
this._popup = this._createComponent($popup, Popover, config); // TODO: Circular dep
},
_popupOptions: function _popupOptions() {
var usePopup = !this.option("usePopover");
return {
onInitialized: function onInitialized(args) {
args.component._wrapper().addClass(DROP_DOWN_MENU_POPUP_WRAPPER_CLASS).toggleClass(DROP_DOWN_MENU_POPUP_CLASS, usePopup);
},
visible: this.option("opened"),
deferRendering: false,
contentTemplate: function (contentElement) {
this._renderList(contentElement);
}.bind(this),
position: this.option("popupPosition"),
animation: this.option("popupAnimation"),
onOptionChanged: function (args) {
if (args.name === "visible") {
this.option("opened", args.value);
}
}.bind(this),
target: this.$element(),
height: this.option("popupHeight"),
width: this.option("popupWidth"),
maxHeight: this.option("popupMaxHeight")
};
},
_renderList: function _renderList(contentElement) {
var $content = $(contentElement),
listConfig = this._listOptions();
$content.addClass(DROP_DOWN_MENU_LIST_CLASS);
this._list = this._createComponent($content, this.option("menuWidget"), listConfig);
// todo: replace with option
this._list._getAriaTarget = function () {
return this.$element();
}.bind(this);
this._setListDataSource();
var listMaxHeight = $(window).height() * 0.5;
if ($content.height() > listMaxHeight) {
$content.height(listMaxHeight);
}
},
_listOptions: function _listOptions() {
return {
_keyboardProcessor: this._listProcessor,
pageLoadMode: "scrollBottom",
indicateLoading: false,
noDataText: "",
itemTemplate: this._getTemplateByOption("itemTemplate"),
onItemClick: function (e) {
if (this.option("closeOnClick")) {
this.option("opened", false);
}
this._itemClickAction(e);
}.bind(this),
tabIndex: -1,
focusStateEnabled: this.option("focusStateEnabled"),
activeStateEnabled: this.option("activeStateEnabled"),
onItemRendered: this.option("onItemRendered"),
_itemAttributes: { role: "menuitem" }
};
},
_setListDataSource: function _setListDataSource() {
if (this._list) {
this._list.option("dataSource", this._dataSource || this.option("items"));
}
delete this._deferRendering;
},
_attachKeyboardEvents: function _attachKeyboardEvents() {
this.callBase.apply(this, arguments);
this._listProcessor = this._keyboardProcessor && this._keyboardProcessor.attachChildProcessor();
if (this._list) {
this._list.option("_keyboardProcessor", this._listProcessor);
}
},
_cleanFocusState: function _cleanFocusState() {
this.callBase.apply(this, arguments);
delete this._listProcessor;
},
_toggleVisibility: function _toggleVisibility(visible) {
this.callBase(visible);
this._button.option("visible", visible);
},
_optionChanged: function _optionChanged(args) {
var name = args.name;
var value = args.value;
switch (name) {
case "items":
case "dataSource":
if (this.option("deferRendering") && !this.option("opened")) {
this._deferRendering = true;
} else {
this._refreshDataSource();
this._setListDataSource();
}
break;
case "itemTemplate":
if (this._list) {
this._list.option(name, this._getTemplate(value));
}
break;
case "onItemClick":
this._initItemClickAction();
break;
case "onButtonClick":
this._buttonClickAction();
break;
case "buttonIcon":
case "buttonText":
case "buttonWidth":
case "buttonHeight":
case "buttonTemplate":
this._button.option(BUTTON_OPTION_MAP[name], value);
this._renderPopup();
break;
case "popupWidth":
case "popupHeight":
case "popupMaxHeight":
this._popup.option(POPUP_OPTION_MAP[name], value);
break;
case "usePopover":
case "menuWidget":
case "useInkRipple":
this._invalidate();
break;
case "focusStateEnabled":
case "activeStateEnabled":
if (this._list) {
this._list.option(name, value);
}
this.callBase(args);
break;
case "onItemRendered":
if (this._list) {
this._list.option(name, value);
}
break;
case "opened":
if (this._deferRendering) {
this._refreshDataSource();
this._setListDataSource();
}
this._toggleMenuVisibility(value);
break;
case "deferRendering":
case "popupPosition":
case "closeOnClick":
break;
default:
this.callBase(args);
}
},
/**
* @name dxDropDownMenuMethods.open
* @publicName open()
*/
open: function open() {
this.option("opened", true);
},
/**
* @name dxDropDownMenuMethods.close
* @publicName close()
*/
close: function close() {
this.option("opened", false);
}
}).include(DataHelperMixin);
registerComponent("dxDropDownMenu", DropDownMenu);
module.exports = DropDownMenu;