devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
415 lines (414 loc) • 14.7 kB
JavaScript
/**
* DevExtreme (esm/core/dom_component.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 config from "./config";
import errors from "./errors";
import windowResizeCallbacks from "../core/utils/resize_callbacks";
import {
Component
} from "./component";
import {
TemplateManager
} from "./template_manager";
import {
attachInstanceToElement,
getInstanceByElement
} from "./utils/public_component";
import {
cleanDataRecursive
} from "./element_data";
import {
each
} from "./utils/iterator";
import {
extend
} from "./utils/extend";
import {
getPublicElement
} from "../core/element";
import {
grep,
noop
} from "./utils/common";
import {
inArray
} from "./utils/array";
import {
isString,
isDefined
} from "./utils/type";
import {
hasWindow
} from "../core/utils/window";
import {
resize as resizeEvent,
visibility as visibilityEvents
} from "../events/short";
var {
abstract: abstract
} = Component;
var DOMComponent = Component.inherit({
_getDefaultOptions() {
return extend(this.callBase(), {
width: void 0,
height: void 0,
rtlEnabled: config().rtlEnabled,
elementAttr: {},
disabled: false,
integrationOptions: {}
}, this._useTemplates() ? TemplateManager.createDefaultOptions() : {})
},
ctor(element, options) {
this._customClass = null;
this._createElement(element);
attachInstanceToElement(this._$element, this, this._dispose);
this.callBase(options)
},
_createElement(element) {
this._$element = $(element)
},
_getSynchronizableOptionsForCreateComponent: () => ["rtlEnabled", "disabled", "templatesRenderAsynchronously"],
_visibilityChanged: abstract,
_dimensionChanged: abstract,
_init() {
this.callBase();
this._attachWindowResizeCallback();
this._initTemplateManager()
},
_setOptionsByDevice(instanceCustomRules) {
this.callBase([].concat(this.constructor._classCustomRules || [], instanceCustomRules || []))
},
_isInitialOptionValue(name) {
var isCustomOption = this.constructor._classCustomRules && Object.prototype.hasOwnProperty.call(this._convertRulesToOptions(this.constructor._classCustomRules), name);
return !isCustomOption && this.callBase(name)
},
_attachWindowResizeCallback() {
if (this._isDimensionChangeSupported()) {
var windowResizeCallBack = this._windowResizeCallBack = this._dimensionChanged.bind(this);
windowResizeCallbacks.add(windowResizeCallBack)
}
},
_isDimensionChangeSupported() {
return this._dimensionChanged !== abstract
},
_renderComponent() {
this._initMarkup();
hasWindow() && this._render()
},
_initMarkup() {
var {
rtlEnabled: rtlEnabled
} = this.option() || {};
this._renderElementAttributes();
this._toggleRTLDirection(rtlEnabled);
this._renderVisibilityChange();
this._renderDimensions()
},
_render() {
this._attachVisibilityChangeHandlers()
},
_renderElementAttributes() {
var {
elementAttr: elementAttr
} = this.option() || {};
var attributes = extend({}, elementAttr);
var classNames = attributes.class;
delete attributes.class;
this.$element().attr(attributes).removeClass(this._customClass).addClass(classNames);
this._customClass = classNames
},
_renderVisibilityChange() {
if (this._isDimensionChangeSupported()) {
this._attachDimensionChangeHandlers()
}
if (this._isVisibilityChangeSupported()) {
var $element = this.$element();
$element.addClass("dx-visibility-change-handler")
}
},
_renderDimensions() {
var $element = this.$element();
var element = $element.get(0);
var width = this._getOptionValue("width", element);
var height = this._getOptionValue("height", element);
if (this._isCssUpdateRequired(element, height, width)) {
$element.css({
width: null === width ? "" : width,
height: null === height ? "" : height
})
}
},
_isCssUpdateRequired: (element, height, width) => !!(isDefined(width) || isDefined(height) || element.style.width || element.style.height),
_attachDimensionChangeHandlers() {
var $el = this.$element();
var namespace = "".concat(this.NAME, "VisibilityChange");
resizeEvent.off($el, {
namespace: namespace
});
resizeEvent.on($el, () => this._dimensionChanged(), {
namespace: namespace
})
},
_attachVisibilityChangeHandlers() {
if (this._isVisibilityChangeSupported()) {
var $el = this.$element();
var namespace = "".concat(this.NAME, "VisibilityChange");
this._isHidden = !this._isVisible();
visibilityEvents.off($el, {
namespace: namespace
});
visibilityEvents.on($el, () => this._checkVisibilityChanged("shown"), () => this._checkVisibilityChanged("hiding"), {
namespace: namespace
})
}
},
_isVisible() {
var $element = this.$element();
return $element.is(":visible")
},
_checkVisibilityChanged(action) {
var isVisible = this._isVisible();
if (isVisible) {
if ("hiding" === action && !this._isHidden) {
this._visibilityChanged(false);
this._isHidden = true
} else if ("shown" === action && this._isHidden) {
this._isHidden = false;
this._visibilityChanged(true)
}
}
},
_isVisibilityChangeSupported() {
return this._visibilityChanged !== abstract && hasWindow()
},
_clean: noop,
_modelByElement() {
var {
modelByElement: modelByElement
} = this.option();
var $element = this.$element();
return modelByElement ? modelByElement($element) : void 0
},
_invalidate() {
if (this._isUpdateAllowed()) {
throw errors.Error("E0007")
}
this._requireRefresh = true
},
_refresh() {
this._clean();
this._renderComponent()
},
_dispose() {
this._templateManager && this._templateManager.dispose();
this.callBase();
this._clean();
this._detachWindowResizeCallback()
},
_detachWindowResizeCallback() {
if (this._isDimensionChangeSupported()) {
windowResizeCallbacks.remove(this._windowResizeCallBack)
}
},
_toggleRTLDirection(rtl) {
var $element = this.$element();
$element.toggleClass("dx-rtl", rtl)
},
_createComponent(element, component) {
var config = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : {};
var synchronizableOptions = grep(this._getSynchronizableOptionsForCreateComponent(), value => !(value in config));
var {
integrationOptions: integrationOptions
} = this.option();
var {
nestedComponentOptions: nestedComponentOptions
} = this.option();
nestedComponentOptions = nestedComponentOptions || noop;
var nestedComponentConfig = extend({
integrationOptions: integrationOptions
}, nestedComponentOptions(this));
synchronizableOptions.forEach(optionName => nestedComponentConfig[optionName] = this.option(optionName));
this._extendConfig(config, nestedComponentConfig);
var instance = void 0;
if (isString(component)) {
var $element = $(element)[component](config);
instance = $element[component]("instance")
} else if (element) {
instance = component.getInstance(element);
if (instance) {
instance.option(config)
} else {
instance = new component(element, config)
}
}
if (instance) {
var optionChangedHandler = _ref => {
var {
name: name,
value: value
} = _ref;
if (inArray(name, synchronizableOptions) >= 0) {
instance.option(name, value)
}
};
this.on("optionChanged", optionChangedHandler);
instance.on("disposing", () => this.off("optionChanged", optionChangedHandler))
}
return instance
},
_extendConfig(config, extendConfig) {
each(extendConfig, (key, value) => {
!Object.prototype.hasOwnProperty.call(config, key) && (config[key] = value)
})
},
_defaultActionConfig() {
var $element = this.$element();
var context = this._modelByElement($element);
return extend(this.callBase(), {
context: context
})
},
_defaultActionArgs() {
var $element = this.$element();
var model = this._modelByElement($element);
var element = this.element();
return extend(this.callBase(), {
element: element,
model: model
})
},
_optionChanged(args) {
switch (args.name) {
case "width":
case "height":
this._renderDimensions();
break;
case "rtlEnabled":
this._invalidate();
break;
case "elementAttr":
this._renderElementAttributes();
break;
case "disabled":
case "integrationOptions":
break;
default:
this.callBase(args)
}
},
_removeAttributes(element) {
var attrs = element.attributes;
for (var i = attrs.length - 1; i >= 0; i--) {
var attr = attrs[i];
if (attr) {
var {
name: name
} = attr;
if (!name.indexOf("aria-") || -1 !== name.indexOf("dx-") || "role" === name || "style" === name || "tabindex" === name) {
element.removeAttribute(name)
}
}
}
},
_removeClasses(element) {
element.className = element.className.split(" ").filter(cssClass => 0 !== cssClass.lastIndexOf("dx-", 0)).join(" ")
},
_updateDOMComponent(renderRequired) {
if (renderRequired) {
this._renderComponent()
} else if (this._requireRefresh) {
this._requireRefresh = false;
this._refresh()
}
},
endUpdate() {
var renderRequired = this._isInitializingRequired();
this.callBase();
this._isUpdateAllowed() && this._updateDOMComponent(renderRequired)
},
$element() {
return this._$element
},
element() {
var $element = this.$element();
return getPublicElement($element)
},
dispose() {
var element = this.$element().get(0);
cleanDataRecursive(element, true);
element.textContent = "";
this._removeAttributes(element);
this._removeClasses(element)
},
resetOption(optionName) {
this.callBase(optionName);
if ("width" === optionName || "height" === optionName) {
var initialOption = this.initialOption(optionName);
!isDefined(initialOption) && this.$element().css(optionName, "")
}
},
_getAnonymousTemplateName() {
return
},
_initTemplateManager() {
if (this._templateManager || !this._useTemplates()) {
return
}
var {
integrationOptions: integrationOptions = {}
} = this.option();
var {
createTemplate: createTemplate
} = integrationOptions;
this._templateManager = new TemplateManager(createTemplate, this._getAnonymousTemplateName());
this._initTemplates()
},
_initTemplates() {
var {
templates: templates,
anonymousTemplateMeta: anonymousTemplateMeta
} = this._templateManager.extractTemplates(this.$element());
var anonymousTemplate = this.option("integrationOptions.templates.".concat(anonymousTemplateMeta.name));
templates.forEach(_ref2 => {
var {
name: name,
template: template
} = _ref2;
this._options.silent("integrationOptions.templates.".concat(name), template)
});
if (anonymousTemplateMeta.name && !anonymousTemplate) {
this._options.silent("integrationOptions.templates.".concat(anonymousTemplateMeta.name), anonymousTemplateMeta.template);
this._options.silent("_hasAnonymousTemplateContent", true)
}
},
_getTemplateByOption(optionName) {
return this._getTemplate(this.option(optionName))
},
_getTemplate(templateSource) {
var templates = this.option("integrationOptions.templates");
var isAsyncTemplate = this.option("templatesRenderAsynchronously");
var skipTemplates = this.option("integrationOptions.skipTemplates");
return this._templateManager.getTemplate(templateSource, templates, {
isAsyncTemplate: isAsyncTemplate,
skipTemplates: skipTemplates
}, this)
},
_saveTemplate(name, template) {
this._setOptionWithoutOptionChange("integrationOptions.templates." + name, this._templateManager._createTemplate(template))
},
_useTemplates: () => true
});
DOMComponent.getInstance = function(element) {
return getInstanceByElement($(element), this)
};
DOMComponent.defaultOptions = function(rule) {
this._classCustomRules = this._classCustomRules || [];
this._classCustomRules.push(rule)
};
export default DOMComponent;