UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

443 lines (366 loc) • 13.9 kB
"use strict"; var $ = require("../core/renderer"), windowUtils = require("../core/utils/window"), window = windowUtils.getWindow(), eventsEngine = require("../events/core/events_engine"), devices = require("../core/devices"), extend = require("../core/utils/extend").extend, inkRipple = require("./widget/utils.ink_ripple"), registerComponent = require("../core/component_registrator"), Editor = require("./editor/editor"), eventUtils = require("../events/utils"), feedbackEvents = require("../events/core/emitter.feedback"), themes = require("./themes"), fx = require("../animation/fx"), messageLocalization = require("../localization/message"), clickEvent = require("../events/click"), Swipeable = require("../events/gesture/swipeable"), Deferred = require("../core/utils/deferred").Deferred; var SWITCH_CLASS = "dx-switch", SWITCH_WRAPPER_CLASS = SWITCH_CLASS + "-wrapper", SWITCH_CONTAINER_CLASS = SWITCH_CLASS + "-container", SWITCH_INNER_CLASS = SWITCH_CLASS + "-inner", SWITCH_HANDLE_CLASS = SWITCH_CLASS + "-handle", SWITCH_ON_VALUE_CLASS = SWITCH_CLASS + "-on-value", SWITCH_ON_CLASS = SWITCH_CLASS + "-on", SWITCH_OFF_CLASS = SWITCH_CLASS + "-off", SWITCH_ANIMATION_DURATION = 100; /** * @name dxSwitch * @isEditor * @publicName dxSwitch * @inherits Editor * @module ui/switch * @export default */ var Switch = Editor.inherit({ _supportedKeys: function _supportedKeys() { var isRTL = this.option("rtlEnabled"); var click = function click(e) { e.preventDefault(); this._clickAction({ event: e }); }, move = function move(value, e) { e.preventDefault(); e.stopPropagation(); this._animateValue(value); }; return extend(this.callBase(), { space: click, enter: click, leftArrow: move.bind(this, isRTL ? true : false), rightArrow: move.bind(this, isRTL ? false : true) }); }, _getDefaultOptions: function _getDefaultOptions() { return extend(this.callBase(), { /** * @name dxSwitchOptions.hoverStateEnabled * @publicName hoverStateEnabled * @type boolean * @default true * @inheritdoc */ hoverStateEnabled: true, /** * @name dxSwitchOptions.activeStateEnabled * @publicName activeStateEnabled * @type boolean * @default true * @inheritdoc */ activeStateEnabled: true, /** * @name dxSwitchOptions.onText * @publicName onText * @type string * @default "ON" */ onText: messageLocalization.format("dxSwitch-onText"), /** * @name dxSwitchOptions.offText * @publicName offText * @type string * @default "OFF" */ offText: messageLocalization.format("dxSwitch-offText"), /** * @name dxSwitchOptions.value * @publicName value * @type boolean * @default false */ value: false, useInkRipple: false /** * @name dxSwitchOptions.name * @publicName name * @type string * @hidden false * @inheritdoc */ }); }, _defaultOptionsRules: function _defaultOptionsRules() { return this.callBase().concat([{ device: function device() { return devices.real().deviceType === "desktop" && !devices.isSimulator(); }, options: { /** * @name dxSwitchOptions.focusStateEnabled * @publicName focusStateEnabled * @type boolean * @default true @for desktop * @inheritdoc */ focusStateEnabled: true } }, { device: function device() { return (/android5/.test(themes.current()) ); }, options: { useInkRipple: true } }]); }, _feedbackHideTimeout: 0, _animating: false, _initMarkup: function _initMarkup() { this._renderContainers(); this.option("useInkRipple") && this._renderInkRipple(); this.$element().addClass(SWITCH_CLASS).append(this._$switchWrapper); this._renderSubmitElement(); this._renderClick(); this.setAria("role", "button"); this._renderSwipeable(); this.callBase(); }, _render: function _render() { this._renderSwitchInner(); this._renderLabels(); this._renderHandleWidth(); this._renderValue(); this.callBase(); }, _renderHandleWidth: function _renderHandleWidth() { this._handleWidth = parseFloat(window.getComputedStyle(this._$handle.get(0)).width); }, _getCalcOffset: function _getCalcOffset(value, offset) { var ratio = offset - Number(!value); return "calc(" + 100 * ratio + "% + " + -this._getHandleWidth() * ratio + "px)"; }, _getHandleWidth: function _getHandleWidth() { !this._handleWidth && this._renderHandleWidth(); return this._handleWidth; }, _getPixelOffset: function _getPixelOffset(value, offset) { return this._getMarginBound() * (offset - Number(!value)); }, _renderSwitchInner: function _renderSwitchInner() { this._$switchInner = $("<div>").addClass(SWITCH_INNER_CLASS).appendTo(this._$switchContainer); this._$handle = $("<div>").addClass(SWITCH_HANDLE_CLASS).appendTo(this._$switchInner); }, _renderLabels: function _renderLabels() { this._$labelOn = $("<div>").addClass(SWITCH_ON_CLASS).prependTo(this._$switchInner); this._$labelOff = $("<div>").addClass(SWITCH_OFF_CLASS).appendTo(this._$switchInner); this._setLabelsText(); }, _renderContainers: function _renderContainers() { this._$switchContainer = $("<div>").addClass(SWITCH_CONTAINER_CLASS); this._$switchWrapper = $("<div>").addClass(SWITCH_WRAPPER_CLASS).append(this._$switchContainer); }, _renderSwipeable: function _renderSwipeable() { this._createComponent(this.$element(), Swipeable, { elastic: false, immediate: true, onStart: this._swipeStartHandler.bind(this), onUpdated: this._swipeUpdateHandler.bind(this), onEnd: this._swipeEndHandler.bind(this), itemSizeFunc: this._getMarginBound.bind(this) }); }, _renderSubmitElement: function _renderSubmitElement() { this._$submitElement = $("<input>").attr("type", "hidden").appendTo(this.$element()); }, _getSubmitElement: function _getSubmitElement() { return this._$submitElement; }, _renderInkRipple: function _renderInkRipple() { this._inkRipple = inkRipple.render({ waveSizeCoefficient: 1.7, isCentered: true, useHoldAnimation: false, wavesNumber: 2 }); }, _renderInkWave: function _renderInkWave(element, dxEvent, doRender, waveIndex) { if (!this._inkRipple) { return; } var config = { element: element, event: dxEvent, wave: waveIndex }; if (doRender) { this._inkRipple.showWave(config); } else { this._inkRipple.hideWave(config); } }, _updateFocusState: function _updateFocusState(e, value) { this.callBase.apply(this, arguments); this._renderInkWave(this._$handle, e, value, 0); }, _toggleActiveState: function _toggleActiveState($element, value, e) { this.callBase.apply(this, arguments); this._renderInkWave(this._$handle, e, value, 1); }, _getMarginBound: function _getMarginBound() { if (!this._marginBound) { this._marginBound = this._$switchContainer.outerWidth(true) - this._getHandleWidth(); } return this._marginBound; }, _marginDirection: function _marginDirection() { return this.option("rtlEnabled") ? "Right" : "Left"; }, _offsetDirection: function _offsetDirection() { return this.option("rtlEnabled") ? -1 : 1; }, _renderPosition: function _renderPosition(state, swipeOffset) { if (!windowUtils.hasWindow()) { return; } var marginDirection = this._marginDirection(), resetMarginDirection = marginDirection === "Left" ? "Right" : "Left"; this._$switchInner.css("margin" + marginDirection, this._getCalcOffset(state, swipeOffset)); this._$switchInner.css("margin" + resetMarginDirection, 0); }, _validateValue: function _validateValue() { var check = this.option("value"); if (typeof check !== "boolean") { this._options["value"] = !!check; } }, _renderClick: function _renderClick() { var eventName = eventUtils.addNamespace(clickEvent.name, this.NAME); var $element = this.$element(); this._clickAction = this._createAction(this._clickHandler.bind(this)); eventsEngine.off($element, eventName); eventsEngine.on($element, eventName, function (e) { this._clickAction({ event: e }); }.bind(this)); }, _clickHandler: function _clickHandler(args) { var e = args.event; this._saveValueChangeEvent(e); if (this._animating || this._swiping) { return; } this._animateValue(!this.option("value")); }, _animateValue: function _animateValue(value) { var startValue = this.option("value"), endValue = value; if (startValue === endValue) { return; } this._animating = true; var that = this, marginDirection = this._marginDirection(), resetMarginDirection = marginDirection === "Left" ? "Right" : "Left", fromConfig = {}, toConfig = {}; this._$switchInner.css("margin" + resetMarginDirection, 0); fromConfig["margin" + marginDirection] = this._getCalcOffset(startValue, 0); toConfig["margin" + marginDirection] = this._getCalcOffset(endValue, 0); fx.animate(this._$switchInner, { from: fromConfig, to: toConfig, duration: SWITCH_ANIMATION_DURATION, complete: function complete() { that._animating = false; that.option("value", endValue); } }); }, _swipeStartHandler: function _swipeStartHandler(e) { var state = this.option("value"), rtlEnabled = this.option("rtlEnabled"), maxOffOffset = rtlEnabled ? 0 : 1, maxOnOffset = rtlEnabled ? 1 : 0; e.event.maxLeftOffset = state ? maxOffOffset : maxOnOffset; e.event.maxRightOffset = state ? maxOnOffset : maxOffOffset; this._swiping = true; this._feedbackDeferred = new Deferred(); feedbackEvents.lock(this._feedbackDeferred); this._toggleActiveState(this.$element(), this.option("activeStateEnabled")); }, _swipeUpdateHandler: function _swipeUpdateHandler(e) { this._renderPosition(this.option("value"), this._offsetDirection() * e.event.offset); }, _swipeEndHandler: function _swipeEndHandler(e) { var that = this, offsetDirection = this._offsetDirection(), toConfig = {}; toConfig["margin" + this._marginDirection()] = this._getCalcOffset(that.option("value"), offsetDirection * e.event.targetOffset); fx.animate(this._$switchInner, { to: toConfig, duration: SWITCH_ANIMATION_DURATION, complete: function complete() { that._swiping = false; var pos = that.option("value") + offsetDirection * e.event.targetOffset; that.option("value", Boolean(pos)); that._feedbackDeferred.resolve(); that._toggleActiveState(that.$element(), false); } }); }, _renderValue: function _renderValue() { this._validateValue(); var val = this.option("value"); this._renderPosition(val, 0); this.$element().toggleClass(SWITCH_ON_VALUE_CLASS, val); this._$submitElement.val(val); this.setAria({ "pressed": val, "label": val ? this.option("onText") : this.option("offText") }); }, _setLabelsText: function _setLabelsText() { this._$labelOn && this._$labelOn.text(this.option("onText")); this._$labelOff && this._$labelOff.text(this.option("offText")); }, _visibilityChanged: function _visibilityChanged(visible) { if (visible) { this.repaint(); } }, _optionChanged: function _optionChanged(args) { switch (args.name) { case "useInkRipple": this._invalidate(); break; case "width": delete this._marginBound; this._refresh(); break; case "onText": case "offText": this._setLabelsText(); break; case "value": this._renderValue(); this.callBase(args); break; default: this.callBase(args); } } }); registerComponent("dxSwitch", Switch); module.exports = Switch;