devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
791 lines (689 loc) • 25.4 kB
JavaScript
"use strict";
var $ = require("../core/renderer"),
window = require("../core/utils/window").getWindow(),
translator = require("../animation/translator"),
camelize = require("../core/utils/inflector").camelize,
noop = require("../core/utils/common").noop,
getPublicElement = require("../core/utils/dom").getPublicElement,
each = require("../core/utils/iterator").each,
isDefined = require("../core/utils/type").isDefined,
inArray = require("../core/utils/array").inArray,
extend = require("../core/utils/extend").extend,
messageLocalization = require("../localization/message"),
devices = require("../core/devices"),
registerComponent = require("../core/component_registrator"),
Button = require("./button"),
themes = require("./themes"),
Overlay = require("./overlay"),
EmptyTemplate = require("./widget/empty_template"),
domUtils = require("../core/utils/dom"),
windowUtils = require("../core/utils/window");
require("./toolbar/ui.toolbar.base");
var POPUP_CLASS = "dx-popup",
POPUP_WRAPPER_CLASS = "dx-popup-wrapper",
POPUP_FULL_SCREEN_CLASS = "dx-popup-fullscreen",
POPUP_FULL_SCREEN_WIDTH_CLASS = "dx-popup-fullscreen-width",
POPUP_NORMAL_CLASS = "dx-popup-normal",
POPUP_CONTENT_CLASS = "dx-popup-content",
POPUP_DRAGGABLE_CLASS = "dx-popup-draggable",
POPUP_TITLE_CLASS = "dx-popup-title",
POPUP_TITLE_CLOSEBUTTON_CLASS = "dx-closebutton",
POPUP_BOTTOM_CLASS = "dx-popup-bottom",
TEMPLATE_WRAPPER_CLASS = "dx-template-wrapper",
ALLOWED_TOOLBAR_ITEM_ALIASES = ["cancel", "clear", "done"];
var getButtonPlace = function getButtonPlace(name) {
var device = devices.current(),
platform = device.platform,
toolbar = "bottom",
location = "before";
if (platform === "ios") {
switch (name) {
case "cancel":
toolbar = "top";
break;
case "clear":
toolbar = "top";
location = "after";
break;
case "done":
location = "after";
break;
}
} else if (platform === "win") {
location = "after";
} else if (platform === "android" && device.version && parseInt(device.version[0]) > 4) {
switch (name) {
case "cancel":
location = "after";
break;
case "done":
location = "after";
break;
}
} else if (platform === "android") {
location = "center";
}
return {
toolbar: toolbar,
location: location
};
};
/**
* @name dxPopup
* @publicName dxPopup
* @inherits dxOverlay
* @module ui/popup
* @export default
*/
var Popup = Overlay.inherit({
_getDefaultOptions: function _getDefaultOptions() {
return extend(this.callBase(), {
/**
* @name dxPopupOptions.fullScreen
* @publicName fullScreen
* @type boolean
* @default false
*/
fullScreen: false,
/**
* @name dxPopupOptions.title
* @publicName title
* @type string
* @default ""
*/
title: "",
/**
* @name dxPopupOptions.showtitle
* @publicName showTitle
* @type boolean
* @default true
*/
showTitle: true,
/**
* @name dxPopupOptions.container
* @publicName container
* @type string|Node|jQuery
* @default undefined
*/
/**
* @name dxPopupOptions.titleTemplate
* @publicName titleTemplate
* @type template|function
* @default "title"
* @type_function_param1 titleElement:dxElement
* @type_function_return string|Node|jQuery
*/
titleTemplate: "title",
/**
* @name dxPopupOptions.onTitleRendered
* @publicName onTitleRendered
* @extends Action
* @type function(e)
* @type_function_param1 e:object
* @type_function_param1_field4 titleElement:dxElement
* @action
*/
onTitleRendered: null,
/**
* @name dxPopupOptions.dragEnabled
* @publicName dragEnabled
* @type boolean
* @default false
*/
dragEnabled: false,
/**
* @name dxPopupOptions.position
* @publicName position
* @type Enums.PositionAlignment|positionConfig|function
* @inheritdoc
*/
/**
* @name dxPopupOptions.resizeEnabled
* @publicName resizeEnabled
* @type boolean
* @default false
*/
/**
* @name dxPopupOptions.onResizeStart
* @publicName onResizeStart
* @extends Action
* @action
*/
/**
* @name dxPopupOptions.onResize
* @publicName onResize
* @extends Action
* @action
*/
/**
* @name dxPopupOptions.onResizeEnd
* @publicName onResizeEnd
* @extends Action
* @action
*/
/**
* @name dxPopupOptions.width
* @publicName width
* @fires dxPopupOptions.onResize
* @inheritdoc
*/
/**
* @name dxPopupOptions.height
* @publicName height
* @fires dxPopupOptions.onResize
* @inheritdoc
*/
/**
* @name dxPopupOptions.toolbarItems
* @publicName toolbarItems
* @type Array<Object>
*/
/**
* @name dxPopupOptions.toolbarItems.toolbar
* @publicName toolbar
* @type Enums.Toolbar
* @default 'top'
*/
/**
* @name dxPopupOptions.toolbarItems.html
* @publicName html
* @type String
*/
/**
* @name dxPopupOptions.toolbarItems.text
* @publicName text
* @type String
*/
/**
* @name dxPopupOptions.toolbarItems.visible
* @publicName visible
* @type boolean
* @default true
*/
/**
* @name dxPopupOptions.toolbarItems.disabled
* @publicName disabled
* @type boolean
* @default false
*/
/**
* @name dxPopupOptions.toolbarItems.template
* @publicName template
* @type template
*/
/**
* @name dxPopupOptions.toolbarItems.widget
* @publicName widget
* @type Enums.ToolbarItemWidget
*/
/**
* @name dxPopupOptions.toolbarItems.options
* @publicName options
* @type object
*/
/**
* @name dxPopupOptions.toolbarItems.location
* @publicName location
* @type Enums.ToolbarItemLocation
* @default 'center'
*/
toolbarItems: [],
/**
* @name dxPopupOptions.showCloseButton
* @publicName showCloseButton
* @type boolean
* @default false
*/
showCloseButton: false,
bottomTemplate: "bottom"
});
},
_defaultOptionsRules: function _defaultOptionsRules() {
return this.callBase().concat([{
device: function device(_device) {
var currentTheme = (themes.current() || "").split(".")[0];
return _device.phone && currentTheme === "win8";
},
options: {
position: {
my: "top center",
at: "top center",
offset: "0 0"
}
}
}, {
device: { platform: "ios" },
options: {
/**
* @name dxPopupOptions.animation
* @publicName animation
* @default { show: { type: 'slide', duration: 400, from: { position: { my: 'top', at: 'bottom', of: window } }, to: { position: { my: 'center', at: 'center', of: window } } }, hide: { type: 'slide', duration: 400, from: { position: { my: 'center', at: 'center', of: window } }, to: { position: { my: 'top', at: 'bottom', of: window } } }} @for iOS
* @inheritdoc
*/
/**
* @name dxPopupOptions.animation.show
* @publicName show
* @default { type: 'slide', duration: 400, from: { position: { my: 'top', at: 'bottom', of: window } }, to: { position: { my: 'center', at: 'center', of: window } }} @for iOS
* @inheritdoc
*/
/**
* @name dxPopupOptions.animation.hide
* @publicName hide
* @default { type: 'slide', duration: 400, from: { position: { my: 'center', at: 'center', of: window } }, to: { position: { my: 'top', at: 'bottom', of: window } }} @for iOS
* @inheritdoc
*/
animation: this._iosAnimation
}
}, {
device: { platform: "android" },
options: {
animation: this._androidAnimation
}
}, {
device: { platform: "generic" },
options: {
/**
* @name dxPopupOptions.showCloseButton
* @publicName showCloseButton
* @default true @for desktop
*/
showCloseButton: true
}
}, {
device: function device(_device2) {
return devices.real().platform === "generic" && _device2.platform === "generic";
},
options: {
/**
* @name dxPopupOptions.dragEnabled
* @publicName dragEnabled
* @default true @for desktop
*/
dragEnabled: true
}
}, {
device: function device() {
return devices.real().deviceType === "desktop" && !devices.isSimulator();
},
options: {
/**
* @name dxPopupOptions.focusStateEnabled
* @publicName focusStateEnabled
* @type boolean
* @default true @for desktop
* @inheritdoc
*/
focusStateEnabled: true
}
}]);
},
_iosAnimation: {
show: {
type: "slide",
duration: 400,
from: {
position: {
my: "top",
at: "bottom"
}
},
to: {
position: {
my: "center",
at: "center"
}
}
},
hide: {
type: "slide",
duration: 400,
from: {
opacity: 1,
position: {
my: "center",
at: "center"
}
},
to: {
opacity: 1,
position: {
my: "top",
at: "bottom"
}
}
}
},
_androidAnimation: function _androidAnimation() {
var fullScreenConfig = {
show: { type: "slide", duration: 300, from: { top: "30%", opacity: 0 }, to: { top: 0, opacity: 1 } },
hide: { type: "slide", duration: 300, from: { top: 0, opacity: 1 }, to: { top: "30%", opacity: 0 } }
},
defaultConfig = {
show: { type: "fade", duration: 400, from: 0, to: 1 },
hide: { type: "fade", duration: 400, from: 1, to: 0 }
};
return this.option("fullScreen") ? fullScreenConfig : defaultConfig;
},
_init: function _init() {
this.callBase();
this.$element().addClass(POPUP_CLASS);
this._wrapper().addClass(POPUP_WRAPPER_CLASS);
this._$popupContent = this._$content.wrapInner($("<div>").addClass(POPUP_CONTENT_CLASS)).children().eq(0);
},
_render: function _render() {
var isFullscreen = this.option("fullScreen");
this._toggleFullScreenClass(isFullscreen);
this.callBase();
},
_toggleFullScreenClass: function _toggleFullScreenClass(value) {
this._$content.toggleClass(POPUP_FULL_SCREEN_CLASS, value).toggleClass(POPUP_NORMAL_CLASS, !value);
},
_initTemplates: function _initTemplates() {
this.callBase();
this._defaultTemplates["title"] = new EmptyTemplate(this);
this._defaultTemplates["bottom"] = new EmptyTemplate(this);
},
_renderContentImpl: function _renderContentImpl() {
this.callBase();
this._renderTitle();
this._renderBottom();
},
_renderTitle: function _renderTitle() {
var items = this._getToolbarItems("top"),
titleText = this.option("title"),
showTitle = this.option("showTitle");
if (showTitle && !!titleText) {
items.unshift({
location: devices.current().ios ? "center" : "before",
text: titleText
});
}
if (showTitle || items.length > 0) {
this._$title && this._$title.remove();
var $title = $("<div>").addClass(POPUP_TITLE_CLASS).insertBefore(this.$content());
this._$title = this._renderTemplateByType("titleTemplate", items, $title).addClass(POPUP_TITLE_CLASS);
this._renderDrag();
this._executeTitleRenderAction(this._$title);
} else if (this._$title) {
this._$title.detach();
}
},
_renderTemplateByType: function _renderTemplateByType(optionName, data, $container) {
var template = this._getTemplateByOption(optionName),
toolbarTemplate = template instanceof EmptyTemplate;
if (toolbarTemplate) {
var toolbarOptions = {
items: data,
rtlEnabled: this.option("rtlEnabled")
};
this._getTemplate("dx-polymorph-widget").render({
container: $container,
model: {
widget: "dxToolbarBase",
options: toolbarOptions
}
});
var $toolbar = $container.children("div");
$container.replaceWith($toolbar);
return $toolbar;
} else {
var $result = $(template.render({ container: getPublicElement($container) }));
if ($result.hasClass(TEMPLATE_WRAPPER_CLASS)) {
$container.replaceWith($result);
$container = $result;
}
return $container;
}
},
_executeTitleRenderAction: function _executeTitleRenderAction($titleElement) {
this._getTitleRenderAction()({
titleElement: getPublicElement($titleElement)
});
},
_getTitleRenderAction: function _getTitleRenderAction() {
return this._titleRenderAction || this._createTitleRenderAction();
},
_createTitleRenderAction: function _createTitleRenderAction() {
return this._titleRenderAction = this._createActionByOption("onTitleRendered", {
element: this.element(),
excludeValidators: ["designMode", "disabled", "readOnly"]
});
},
_getCloseButton: function _getCloseButton() {
return {
toolbar: "top",
location: "after",
template: this._getCloseButtonRenderer()
};
},
_getCloseButtonRenderer: function _getCloseButtonRenderer() {
return function (_, __, container) {
var $button = $("<div>").addClass(POPUP_TITLE_CLOSEBUTTON_CLASS);
this._createComponent($button, Button, {
icon: 'close',
onClick: this._createToolbarItemAction(undefined),
integrationOptions: {}
});
$(container).append($button);
}.bind(this);
},
_getToolbarItems: function _getToolbarItems(toolbar) {
var toolbarItems = this.option("toolbarItems");
var toolbarsItems = [];
this._toolbarItemClasses = [];
var currentPlatform = devices.current().platform,
index = 0;
each(toolbarItems, function (_, data) {
var isShortcut = isDefined(data.shortcut),
item = isShortcut ? getButtonPlace(data.shortcut) : data;
if (isShortcut && currentPlatform === "ios" && index < 2) {
item.toolbar = "top";
index++;
}
item.toolbar = data.toolbar || item.toolbar || "top";
if (item && item.toolbar === toolbar) {
if (isShortcut) {
extend(item, { location: data.location }, this._getToolbarItemByAlias(data));
}
var isLTROrder = currentPlatform === "win" || currentPlatform === "generic";
if (data.shortcut === "done" && isLTROrder || data.shortcut === "cancel" && !isLTROrder) {
toolbarsItems.unshift(item);
} else {
toolbarsItems.push(item);
}
}
}.bind(this));
if (toolbar === "top" && this.option("showCloseButton") && this.option("showTitle")) {
toolbarsItems.push(this._getCloseButton());
}
return toolbarsItems;
},
_getToolbarItemByAlias: function _getToolbarItemByAlias(data) {
var that = this,
itemType = data.shortcut;
if (inArray(itemType, ALLOWED_TOOLBAR_ITEM_ALIASES) < 0) {
return false;
}
var itemConfig = extend({
text: messageLocalization.format(camelize(itemType, true)),
onClick: this._createToolbarItemAction(data.onClick),
integrationOptions: {}
}, data.options || {});
var itemClass = POPUP_CLASS + "-" + itemType;
this._toolbarItemClasses.push(itemClass);
return {
template: function template(_, __, container) {
var $toolbarItem = $("<div>").addClass(itemClass).appendTo(container);
that._createComponent($toolbarItem, Button, itemConfig);
}
};
},
_createToolbarItemAction: function _createToolbarItemAction(clickAction) {
return this._createAction(clickAction, {
afterExecute: function afterExecute(e) {
e.component.hide();
}
});
},
_renderBottom: function _renderBottom() {
var items = this._getToolbarItems("bottom");
if (items.length) {
this._$bottom && this._$bottom.remove();
var $bottom = $("<div>").addClass(POPUP_BOTTOM_CLASS).insertAfter(this.$content());
this._$bottom = this._renderTemplateByType("bottomTemplate", items, $bottom).addClass(POPUP_BOTTOM_CLASS);
this._toggleClasses();
} else {
this._$bottom && this._$bottom.detach();
}
},
_toggleClasses: function _toggleClasses() {
var aliases = ALLOWED_TOOLBAR_ITEM_ALIASES;
each(aliases, function (_, alias) {
var className = POPUP_CLASS + "-" + alias;
if (inArray(className, this._toolbarItemClasses) >= 0) {
this._wrapper().addClass(className + "-visible");
this._$bottom.addClass(className);
} else {
this._wrapper().removeClass(className + "-visible");
this._$bottom.removeClass(className);
}
}.bind(this));
},
_getDragTarget: function _getDragTarget() {
return this._$title;
},
_renderGeometryImpl: function _renderGeometryImpl() {
this._resetContentHeight();
this.callBase.apply(this, arguments);
this._setContentHeight();
},
_resetContentHeight: function _resetContentHeight() {
this._$popupContent.css({
"height": "auto"
});
},
_renderDrag: function _renderDrag() {
this.callBase();
this._$content.toggleClass(POPUP_DRAGGABLE_CLASS, this.option("dragEnabled"));
},
_renderResize: function _renderResize() {
this.callBase();
this._resizable.option("onResize", function () {
this._setContentHeight();
this._actions.onResize(arguments);
}.bind(this));
},
_setContentHeight: function _setContentHeight() {
(this.option("forceApplyBindings") || noop)();
if (this._disallowUpdateContentHeight()) {
return;
}
var contentPaddings = this._$content.outerHeight() - this._$content.height(),
contentHeight = this._$content.get(0).getBoundingClientRect().height - contentPaddings;
if (this._$title && this._$title.is(":visible")) {
contentHeight -= this._$title.get(0).getBoundingClientRect().height || 0;
}
if (this._$bottom && this._$bottom.is(":visible")) {
contentHeight -= this._$bottom.get(0).getBoundingClientRect().height || 0;
}
this._$popupContent.css("height", contentHeight < 0 ? 0 : contentHeight);
},
_disallowUpdateContentHeight: function _disallowUpdateContentHeight() {
var isHeightAuto = this._$content.get(0).style.height === "auto",
maxHeightSpecified = this._$content.css("maxHeight") !== "none",
minHeightSpecified = parseInt(this._$content.css("minHeight")) > 0;
return isHeightAuto && !(maxHeightSpecified || minHeightSpecified);
},
_renderDimensions: function _renderDimensions() {
if (this.option("fullScreen")) {
this._$content.css({
width: "100%",
height: "100%"
});
} else {
this.callBase.apply(this, arguments);
}
if (windowUtils.hasWindow()) {
this._renderFullscreenWidthClass();
}
},
_renderFullscreenWidthClass: function _renderFullscreenWidthClass() {
this.overlayContent().toggleClass(POPUP_FULL_SCREEN_WIDTH_CLASS, this.overlayContent().outerWidth() === $(window).width());
},
_renderShadingDimensions: function _renderShadingDimensions() {
if (this.option("fullScreen")) {
this._wrapper().css({
width: "100%",
height: "100%"
});
} else {
this.callBase.apply(this, arguments);
}
},
refreshPosition: function refreshPosition() {
this._renderPosition();
},
_renderPosition: function _renderPosition() {
if (this.option("fullScreen")) {
translator.move(this._$content, {
top: 0,
left: 0
});
} else {
(this.option("forceApplyBindings") || noop)();
return this.callBase.apply(this, arguments);
}
},
_optionChanged: function _optionChanged(args) {
switch (args.name) {
case "showTitle":
case "title":
case "titleTemplate":
this._renderTitle();
this._renderGeometry();
break;
case "bottomTemplate":
this._renderBottom();
this._renderGeometry();
break;
case "onTitleRendered":
this._createTitleRenderAction(args.value);
break;
case "toolbarItems":
var isPartialUpdate = args.fullName.search(".options") !== -1;
this._renderTitle();
this._renderBottom();
if (!isPartialUpdate) {
this._renderGeometry();
}
break;
case "dragEnabled":
this._renderDrag();
break;
case "fullScreen":
this._toggleFullScreenClass(args.value);
this._renderGeometry();
domUtils.triggerResizeEvent(this._$content);
break;
case "showCloseButton":
this._renderTitle();
break;
default:
this.callBase(args);
}
},
bottomToolbar: function bottomToolbar() {
return this._$bottom;
},
$content: function $content() {
return this._$popupContent;
},
content: function content() {
return getPublicElement(this._$popupContent);
},
overlayContent: function overlayContent() {
return this._$content;
}
});
registerComponent("dxPopup", Popup);
module.exports = Popup;