devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
462 lines (388 loc) • 14.2 kB
JavaScript
"use strict";
var $ = require("../core/renderer"),
eventsEngine = require("../events/core/events_engine"),
noop = require("../core/utils/common").noop,
fx = require("../animation/fx"),
clickEvent = require("../events/click"),
translator = require("../animation/translator"),
getPublicElement = require("../core/utils/dom").getPublicElement,
hideTopOverlayCallback = require("../mobile/hide_top_overlay").hideCallback,
registerComponent = require("../core/component_registrator"),
extend = require("../core/utils/extend").extend,
Widget = require("./widget/ui.widget"),
Swipeable = require("../events/gesture/swipeable"),
EmptyTemplate = require("./widget/empty_template"),
Deferred = require("../core/utils/deferred").Deferred,
windowUtils = require("../core/utils/window");
var SLIDEOUTVIEW_CLASS = "dx-slideoutview",
SLIDEOUTVIEW_WRAPPER_CLASS = "dx-slideoutview-wrapper",
SLIDEOUTVIEW_MENU_CONTENT_CLASS = "dx-slideoutview-menu-content",
SLIDEOUTVIEW_CONTENT_CLASS = "dx-slideoutview-content",
SLIDEOUTVIEW_SHIELD_CLASS = "dx-slideoutview-shield",
INVISIBLE_STATE_CLASS = "dx-state-invisible",
ANONYMOUS_TEMPLATE_NAME = "content",
ANIMATION_DURATION = 400;
var animation = {
moveTo: function moveTo($element, position, completeAction) {
fx.animate($element, {
type: "slide",
to: { left: position },
duration: ANIMATION_DURATION,
complete: completeAction
});
},
complete: function complete($element) {
fx.stop($element, true);
}
};
/**
* @name dxSlideOutView
* @publicName dxSlideOutView
* @inherits Widget
* @module ui/slide_out_view
* @export default
*/
var SlideOutView = Widget.inherit({
_getDefaultOptions: function _getDefaultOptions() {
return extend(this.callBase(), {
/**
* @name dxSlideOutViewOptions.menuPosition
* @publicName menuPosition
* @type Enums.SlideOutMenuPosition
* @default "normal"
*/
menuPosition: "normal",
/**
* @name dxSlideOutViewOptions.menuVisible
* @publicName menuVisible
* @type boolean
* @default false
*/
menuVisible: false,
/**
* @name dxSlideOutViewOptions.swipeEnabled
* @publicName swipeEnabled
* @type boolean
* @default true
*/
swipeEnabled: true,
/**
* @name dxSlideOutViewOptions.menuTemplate
* @publicName menuTemplate
* @type_function_param1 menuElement:dxElement
* @type template|function
* @default null
*/
menuTemplate: "menu",
/**
* @name dxSlideOutViewOptions.contentTemplate
* @publicName contentTemplate
* @type_function_param1 contentElement:dxElement
* @type template|function
* @default "content"
*/
contentTemplate: "content",
/**
* @name dxSlideOutViewOptions.contentOffset
* @publicName contentOffset
* @hidden
* @inheritdoc
*/
contentOffset: 45
/**
* @name dxSlideOutViewOptions.onContentReady
* @publicName onContentReady
* @hidden true
* @action
*/
/**
* @name dxSlideOutViewOptions.focusStateEnabled
* @publicName focusStateEnabled
* @hidden
* @inheritdoc
*/
/**
* @name dxSlideOutViewOptions.accessKey
* @publicName accessKey
* @hidden
* @inheritdoc
*/
/**
* @name dxSlideOutViewOptions.tabIndex
* @publicName tabIndex
* @hidden
* @inheritdoc
*/
});
},
_defaultOptionsRules: function _defaultOptionsRules() {
return this.callBase().concat([{
device: {
android: true
},
options: {
contentOffset: 54
}
}, {
device: function device(_device) {
return _device.platform === "generic" && _device.deviceType !== "desktop";
},
options: {
contentOffset: 56
}
}, {
device: {
win: true,
phone: false
},
options: {
contentOffset: 76
}
}]);
},
_getAnonymousTemplateName: function _getAnonymousTemplateName() {
return ANONYMOUS_TEMPLATE_NAME;
},
_init: function _init() {
this.callBase();
this.$element().addClass(SLIDEOUTVIEW_CLASS);
this._deferredAnimate = undefined;
this._initHideTopOverlayHandler();
},
_initHideTopOverlayHandler: function _initHideTopOverlayHandler() {
this._hideMenuHandler = this.hideMenu.bind(this);
},
_initTemplates: function _initTemplates() {
this.callBase();
this._defaultTemplates["menu"] = new EmptyTemplate(this);
this._defaultTemplates["content"] = new EmptyTemplate(this);
},
_initMarkup: function _initMarkup() {
this.callBase();
this._renderMarkup();
var menuTemplate = this._getTemplate(this.option("menuTemplate")),
contentTemplate = this._getTemplate(this.option("contentTemplate"));
menuTemplate && menuTemplate.render({
container: this.menuContent()
});
contentTemplate && contentTemplate.render({
container: this.content(),
noModel: true
});
this._renderShield();
this._toggleMenuPositionClass();
},
_render: function _render() {
this.callBase();
this._initSwipeHandlers();
this._dimensionChanged();
},
_renderMarkup: function _renderMarkup() {
var $wrapper = $("<div>").addClass(SLIDEOUTVIEW_WRAPPER_CLASS);
this._$menu = $("<div>").addClass(SLIDEOUTVIEW_MENU_CONTENT_CLASS);
this._$container = $("<div>").addClass(SLIDEOUTVIEW_CONTENT_CLASS);
$wrapper.append(this._$menu);
$wrapper.append(this._$container);
this.$element().append($wrapper);
// NOTE: B251455
eventsEngine.on(this._$container, "MSPointerDown", noop);
},
_renderShield: function _renderShield() {
this._$shield = this._$shield || $("<div>").addClass(SLIDEOUTVIEW_SHIELD_CLASS);
this._$shield.appendTo(this.content());
eventsEngine.off(this._$shield, clickEvent.name);
eventsEngine.on(this._$shield, clickEvent.name, this.hideMenu.bind(this));
this._toggleShieldVisibility(this.option("menuVisible"));
},
_initSwipeHandlers: function _initSwipeHandlers() {
this._createComponent($(this.content()), Swipeable, {
disabled: !this.option("swipeEnabled"),
elastic: false,
itemSizeFunc: this._getMenuWidth.bind(this),
onStart: this._swipeStartHandler.bind(this),
onUpdated: this._swipeUpdateHandler.bind(this),
onEnd: this._swipeEndHandler.bind(this)
});
},
_isRightMenuPosition: function _isRightMenuPosition() {
var invertedPosition = this.option("menuPosition") === "inverted",
rtl = this.option("rtlEnabled");
return rtl && !invertedPosition || !rtl && invertedPosition;
},
_swipeStartHandler: function _swipeStartHandler(e) {
animation.complete($(this.content()));
var event = e.event,
menuVisible = this.option("menuVisible"),
rtl = this._isRightMenuPosition();
event.maxLeftOffset = +(rtl ? !menuVisible : menuVisible);
event.maxRightOffset = +(rtl ? menuVisible : !menuVisible);
this._toggleShieldVisibility(true);
},
_swipeUpdateHandler: function _swipeUpdateHandler(e) {
var event = e.event,
offset = this.option("menuVisible") ? event.offset + 1 * this._getRTLSignCorrection() : event.offset;
offset *= this._getRTLSignCorrection();
this._renderPosition(offset, false);
},
_swipeEndHandler: function _swipeEndHandler(e) {
var targetOffset = e.event.targetOffset * this._getRTLSignCorrection() + this.option("menuVisible"),
menuVisible = targetOffset !== 0;
if (this.option("menuVisible") === menuVisible) {
this._renderPosition(this.option("menuVisible"), true);
} else {
this.option("menuVisible", menuVisible);
}
},
_toggleMenuPositionClass: function _toggleMenuPositionClass() {
var left = SLIDEOUTVIEW_CLASS + "-left",
right = SLIDEOUTVIEW_CLASS + "-right",
menuPosition = this._isRightMenuPosition() ? "right" : "left";
this._$menu.removeClass(left + " " + right);
this._$menu.addClass(SLIDEOUTVIEW_CLASS + "-" + menuPosition);
},
_renderPosition: function _renderPosition(offset, animate) {
if (!windowUtils.hasWindow()) return;
var pos = this._calculatePixelOffset(offset) * this._getRTLSignCorrection();
this._toggleHideMenuCallback(offset);
if (animate) {
this._toggleShieldVisibility(true);
animation.moveTo($(this.content()), pos, this._animationCompleteHandler.bind(this));
} else {
translator.move($(this.content()), { left: pos });
}
},
_calculatePixelOffset: function _calculatePixelOffset(offset) {
offset = offset || 0;
return offset * this._getMenuWidth();
},
_getMenuWidth: function _getMenuWidth() {
if (!this._menuWidth) {
var maxMenuWidth = this.$element().width() - this.option("contentOffset"),
menuContent = $(this.menuContent());
menuContent.css("maxWidth", maxMenuWidth < 0 ? 0 : maxMenuWidth);
var currentMenuWidth = menuContent.width();
this._menuWidth = Math.min(currentMenuWidth, maxMenuWidth);
}
return this._menuWidth;
},
_animationCompleteHandler: function _animationCompleteHandler() {
this._toggleShieldVisibility(this.option("menuVisible"));
if (this._deferredAnimate) {
this._deferredAnimate.resolveWith(this);
}
},
_toggleHideMenuCallback: function _toggleHideMenuCallback(subscribe) {
if (subscribe) {
hideTopOverlayCallback.add(this._hideMenuHandler);
} else {
hideTopOverlayCallback.remove(this._hideMenuHandler);
}
},
_getRTLSignCorrection: function _getRTLSignCorrection() {
return this._isRightMenuPosition() ? -1 : 1;
},
_dispose: function _dispose() {
animation.complete($(this.content()));
this._toggleHideMenuCallback(false);
this.callBase();
},
_visibilityChanged: function _visibilityChanged(visible) {
if (visible) {
this._dimensionChanged();
}
},
_dimensionChanged: function _dimensionChanged() {
delete this._menuWidth;
this._renderPosition(this.option("menuVisible"), false);
},
_toggleShieldVisibility: function _toggleShieldVisibility(visible) {
this._$shield.toggleClass(INVISIBLE_STATE_CLASS, !visible);
},
_optionChanged: function _optionChanged(args) {
switch (args.name) {
case "width":
this.callBase(args);
this._dimensionChanged();
break;
case "contentOffset":
this._dimensionChanged();
break;
case "menuVisible":
this._renderPosition(args.value, true);
break;
case "menuPosition":
this._renderPosition(this.option("menuVisible"), true);
this._toggleMenuPositionClass();
break;
case "swipeEnabled":
this._initSwipeHandlers();
break;
case "contentTemplate":
case "menuTemplate":
this._invalidate();
break;
default:
this.callBase(args);
}
},
/**
* @name dxSlideOutViewMethods.menuContent
* @publicName menuContent()
* @return dxElement
*/
menuContent: function menuContent() {
return getPublicElement(this._$menu);
},
/**
* @name dxSlideOutViewMethods.content
* @publicName content()
* @return dxElement
*/
content: function content() {
return getPublicElement(this._$container);
},
/**
* @name dxSlideOutViewMethods.showMenu
* @publicName showMenu()
* @return Promise<void>
*/
showMenu: function showMenu() {
return this.toggleMenuVisibility(true);
},
/**
* @name dxSlideOutViewMethods.hideMenu
* @publicName hideMenu()
* @return Promise<void>
*/
hideMenu: function hideMenu() {
return this.toggleMenuVisibility(false);
},
/**
* @name dxSlideOutViewMethods.toggleMenuVisibility
* @publicName toggleMenuVisibility()
* @return Promise<void>
*/
toggleMenuVisibility: function toggleMenuVisibility(showing) {
showing = showing === undefined ? !this.option("menuVisible") : showing;
this._deferredAnimate = new Deferred();
this.option("menuVisible", showing);
return this._deferredAnimate.promise();
}
/**
* @name dxSlideOutViewMethods.registerKeyHandler
* @publicName registerKeyHandler(key, handler)
* @hidden
* @inheritdoc
*/
/**
* @name dxSlideOutViewMethods.focus
* @publicName focus()
* @hidden
* @inheritdoc
*/
});
registerComponent("dxSlideOutView", SlideOutView);
module.exports = SlideOutView;
///#DEBUG
module.exports.animation = animation;
///#ENDDEBUG