devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
406 lines (405 loc) • 13.8 kB
JavaScript
/**
* DevExtreme (esm/ui/button.js)
* Version: 21.1.4
* Build date: Mon Jun 21 2021
*
* Copyright (c) 2012 - 2021 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
import $ from "../core/renderer";
import devices from "../core/devices";
import {
render
} from "./widget/utils.ink_ripple";
import registerComponent from "../core/component_registrator";
import {
isMaterial,
current
} from "./themes";
import Action from "../core/action";
import ValidationEngine from "./validation_engine";
import Widget from "./widget/ui.widget";
import {
active as activeEvents,
click as clickEvent,
dxClick as dxClickEvent
} from "../events/short";
import {
extend
} from "../core/utils/extend";
import {
FunctionTemplate
} from "../core/templates/function_template";
import {
getImageContainer,
getImageSourceType
} from "../core/utils/icon";
import {
getPublicElement
} from "../core/element";
var ANONYMOUS_TEMPLATE_NAME = "content";
class Button extends Widget {
constructor() {
super(...arguments);
this._feedbackHideTimeout = 100
}
_$content() {
return this.$element().find(".dx-button-content")
}
_$submitInput() {
return this.$element().find(".dx-button-submit-input")
}
_attachActiveEvents(active, inactive) {
var $el = this._eventBindingTarget();
var selector = this._activeStateUnit;
activeEvents.off($el, {
namespace: "inkRipple",
selector: selector
});
activeEvents.on($el, new Action(active), new Action(inactive, {
excludeValidators: ["disabled", "readOnly"]
}), {
showTimeout: this._feedbackShowTimeout,
hideTimeout: this._feedbackHideTimeout,
selector: selector,
namespace: "inkRipple"
})
}
_defaultOptionsRules() {
return super._defaultOptionsRules().concat([{
device: () => "desktop" === devices.real().deviceType && !devices.isSimulator(),
options: {
focusStateEnabled: true
}
}, {
device: () => isMaterial(current()),
options: {
useInkRipple: true
}
}])
}
_executeClickAction(event) {
this._clickAction({
validationGroup: this._validationGroupConfig,
event: event
})
}
_findGroup() {
var $element = this.$element();
var model = this._modelByElement($element);
var {
validationGroup: validationGroup
} = this.option();
return validationGroup || ValidationEngine.findGroup($element, model)
}
_getContentData() {
var {
icon: icon,
text: text,
type: type,
_templateData: _templateData
} = this.option();
return extend({
icon: "back" === type && !icon ? "back" : icon,
text: text
}, _templateData)
}
_getDefaultOptions() {
return extend(super._getDefaultOptions(), {
hoverStateEnabled: true,
onClick: null,
type: "normal",
text: "",
icon: "",
iconPosition: "left",
validationGroup: void 0,
activeStateEnabled: true,
template: "content",
useSubmitBehavior: false,
useInkRipple: false,
_templateData: {},
stylingMode: "contained"
})
}
_getSubmitAction() {
var needValidate = true;
var validationStatus = "valid";
return this._createAction(_ref => {
var {
event: event
} = _ref;
if (needValidate) {
var validationGroup = this._validationGroupConfig;
if (validationGroup) {
var {
status: status,
complete: complete
} = validationGroup.validate();
validationStatus = status;
if ("pending" === status) {
needValidate = false;
this.option("disabled", true);
complete.then(_ref2 => {
var {
status: status
} = _ref2;
this.option("disabled", false);
validationStatus = status;
"valid" === validationStatus && this._submitInput().click();
needValidate = true
})
}
}
}
"valid" !== validationStatus && event.preventDefault();
event.stopPropagation()
})
}
_initMarkup() {
this.$element().addClass("dx-button");
this._renderType();
this._renderStylingMode();
this._renderInkRipple();
this._renderClick();
this._updateAriaLabel();
super._initMarkup();
this._updateContent();
this.setAria("role", "button")
}
_getAnonymousTemplateName() {
return ANONYMOUS_TEMPLATE_NAME
}
_initTemplates() {
this._templateManager.addDefaultTemplates({
content: new FunctionTemplate(_ref3 => {
var {
model: model = {},
container: container
} = _ref3;
var {
text: text,
icon: icon
} = model;
var {
iconPosition: iconPosition
} = this.option();
var $icon = getImageContainer(icon);
var $textContainer = text && $("<span>").text(text).addClass("dx-button-text");
var $container = $(container);
$container.append($textContainer);
if ("left" === iconPosition) {
$container.prepend($icon)
} else {
$icon.addClass("dx-icon-right");
$container.append($icon)
}
})
});
super._initTemplates()
}
_optionChanged(args) {
var {
name: name
} = args;
switch (name) {
case "onClick":
this._updateClick();
break;
case "icon":
case "text":
this._updateContent();
this._updateAriaLabel();
break;
case "type":
this._updateType();
this._updateContent();
break;
case "_templateData":
break;
case "template":
case "iconPosition":
this._updateContent();
break;
case "stylingMode":
this._updateStylingMode();
break;
case "useSubmitBehavior":
this._updateSubmitInput();
break;
case "useInkRipple":
this._invalidate();
break;
default:
super._optionChanged(args)
}
}
_renderClick() {
var $el = this.$element();
dxClickEvent.off($el, {
namespace: this.NAME
});
dxClickEvent.on($el, event => this._executeClickAction(event), {
namespace: this.NAME
});
this._updateClick()
}
_renderInkRipple() {
var {
text: text,
icon: icon,
type: type,
useInkRipple: useInkRipple
} = this.option();
if (useInkRipple) {
var isOnlyIconButton = !text && icon || "back" === type;
var _inkRipple = render(isOnlyIconButton ? {
waveSizeCoefficient: 1,
useHoldAnimation: false,
isCentered: true
} : {});
var changeWaveVisibility = (event, visible) => {
var {
activeStateEnabled: activeStateEnabled,
useInkRipple: useInkRipple
} = this.option();
if (useInkRipple && activeStateEnabled && !this._disposed) {
var config = {
element: this._$content(),
event: event
};
visible ? _inkRipple.showWave(config) : _inkRipple.hideWave(config)
}
};
this._attachActiveEvents(_ref4 => {
var {
event: event
} = _ref4;
return changeWaveVisibility(event, true)
}, _ref5 => {
var {
event: event
} = _ref5;
return changeWaveVisibility(event)
})
}
}
_renderStylingMode() {
var $element = this.$element();
var {
stylingMode: stylingMode
} = this.option();
if (-1 === ["contained", "text", "outlined"].indexOf(stylingMode)) {
stylingMode = this._getDefaultOptions().stylingMode
}
$element.addClass("dx-button-mode-".concat(stylingMode))
}
_renderSubmitInput() {
var {
useSubmitBehavior: useSubmitBehavior
} = this.option();
if (useSubmitBehavior) {
var submitAction = this._getSubmitAction();
var $content = this._$content();
$("<input>").attr("type", "submit").attr("tabindex", -1).addClass("dx-button-submit-input").appendTo($content);
clickEvent.on(this._$submitInput(), event => submitAction({
event: event
}))
}
}
_renderType() {
var {
type: type
} = this.option();
var $element = this.$element();
type && $element.addClass("dx-button-".concat(type))
}
_submitInput() {
return this._$submitInput().get(0)
}
_supportedKeys() {
var click = e => {
e.preventDefault();
this._executeClickAction(e)
};
return extend(super._supportedKeys(), {
space: click,
enter: click
})
}
_updateAriaLabel() {
var ariaTarget = this._getAriaTarget();
var {
icon: icon,
text: text
} = this.option();
if (!text) {
if ("image" === getImageSourceType(icon)) {
icon = -1 === icon.indexOf("base64") ? icon.replace(/.+\/([^.]+)\..+$/, "$1") : "Base64"
}
text = icon || ""
}
ariaTarget.attr("aria-label", text || null)
}
_updateClick() {
this._clickAction = this._createActionByOption("onClick", {
excludeValidators: ["readOnly"],
afterExecute: () => {
var {
useSubmitBehavior: useSubmitBehavior
} = this.option();
useSubmitBehavior && setTimeout(() => this._submitInput().click())
}
})
}
_updateContent() {
var $element = this.$element();
var $content = this._$content();
var data = this._getContentData();
var {
template: template,
iconPosition: iconPosition
} = this.option();
var {
icon: icon,
text: text
} = data;
$content.length ? $content.empty() : $content = $("<div>").addClass("dx-button-content").appendTo($element);
$element.toggleClass("dx-button-has-icon", !!icon).toggleClass("dx-button-icon-right", !!icon && "left" !== iconPosition).toggleClass("dx-button-has-text", !!text);
var $template = $(this._getTemplateByOption("template").render({
model: data,
container: getPublicElement($content),
transclude: this._templateManager.anonymousTemplateName === template
}));
if ($template.hasClass("dx-template-wrapper")) {
$template.addClass("dx-button-content");
$content.replaceWith($template)
}
this._updateSubmitInput()
}
_updateSubmitInput() {
var {
useSubmitBehavior: useSubmitBehavior
} = this.option();
var $submitInput = this._$submitInput();
if (!useSubmitBehavior && $submitInput.length) {
$submitInput.remove()
} else if (useSubmitBehavior && !$submitInput.length) {
this._renderSubmitInput()
}
}
_updateStylingMode() {
var $element = this.$element();
["contained", "text", "outlined"].map(mode => "dx-button-mode-".concat(mode)).forEach($element.removeClass.bind($element));
this._renderStylingMode()
}
_updateType() {
var $element = this.$element();
["back", "danger", "default", "normal", "success"].map(type => "dx-button-".concat(type)).forEach($element.removeClass.bind($element));
this._renderType()
}
get _validationGroupConfig() {
return ValidationEngine.getGroupConfig(this._findGroup())
}
}
registerComponent("dxButton", Button);
export default Button;